diff --git a/lib/haskell.nix b/lib/haskell.nix index 702df2e..8321519 100644 --- a/lib/haskell.nix +++ b/lib/haskell.nix @@ -4,29 +4,55 @@ let makeCI = haskellPkgs: { # haskell project root src, - # haskell package names - packageNames, - # cabal file with ghc versions specified in test-with - cabalFile, + # list of ghc versions to build packages, if not specified the ghc versions + # will be taken from tested-with stanzas from .cabal files + ghcVersions ? [], # whether to build the project with stack, disable if you are not using stack - buildWithStack ? true, + buildWithStack ? true, # stack files to use in addition to stack.yaml stackFiles ? [], # stack resolvers for building the project, they will be replaced in stack.yaml resolvers ? [], # extra haskell.nix arguments extraArgs ? {} - }: let - pkgs = nixpkgs.legacyPackages.${haskellPkgs.system}; + }: + # if buildWithStack is false, there is no point in specifying resolvers or stackFiles + assert !buildWithStack -> resolvers == [] && stackFiles == []; + let + pkgs = nixpkgs.legacyPackages.x86_64-linux; replaceDots = builtins.replaceStrings ["."] ["-"]; - - # invoke haskell.nix for every ghc specified in tested-with stanza of cabalFile - ghc-versions = getTestedWithVersions cabalFile; - pkgs-per-ghc = lib.genAttrs ghc-versions - (ghc: haskellPkgs.haskell-nix.cabalProject ({ - inherit src; - compiler-nix-name = ghc; - } // extraArgs)); + cabalFiles = lib.filter (x: x != "") (lib.splitString "\n" + (builtins.readFile (pkgs.runCommand "cabalFiles" {} '' + ${pkgs.findutils}/bin/find ${src} -type f -name "*.cabal" > $out + ''))); + + # extract ghc tested-with versions from each .cabal file, keep a list of packages built with each ghc + ghc-versions-tested-with = let + # ghc versions for each package + ghcsPerCabal = map (f: { + package = lib.removeSuffix ".cabal" (builtins.baseNameOf f); + ghcs = getTestedWithVersions f;}) cabalFiles; + # ghc versions from all .cabal files + allGhcs = lib.unique (lib.concatMap (lib.getAttr "ghcs") ghcsPerCabal); + in lib.genAttrs allGhcs + (ghc: map (lib.getAttr "package") (lib.filter (attrs: builtins.elem ghc attrs.ghcs) ghcsPerCabal)); + ghc-versions = if ghcVersions != [] then ghcVersions else lib.attrNames ghc-versions-tested-with; + # invoke haskell.nix for every ghc specified in tested-with stanzas of .cabal files + pkgs-per-ghc = let + pkgs' = lib.genAttrs ghc-versions + (ghc: haskellPkgs.haskell-nix.cabalProject ({ + inherit src; + compiler-nix-name = ghc; + } // extraArgs)); + # we need to filter out packages and checks that are not specified to build with this compiler + filterFlake' = ghc: flake': flake' // { + packages = filterPackages ghc flake'.packages; + checks = filterPackages ghc flake'.checks; + }; + filterPackages = ghc: packages: lib.filterAttrs + (n: _: builtins.elem (lib.head (lib.splitString ":" n)) ghc-versions-tested-with.${ghc}) packages; + in if ghcVersions != [] then pkgs' + else lib.flip lib.mapAttrs pkgs' (n: v: v // { flake' = filterFlake' n v.flake';}); # invoke haskell.nix for stack.yaml and every file from stackFiles stackYamls = lib.optionals buildWithStack ([ "stack.yaml" ] ++ stackFiles); @@ -43,36 +69,22 @@ let (resolver: haskellPkgs.haskell-nix.stackProject { src = pkgs.runCommand "change resolver" { } '' mkdir -p $out - cp -r ${src}/* . - ${pkgs.gnused}/bin/sed -i 's/resolver:.*/resolver: ${resolver}/' stack.yaml - cp -r ./* $out + cp -rT ${src} $out + ${pkgs.gnused}/bin/sed -i 's/resolver:.*/resolver: ${resolver}/' $out/stack.yaml ''; } // extraArgs)); all-pkgs = pkgs-per-ghc // pkgs-per-stack-yaml // pkgs-per-resolver; - # returns the list of all components for a package - get-package-components = pkg: - # library - lib.optional (pkg ? library) pkg.library - # haddock - ++ lib.optional (pkg ? library) pkg.library.haddock - # exes, tests and benchmarks - ++ lib.attrValues pkg.exes - ++ lib.attrValues pkg.tests - ++ lib.attrValues pkg.benchmarks; - # all components for each specified ghc version or stack yaml build-all = lib.mapAttrs' (prefix: pkg: - let components = lib.concatMap (name: get-package-components pkg.${name}.components) packageNames; - in lib.nameValuePair "${prefix}:build-all" - (pkgs.linkFarmFromDrvs "build-all" components)) all-pkgs; + lib.nameValuePair "${prefix}:build-all" + (pkgs.linkFarmFromDrvs "build-all" (lib.attrValues pkg.flake'.packages))) all-pkgs; # all tests for each specified ghc version or stack yaml test-all = lib.mapAttrs' (prefix: pkg: - let tests = lib.filter lib.isDerivation (lib.concatMap (name: lib.attrValues pkg.${name}.checks) packageNames); - in lib.nameValuePair "${prefix}:test-all" - (pkgs.linkFarmFromDrvs "test-all" tests)) all-pkgs; + lib.nameValuePair "${prefix}:test-all" + (pkgs.linkFarmFromDrvs "test-all" (lib.attrValues pkg.flake'.checks))) all-pkgs; # build matrix used in github actions build-matrix = { include = map (prefix: { inherit prefix; }) (ghc-versions ++ (map replaceDots (stackYamls ++ stackResolvers))); }; @@ -94,7 +106,7 @@ let pkgs.haskell-nix.stackProject { # <...> modules = [ - (optionsLocalPackages = { + (optionsLocalPackages { ghcOptions = [ "-Werror" ]; }) ];