mirror of
https://github.com/simplex-chat/haskell.nix.git
synced 2026-06-03 09:17:32 +00:00
When trying to build haskell packages from local source using nix ( source code you have used `git clone` to retrieve and have modified) it is often annoying that small changes can trigger a lot of components to be built unnecessarily. For instance if you change the code for a test in a package it will often trigger builds of the library and all its dependencies. To avoid this problem we could manually set up `cleanSourceWith` calls in nix to filter out files that are not need, but to do this every component would be difficult and error prone. This change automates this process using the information in the packages `.cabal` file to filter the source of each component automatically when using `callCabalPlanToNix` (we should be able to add it to `callStackToNix` too). ## How does it work? * A new version on `cleanSourceWith` is used that adds a `subDir` argument. It allows descending into a subdirectory to compose with cleaning the source. * A new `--full` argument is passed to `plan-to-nix` by `callCabalPlanToNix` telling it to output extra information about the cabal packages in the plan (directories and filenames). * `callCabalProjectToNix` uses the new `subDir` argument of `cleanSourceWith` when setting the packageSrc. * `comp-builder.nix` and `setup-builder.nix` use a new `cleanCabalComponent` function to further clean the packageSrc using the new information in the `package` and `component`. ## hpack wildcards `package.yaml` files can include wild cards and these are not handled yet (they do not map to anything in the `.cabal` file and so are lost). They will need to be added manually to the appropriate field (eg. `packages.xyz.package.dataFiles`) If your hpack file does not contain wildcards or if you are willing to add the wildcards entries manually in nix it is still useful to enable cleaning on hpack. For example: ``` packages.ghc-toolkit.package.cleanHpack = true; packages.ghc-toolkit.components.library.extraSrcFiles = [ "genapply/**/**" "boot-libs/**/**" "ghc-libdir/**/**" ]; ```
281 lines
7.6 KiB
Nix
281 lines
7.6 KiB
Nix
# package descriptions in hackage will look like:
|
|
# { system, compiler, flags, pkgs, hsPkgs, pkgconfPkgs }:
|
|
# { flags = { flag1 = false; flags2 = true; ... };
|
|
# package = { specVersion = "X.Y"; identifier = { name = "..."; version = "a.b.c.d"; };
|
|
# license = "..."; copyright = "..."; maintainer = "..."; author = "...";
|
|
# homepage = "..."; url = "..."; synopsis = "..."; description = "...";
|
|
# buildType = "Simple"; # or Custom, Autoconf, ...
|
|
# };
|
|
# components = {
|
|
# "..." = { depends = [ (hsPkgs.base) ... ]; };
|
|
# exes = { "..." = { depends = ... };
|
|
# "..." = { depends = ... }; };
|
|
# tests = { "..." = { depends = ... }; ... };
|
|
# };
|
|
|
|
{ parentConfig, mod_args, listOfFilteringNulls, componentOptions, packageOptions }:
|
|
{ lib, config, pkgs, haskellLib, ... }:
|
|
|
|
with lib;
|
|
with types;
|
|
|
|
# Work around issue that can cause _lots_ of files to be copied into the store.
|
|
# See https://github.com/NixOS/nixpkgs/pull/64691
|
|
let path = types.path // { check = x: types.path.check (x.origSrc or x); };
|
|
|
|
in {
|
|
# This is how the Nix expressions generated by *-to-nix receive
|
|
# their flags argument.
|
|
config._module.args = mod_args // { flags = config.flags; };
|
|
|
|
options = (packageOptions parentConfig) // {
|
|
# TODO: Add descriptions to everything.
|
|
flags = mkOption {
|
|
type = attrsOf bool;
|
|
};
|
|
package = {
|
|
specVersion = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
identifier.name = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
identifier.version = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
license = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
copyright = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
maintainer = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
author = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
homepage = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
url = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
synopsis = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
description = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
buildType = mkOption {
|
|
type = str;
|
|
};
|
|
|
|
setup-depends = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
|
|
detailLevel = mkOption {
|
|
type = str;
|
|
default = "MinimalDetails";
|
|
};
|
|
|
|
licenseFiles = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
|
|
dataDir = mkOption {
|
|
type = str;
|
|
default = "";
|
|
};
|
|
|
|
dataFiles = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
|
|
extraSrcFiles = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
|
|
extraTmpFiles = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
|
|
extraDocFiles = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
|
|
cleanHpack = mkOption {
|
|
type = bool;
|
|
default = false;
|
|
};
|
|
};
|
|
|
|
components = let
|
|
componentType = submodule {
|
|
# add the shared componentOptions
|
|
options = (packageOptions config) // {
|
|
depends = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
libs = mkOption {
|
|
type = listOfFilteringNulls (nullOr package);
|
|
default = [];
|
|
};
|
|
frameworks = mkOption {
|
|
type = listOfFilteringNulls package;
|
|
default = [];
|
|
};
|
|
pkgconfig = mkOption {
|
|
type = listOf (listOfFilteringNulls package);
|
|
default = [];
|
|
};
|
|
build-tools = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
modules = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
asmSources = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
cmmSources = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
cSources = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
cxxSources = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
jsSources = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
hsSourceDirs = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
includeDirs = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
includes = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
mainPath = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
extraSrcFiles = mkOption {
|
|
type = listOfFilteringNulls unspecified;
|
|
default = [];
|
|
};
|
|
};
|
|
};
|
|
in {
|
|
library = mkOption {
|
|
type = nullOr componentType;
|
|
default = null;
|
|
};
|
|
sublibs = mkOption {
|
|
type = attrsOf componentType;
|
|
default = {};
|
|
};
|
|
foreignlibs = mkOption {
|
|
type = attrsOf componentType;
|
|
default = {};
|
|
};
|
|
exes = mkOption {
|
|
type = attrsOf componentType;
|
|
default = {};
|
|
};
|
|
tests = mkOption {
|
|
type = attrsOf componentType;
|
|
default = {};
|
|
};
|
|
benchmarks = mkOption {
|
|
type = attrsOf componentType;
|
|
default = {};
|
|
};
|
|
all = mkOption {
|
|
type = componentType;
|
|
apply = all: all // {
|
|
# TODO: Should this check for the entire component
|
|
# definition to match, rather than just the identifier?
|
|
depends = builtins.filter (p: p.identifier != config.package.identifier) all.depends;
|
|
};
|
|
description = "The merged dependencies of all other components";
|
|
};
|
|
};
|
|
|
|
name = mkOption {
|
|
type = str;
|
|
default = "${config.package.identifier.name}-${config.package.identifier.version}";
|
|
defaultText = "\${config.package.identifier.name}-\${config.package.identifier.version}";
|
|
};
|
|
sha256 = mkOption {
|
|
type = nullOr str;
|
|
default = null;
|
|
};
|
|
src = mkOption {
|
|
type = either path package;
|
|
default = pkgs.fetchurl { url = "mirror://hackage/${config.name}.tar.gz"; inherit (config) sha256; };
|
|
defaultText = "pkgs.fetchurl { url = \"mirror://hackage/\${config.name}.tar.gz\"; inherit (config) sha256; };";
|
|
};
|
|
cabal-generator = mkOption {
|
|
type = nullOr str;
|
|
default = null;
|
|
};
|
|
revision = mkOption {
|
|
type = nullOr int;
|
|
default = null;
|
|
};
|
|
revisionSha256 = mkOption {
|
|
type = nullOr str;
|
|
default = null;
|
|
};
|
|
patches = mkOption {
|
|
type = listOf (either unspecified path);
|
|
default = [];
|
|
};
|
|
};
|
|
|
|
# This has one quirk. Manually setting options on the all component
|
|
# will be considered a conflict. This is almost always fine; most
|
|
# settings should be modified in either the package options, or an
|
|
# individual component's options. When this isn't sufficient,
|
|
# mkForce is a reasonable workaround.
|
|
#
|
|
# An alternative solution to mkForce for many of the options where
|
|
# this is relevant would be to switch from the bool type to
|
|
# something like an anyBool type, which would merge definitions by
|
|
# returning true if any is true.
|
|
config.components.all = lib.mkMerge (haskellLib.getAllComponents config);
|
|
}
|