From fc79ecb1e3c01f6f6c6995dc5e5df3f8b61e45ea Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 13:08:06 -0500 Subject: [PATCH 1/8] wip --- flake-module.nix | 272 ++++++++++++++++++++++++----------------------- 1 file changed, 138 insertions(+), 134 deletions(-) diff --git a/flake-module.nix b/flake-module.nix index f28513ab..972135b5 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -94,7 +94,7 @@ in }; }; }; - projectSubmodule = types.submodule { + projectSubmodule = types.submodule (args@{ name, config, ... }: { options = { haskellPackages = mkOption { type = types.attrsOf raw; @@ -148,8 +148,132 @@ in ''; default = { }; }; + outputs = mkOption { + type = types.attrsOf types.raw; + default = { }; + description = '' + The flake outputs for this project. + ''; + }; }; - }; + config = { + outputs = + let + # Like pkgs.runCommand but runs inside nix-shell with a mutable project directory. + # + # It currenty respects only the nativeBuildInputs (and no shellHook for + # instance), which seems sufficient for our purposes. We also set $HOME and + # make the project root mutable, because those are expected when running + # something in a project shell (it is indeed the case with HLS). + runCommandInSimulatedShell = devShell: projectRoot: name: attrs: command: + pkgs.runCommand name (attrs // { nativeBuildInputs = devShell.nativeBuildInputs; }) + '' + # Set pipefail option for safer bash + set -euo pipefail + + # Copy project root to a mutable area + # We expect "command" to mutate it. + export HOME=$TMP + cp -R ${projectRoot} $HOME/project + chmod -R a+w $HOME/project + pushd $HOME/project + + ${command} + touch $out + ''; + projectKey = name; + project = + let + localPackagesOverlay = self: _: + let + fromSdist = self.buildFromCabalSdist or (builtins.trace "Your version of Nixpkgs does not support hs.buildFromCabalSdist yet." (pkg: pkg)); + filterSrc = name: src: lib.cleanSourceWith { inherit src name; filter = path: type: true; }; + in + lib.mapAttrs + (name: value: + let + # callCabal2nix does not need a filtered source. It will + # only pick out the cabal and/or hpack file. + pkgProto = self.callCabal2nix name value.root { }; + pkgFiltered = pkgs.haskell.lib.overrideSrc pkgProto { + src = filterSrc name value.root; + }; + in + fromSdist pkgFiltered) + config.packages; + finalOverlay = + pkgs.lib.composeManyExtensions + [ + # The order here matters. + # + # User's overrides (cfg.overrides) is applied **last** so + # as to give them maximum control over the final package + # set used. + localPackagesOverlay + (pkgs.haskell.lib.packageSourceOverrides config.source-overrides) + config.overrides + ]; + finalPackages = config.haskellPackages.extend finalOverlay; + + defaultBuildTools = hp: with hp; { + inherit + cabal-install + haskell-language-server + ghcid + hlint; + }; + nativeBuildInputs = lib.attrValues (defaultBuildTools finalPackages // config.devShell.tools finalPackages); + devShell = finalPackages.shellFor { + inherit nativeBuildInputs; + packages = p: + map + (name: p."${name}") + (lib.attrNames config.packages); + withHoogle = true; + }; + devShellCheck = name: command: + runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; + in + { + packages = + lib.mapAttrs + (name: _: finalPackages."${name}") + config.packages; + } // lib.optionalAttrs config.devShell.enable { + inherit devShell; + checks = lib.filterAttrs (_: v: v != null) + { + "${projectKey}-hls" = + if config.devShell.hlsCheck.enable then + devShellCheck "hls" "haskell-language-server" + else null; + "${projectKey}-hlint" = + if config.devShell.hlintCheck.enable then + devShellCheck "hlint" '' + hlint ${lib.concatStringsSep " " config.devShell.hlintCheck.dirs} + '' + else null; + }; + }; + in + { + packages = with lib; + mapAttrs' + (packageName: package: { + name = + # Prefix package names with the project name (unless + # project is named `default`) + if projectKey == "default" + then packageName + else "${projectName}-${packageName}"; + value = package; + }) + project.packages; + checks = project.checks; + devShells.${projectKey} = project.devShell; + }; + }; + }); in { options.haskellProjects = mkOption { @@ -158,139 +282,19 @@ in }; }); }; - config = { - perSystem = { config, self', inputs', pkgs, ... }: - let - # Like pkgs.runCommand but runs inside nix-shell with a mutable project directory. - # - # It currenty respects only the nativeBuildInputs (and no shellHook for - # instance), which seems sufficient for our purposes. We also set $HOME and - # make the project root mutable, because those are expected when running - # something in a project shell (it is indeed the case with HLS). - runCommandInSimulatedShell = devShell: projectRoot: name: attrs: command: - pkgs.runCommand name (attrs // { nativeBuildInputs = devShell.nativeBuildInputs; }) - '' - # Set pipefail option for safer bash - set -euo pipefail - - # Copy project root to a mutable area - # We expect "command" to mutate it. - export HOME=$TMP - cp -R ${projectRoot} $HOME/project - chmod -R a+w $HOME/project - pushd $HOME/project - ${command} - touch $out - ''; - projects = - lib.mapAttrs - (projectKey: cfg: - let - inherit (pkgs.lib.lists) optionals; - localPackagesOverlay = self: _: - let - fromSdist = self.buildFromCabalSdist or (builtins.trace "Your version of Nixpkgs does not support hs.buildFromCabalSdist yet." (pkg: pkg)); - filterSrc = name: src: lib.cleanSourceWith { inherit src name; filter = path: type: true; }; - in - lib.mapAttrs - (name: value: - let - # callCabal2nix does not need a filtered source. It will - # only pick out the cabal and/or hpack file. - pkgProto = self.callCabal2nix name value.root { }; - pkgFiltered = pkgs.haskell.lib.overrideSrc pkgProto { - src = filterSrc name value.root; - }; - in - fromSdist pkgFiltered) - cfg.packages; - finalOverlay = - pkgs.lib.composeManyExtensions - [ - # The order here matters. - # - # User's overrides (cfg.overrides) is applied **last** so - # as to give them maximum control over the final package - # set used. - localPackagesOverlay - (pkgs.haskell.lib.packageSourceOverrides cfg.source-overrides) - cfg.overrides - ]; - finalPackages = cfg.haskellPackages.extend finalOverlay; + config = { + perSystem = { config, self', inputs', pkgs, ... }: { + packages = + lib.mkMerge + ( + builtins.map + (haskellProject: + haskellProject.outputs.packages + ) + (lib.attrValues config.haskellProjects) + ); + }; - defaultBuildTools = hp: with hp; { - inherit - cabal-install - haskell-language-server - ghcid - hlint; - }; - nativeBuildInputs = lib.attrValues (defaultBuildTools finalPackages // cfg.devShell.tools finalPackages); - devShell = finalPackages.shellFor { - inherit nativeBuildInputs; - packages = p: - map - (name: p."${name}") - (lib.attrNames cfg.packages); - withHoogle = true; - }; - devShellCheck = name: command: - runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; - in - { - packages = - lib.mapAttrs - (name: _: finalPackages."${name}") - cfg.packages; - } // lib.optionalAttrs cfg.devShell.enable { - inherit devShell; - checks = lib.filterAttrs (_: v: v != null) - { - "${projectKey}-hls" = - if cfg.devShell.hlsCheck.enable then - devShellCheck "hls" "haskell-language-server" - else null; - "${projectKey}-hlint" = - if cfg.devShell.hlintCheck.enable then - devShellCheck "hlint" '' - hlint ${lib.concatStringsSep " " cfg.devShell.hlintCheck.dirs} - '' - else null; - }; - } - ) - config.haskellProjects; - in - { - packages = with lib; - mkMerge - ( - mapAttrsToList - (projectName: project: - mapAttrs' - (packageName: package: { - name = - # Prefix package names with the project name (unless - # project is named `default`) - if projectName == "default" - then packageName - else "${projectName}-${packageName}"; - value = package; - }) - project.packages - ) - projects - ); - checks = - lib.mkMerge - (lib.mapAttrsToList - (_: project: project.checks) - projects); - devShells = - lib.mapAttrs - (_: project: project.devShell) - projects; - }; }; } From 5e64443d36df174887f0fbeda19514568913bdb3 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 13:12:48 -0500 Subject: [PATCH 2/8] finish --- flake-module.nix | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/flake-module.nix b/flake-module.nix index 972135b5..486c7558 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -150,7 +150,6 @@ in }; outputs = mkOption { type = types.attrsOf types.raw; - default = { }; description = '' The flake outputs for this project. ''; @@ -284,17 +283,18 @@ in }; config = { - perSystem = { config, self', inputs', pkgs, ... }: { - packages = - lib.mkMerge - ( - builtins.map - (haskellProject: - haskellProject.outputs.packages - ) - (lib.attrValues config.haskellProjects) - ); - }; + perSystem = { config, self', lib, inputs', pkgs, ... }: + let + flatAttrMap = f: attrs: lib.mkMerge (builtins.map f (lib.attrValues attrs)); + in + { + packages = + flatAttrMap (haskellProject: haskellProject.outputs.packages) config.haskellProjects; + devShells = + flatAttrMap (haskellProject: haskellProject.outputs.devShells) config.haskellProjects; + checks = + flatAttrMap (haskellProject: haskellProject.outputs.checks) config.haskellProjects; + }; }; } From 70f0d19d73ebc940b83c9a5f86c7000b977352c6 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 13:23:03 -0500 Subject: [PATCH 3/8] finialize --- flake-module.nix | 159 ++++++++++++++++++++++------------------------- 1 file changed, 76 insertions(+), 83 deletions(-) diff --git a/flake-module.nix b/flake-module.nix index 486c7558..94f5c171 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -181,95 +181,88 @@ in touch $out ''; projectKey = name; - project = + localPackagesOverlay = self: _: let - localPackagesOverlay = self: _: + fromSdist = self.buildFromCabalSdist or (builtins.trace "Your version of Nixpkgs does not support hs.buildFromCabalSdist yet." (pkg: pkg)); + filterSrc = name: src: lib.cleanSourceWith { inherit src name; filter = path: type: true; }; + in + lib.mapAttrs + (name: value: let - fromSdist = self.buildFromCabalSdist or (builtins.trace "Your version of Nixpkgs does not support hs.buildFromCabalSdist yet." (pkg: pkg)); - filterSrc = name: src: lib.cleanSourceWith { inherit src name; filter = path: type: true; }; + # callCabal2nix does not need a filtered source. It will + # only pick out the cabal and/or hpack file. + pkgProto = self.callCabal2nix name value.root { }; + pkgFiltered = pkgs.haskell.lib.overrideSrc pkgProto { + src = filterSrc name value.root; + }; in - lib.mapAttrs - (name: value: - let - # callCabal2nix does not need a filtered source. It will - # only pick out the cabal and/or hpack file. - pkgProto = self.callCabal2nix name value.root { }; - pkgFiltered = pkgs.haskell.lib.overrideSrc pkgProto { - src = filterSrc name value.root; - }; - in - fromSdist pkgFiltered) - config.packages; - finalOverlay = - pkgs.lib.composeManyExtensions - [ - # The order here matters. - # - # User's overrides (cfg.overrides) is applied **last** so - # as to give them maximum control over the final package - # set used. - localPackagesOverlay - (pkgs.haskell.lib.packageSourceOverrides config.source-overrides) - config.overrides - ]; - finalPackages = config.haskellPackages.extend finalOverlay; + fromSdist pkgFiltered) + config.packages; + finalOverlay = + pkgs.lib.composeManyExtensions + [ + # The order here matters. + # + # User's overrides (cfg.overrides) is applied **last** so + # as to give them maximum control over the final package + # set used. + localPackagesOverlay + (pkgs.haskell.lib.packageSourceOverrides config.source-overrides) + config.overrides + ]; + finalPackages = config.haskellPackages.extend finalOverlay; - defaultBuildTools = hp: with hp; { - inherit - cabal-install - haskell-language-server - ghcid - hlint; - }; - nativeBuildInputs = lib.attrValues (defaultBuildTools finalPackages // config.devShell.tools finalPackages); - devShell = finalPackages.shellFor { - inherit nativeBuildInputs; - packages = p: - map - (name: p."${name}") - (lib.attrNames config.packages); - withHoogle = true; - }; - devShellCheck = name: command: - runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; - in - { - packages = - lib.mapAttrs - (name: _: finalPackages."${name}") - config.packages; - } // lib.optionalAttrs config.devShell.enable { - inherit devShell; - checks = lib.filterAttrs (_: v: v != null) - { - "${projectKey}-hls" = - if config.devShell.hlsCheck.enable then - devShellCheck "hls" "haskell-language-server" - else null; - "${projectKey}-hlint" = - if config.devShell.hlintCheck.enable then - devShellCheck "hlint" '' - hlint ${lib.concatStringsSep " " config.devShell.hlintCheck.dirs} - '' - else null; - }; - }; + defaultBuildTools = hp: with hp; { + inherit + cabal-install + haskell-language-server + ghcid + hlint; + }; + nativeBuildInputs = lib.attrValues (defaultBuildTools finalPackages // config.devShell.tools finalPackages); + devShell = finalPackages.shellFor { + inherit nativeBuildInputs; + packages = p: + map + (name: p."${name}") + (lib.attrNames config.packages); + withHoogle = true; + }; + devShellCheck = name: command: + runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; in { - packages = with lib; - mapAttrs' - (packageName: package: { - name = - # Prefix package names with the project name (unless - # project is named `default`) - if projectKey == "default" - then packageName - else "${projectName}-${packageName}"; - value = package; - }) - project.packages; - checks = project.checks; - devShells.${projectKey} = project.devShell; + packages = + let + mapKeys = f: attrs: lib.mapAttrs' (n: v: { name = f n; value = v; }) attrs; + # Prefix package names with the project name (unless + # project is named `default`) + dropDefaultPrefix = packageName: + if projectKey == "default" + then packageName + else "${projectKey}-${packageName}"; + in + mapKeys dropDefaultPrefix + (lib.mapAttrs + (name: _: finalPackages."${name}") + config.packages); + + devShells = lib.optionalAttrs config.devShell.enable { + "${projectKey}" = devShell; + }; + + checks = lib.optionalAttrs config.devShell.enable (lib.filterAttrs (_: v: v != null) { + "${projectKey}-hls" = + if config.devShell.hlsCheck.enable then + devShellCheck "hls" "haskell-language-server" + else null; + "${projectKey}-hlint" = + if config.devShell.hlintCheck.enable then + devShellCheck "hlint" '' + hlint ${lib.concatStringsSep " " config.devShell.hlintCheck.dirs} + '' + else null; + }); }; }; }); From 9010b3543116ce7d2027e5126a01ffe23d51f01f Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 13:46:39 -0500 Subject: [PATCH 4/8] shift devShell.enable check to outer config --- flake-module.nix | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/flake-module.nix b/flake-module.nix index 94f5c171..78dce350 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -247,11 +247,9 @@ in (name: _: finalPackages."${name}") config.packages); - devShells = lib.optionalAttrs config.devShell.enable { - "${projectKey}" = devShell; - }; + devShells."${projectKey}" = devShell; - checks = lib.optionalAttrs config.devShell.enable (lib.filterAttrs (_: v: v != null) { + checks = lib.filterAttrs (_: v: v != null) { "${projectKey}-hls" = if config.devShell.hlsCheck.enable then devShellCheck "hls" "haskell-language-server" @@ -262,7 +260,7 @@ in hlint ${lib.concatStringsSep " " config.devShell.hlintCheck.dirs} '' else null; - }); + }; }; }; }); @@ -278,15 +276,21 @@ in config = { perSystem = { config, self', lib, inputs', pkgs, ... }: let - flatAttrMap = f: attrs: lib.mkMerge (builtins.map f (lib.attrValues attrs)); + flatAttrMap = f: attrs: lib.mkMerge (lib.attrValues (lib.mapAttrs f attrs)); in { packages = - flatAttrMap (haskellProject: haskellProject.outputs.packages) config.haskellProjects; + flatAttrMap (_: project: project.outputs.packages) config.haskellProjects; devShells = - flatAttrMap (haskellProject: haskellProject.outputs.devShells) config.haskellProjects; + flatAttrMap + (_: project: + lib.optionalAttrs project.devShell.enable project.outputs.devShells) + config.haskellProjects; checks = - flatAttrMap (haskellProject: haskellProject.outputs.checks) config.haskellProjects; + flatAttrMap + (_: project: + lib.optionalAttrs project.devShell.enable project.outputs.checks) + config.haskellProjects; }; }; From 6b1b26a0a11bb4dbf0459ba7efdfd7d3ef85e540 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 13:51:20 -0500 Subject: [PATCH 5/8] changelog, etc. --- CHANGELOG.md | 5 ++++- flake-module.nix | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27bda4fc..5b1e006c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ ## `master` branch -- #37: Group `buildTools` (renamed to `tools`), `hlsCheck` and `hlintCheck` under the `devShell` submodule option; and allow disabling them all using `devShell.enable = false;` (useful if you want haskell-flake to produce just the package outputs). +- New features + - #63: Add `config.haskellProjects.${name}.outputs` containing all flake outputs for that project. +- API changes + - #37: Group `buildTools` (renamed to `tools`), `hlsCheck` and `hlintCheck` under the `devShell` submodule option; and allow disabling them all using `devShell.enable = false;` (useful if you want haskell-flake to produce just the package outputs). ## 0.1.0 diff --git a/flake-module.nix b/flake-module.nix index 78dce350..b6b6d921 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -151,7 +151,9 @@ in outputs = mkOption { type = types.attrsOf types.raw; description = '' - The flake outputs for this project. + The flake outputs generated for this project. + + This is an internal option, not meant to be set by the user. ''; }; }; @@ -276,6 +278,7 @@ in config = { perSystem = { config, self', lib, inputs', pkgs, ... }: let + # Like mapAttrs, but merges the values (also attrsets) of the resulting attrset. flatAttrMap = f: attrs: lib.mkMerge (lib.attrValues (lib.mapAttrs f attrs)); in { @@ -292,6 +295,5 @@ in lib.optionalAttrs project.devShell.enable project.outputs.checks) config.haskellProjects; }; - }; } From d836242d66dae17929a962c58fa757f2d52cae68 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 14:02:16 -0500 Subject: [PATCH 6/8] move main impl to separate file --- flake-module.nix | 111 +------------------------------------------- haskell-project.nix | 110 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 109 deletions(-) create mode 100644 haskell-project.nix diff --git a/flake-module.nix b/flake-module.nix index b6b6d921..bcc70b3e 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -94,7 +94,7 @@ in }; }; }; - projectSubmodule = types.submodule (args@{ name, config, ... }: { + projectSubmodule = types.submodule (args@{ name, config, lib, ... }: { options = { haskellPackages = mkOption { type = types.attrsOf raw; @@ -157,114 +157,7 @@ in ''; }; }; - config = { - outputs = - let - # Like pkgs.runCommand but runs inside nix-shell with a mutable project directory. - # - # It currenty respects only the nativeBuildInputs (and no shellHook for - # instance), which seems sufficient for our purposes. We also set $HOME and - # make the project root mutable, because those are expected when running - # something in a project shell (it is indeed the case with HLS). - runCommandInSimulatedShell = devShell: projectRoot: name: attrs: command: - pkgs.runCommand name (attrs // { nativeBuildInputs = devShell.nativeBuildInputs; }) - '' - # Set pipefail option for safer bash - set -euo pipefail - - # Copy project root to a mutable area - # We expect "command" to mutate it. - export HOME=$TMP - cp -R ${projectRoot} $HOME/project - chmod -R a+w $HOME/project - pushd $HOME/project - - ${command} - touch $out - ''; - projectKey = name; - localPackagesOverlay = self: _: - let - fromSdist = self.buildFromCabalSdist or (builtins.trace "Your version of Nixpkgs does not support hs.buildFromCabalSdist yet." (pkg: pkg)); - filterSrc = name: src: lib.cleanSourceWith { inherit src name; filter = path: type: true; }; - in - lib.mapAttrs - (name: value: - let - # callCabal2nix does not need a filtered source. It will - # only pick out the cabal and/or hpack file. - pkgProto = self.callCabal2nix name value.root { }; - pkgFiltered = pkgs.haskell.lib.overrideSrc pkgProto { - src = filterSrc name value.root; - }; - in - fromSdist pkgFiltered) - config.packages; - finalOverlay = - pkgs.lib.composeManyExtensions - [ - # The order here matters. - # - # User's overrides (cfg.overrides) is applied **last** so - # as to give them maximum control over the final package - # set used. - localPackagesOverlay - (pkgs.haskell.lib.packageSourceOverrides config.source-overrides) - config.overrides - ]; - finalPackages = config.haskellPackages.extend finalOverlay; - - defaultBuildTools = hp: with hp; { - inherit - cabal-install - haskell-language-server - ghcid - hlint; - }; - nativeBuildInputs = lib.attrValues (defaultBuildTools finalPackages // config.devShell.tools finalPackages); - devShell = finalPackages.shellFor { - inherit nativeBuildInputs; - packages = p: - map - (name: p."${name}") - (lib.attrNames config.packages); - withHoogle = true; - }; - devShellCheck = name: command: - runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; - in - { - packages = - let - mapKeys = f: attrs: lib.mapAttrs' (n: v: { name = f n; value = v; }) attrs; - # Prefix package names with the project name (unless - # project is named `default`) - dropDefaultPrefix = packageName: - if projectKey == "default" - then packageName - else "${projectKey}-${packageName}"; - in - mapKeys dropDefaultPrefix - (lib.mapAttrs - (name: _: finalPackages."${name}") - config.packages); - - devShells."${projectKey}" = devShell; - - checks = lib.filterAttrs (_: v: v != null) { - "${projectKey}-hls" = - if config.devShell.hlsCheck.enable then - devShellCheck "hls" "haskell-language-server" - else null; - "${projectKey}-hlint" = - if config.devShell.hlintCheck.enable then - devShellCheck "hlint" '' - hlint ${lib.concatStringsSep " " config.devShell.hlintCheck.dirs} - '' - else null; - }; - }; - }; + config = import ./haskell-project.nix (args // { inherit self pkgs; }); }); in { diff --git a/haskell-project.nix b/haskell-project.nix new file mode 100644 index 00000000..ac1f4caa --- /dev/null +++ b/haskell-project.nix @@ -0,0 +1,110 @@ +# Definition of the `haskellProjects.${name}` submodule's `config` +{ name, self, config, lib, pkgs, ... }: +{ + outputs = + let + # Like pkgs.runCommand but runs inside nix-shell with a mutable project directory. + # + # It currenty respects only the nativeBuildInputs (and no shellHook for + # instance), which seems sufficient for our purposes. We also set $HOME and + # make the project root mutable, because those are expected when running + # something in a project shell (it is indeed the case with HLS). + runCommandInSimulatedShell = devShell: projectRoot: name: attrs: command: + pkgs.runCommand name (attrs // { nativeBuildInputs = devShell.nativeBuildInputs; }) + '' + # Set pipefail option for safer bash + set -euo pipefail + + # Copy project root to a mutable area + # We expect "command" to mutate it. + export HOME=$TMP + cp -R ${projectRoot} $HOME/project + chmod -R a+w $HOME/project + pushd $HOME/project + + ${command} + touch $out + ''; + projectKey = name; + localPackagesOverlay = self: _: + let + fromSdist = self.buildFromCabalSdist or (builtins.trace "Your version of Nixpkgs does not support hs.buildFromCabalSdist yet." (pkg: pkg)); + filterSrc = name: src: lib.cleanSourceWith { inherit src name; filter = path: type: true; }; + in + lib.mapAttrs + (name: value: + let + # callCabal2nix does not need a filtered source. It will + # only pick out the cabal and/or hpack file. + pkgProto = self.callCabal2nix name value.root { }; + pkgFiltered = pkgs.haskell.lib.overrideSrc pkgProto { + src = filterSrc name value.root; + }; + in + fromSdist pkgFiltered) + config.packages; + finalOverlay = + lib.composeManyExtensions + [ + # The order here matters. + # + # User's overrides (cfg.overrides) is applied **last** so + # as to give them maximum control over the final package + # set used. + localPackagesOverlay + (pkgs.haskell.lib.packageSourceOverrides config.source-overrides) + config.overrides + ]; + finalPackages = config.haskellPackages.extend finalOverlay; + + defaultBuildTools = hp: with hp; { + inherit + cabal-install + haskell-language-server + ghcid + hlint; + }; + nativeBuildInputs = lib.attrValues (defaultBuildTools finalPackages // config.devShell.tools finalPackages); + devShell = finalPackages.shellFor { + inherit nativeBuildInputs; + packages = p: + map + (name: p."${name}") + (lib.attrNames config.packages); + withHoogle = true; + }; + devShellCheck = name: command: + runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; + in + { + packages = + let + mapKeys = f: attrs: lib.mapAttrs' (n: v: { name = f n; value = v; }) attrs; + # Prefix package names with the project name (unless + # project is named `default`) + dropDefaultPrefix = packageName: + if projectKey == "default" + then packageName + else "${projectKey}-${packageName}"; + in + mapKeys dropDefaultPrefix + (lib.mapAttrs + (name: _: finalPackages."${name}") + config.packages); + + devShells."${projectKey}" = devShell; + + checks = lib.filterAttrs (_: v: v != null) { + "${projectKey}-hls" = + if config.devShell.hlsCheck.enable then + devShellCheck "hls" "haskell-language-server" + else null; + "${projectKey}-hlint" = + if config.devShell.hlintCheck.enable then + devShellCheck "hlint" '' + hlint ${lib.concatStringsSep " " config.devShell.hlintCheck.dirs} + '' + else null; + }; + }; +} From 6d74866b775948169264d70715831a7f258359d6 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 15:06:38 -0500 Subject: [PATCH 7/8] cleanups --- flake-module.nix | 2 -- haskell-project.nix | 47 ++++++++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/flake-module.nix b/flake-module.nix index bcc70b3e..73639d01 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -3,11 +3,9 @@ let inherit (flake-parts-lib) - mkSubmoduleOptions mkPerSystemOption; inherit (lib) mkOption - mkDefault types; inherit (types) functionTo diff --git a/haskell-project.nix b/haskell-project.nix index ac1f4caa..d3f7ac83 100644 --- a/haskell-project.nix +++ b/haskell-project.nix @@ -1,30 +1,32 @@ # Definition of the `haskellProjects.${name}` submodule's `config` { name, self, config, lib, pkgs, ... }: +let + # Like pkgs.runCommand but runs inside nix-shell with a mutable project directory. + # + # It currenty respects only the nativeBuildInputs (and no shellHook for + # instance), which seems sufficient for our purposes. We also set $HOME and + # make the project root mutable, because those are expected when running + # something in a project shell (it is indeed the case with HLS). + runCommandInSimulatedShell = devShell: projectRoot: name: attrs: command: + pkgs.runCommand name (attrs // { nativeBuildInputs = devShell.nativeBuildInputs; }) + '' + # Set pipefail option for safer bash + set -euo pipefail + + # Copy project root to a mutable area + # We expect "command" to mutate it. + export HOME=$TMP + cp -R ${projectRoot} $HOME/project + chmod -R a+w $HOME/project + pushd $HOME/project + + ${command} + touch $out + ''; +in { outputs = let - # Like pkgs.runCommand but runs inside nix-shell with a mutable project directory. - # - # It currenty respects only the nativeBuildInputs (and no shellHook for - # instance), which seems sufficient for our purposes. We also set $HOME and - # make the project root mutable, because those are expected when running - # something in a project shell (it is indeed the case with HLS). - runCommandInSimulatedShell = devShell: projectRoot: name: attrs: command: - pkgs.runCommand name (attrs // { nativeBuildInputs = devShell.nativeBuildInputs; }) - '' - # Set pipefail option for safer bash - set -euo pipefail - - # Copy project root to a mutable area - # We expect "command" to mutate it. - export HOME=$TMP - cp -R ${projectRoot} $HOME/project - chmod -R a+w $HOME/project - pushd $HOME/project - - ${command} - touch $out - ''; projectKey = name; localPackagesOverlay = self: _: let @@ -73,6 +75,7 @@ (lib.attrNames config.packages); withHoogle = true; }; + devShellCheck = name: command: runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; in From a154e9e80ae195df6f397e25de17603a2da688d9 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 6 Feb 2023 15:08:24 -0500 Subject: [PATCH 8/8] rm unnecessary abstraction --- haskell-project.nix | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/haskell-project.nix b/haskell-project.nix index d3f7ac83..d5f91d4d 100644 --- a/haskell-project.nix +++ b/haskell-project.nix @@ -28,6 +28,7 @@ in outputs = let projectKey = name; + localPackagesOverlay = self: _: let fromSdist = self.buildFromCabalSdist or (builtins.trace "Your version of Nixpkgs does not support hs.buildFromCabalSdist yet." (pkg: pkg)); @@ -75,9 +76,6 @@ in (lib.attrNames config.packages); withHoogle = true; }; - - devShellCheck = name: command: - runCommandInSimulatedShell devShell self "${projectKey}-${name}-check" { } command; in { packages = @@ -100,11 +98,17 @@ in checks = lib.filterAttrs (_: v: v != null) { "${projectKey}-hls" = if config.devShell.hlsCheck.enable then - devShellCheck "hls" "haskell-language-server" + runCommandInSimulatedShell + devShell + self "${projectKey}-hls-check" + { } "haskell-language-server" else null; "${projectKey}-hlint" = if config.devShell.hlintCheck.enable then - devShellCheck "hlint" '' + runCommandInSimulatedShell + devShell + self "${projectKey}-hlint-check" + { } '' hlint ${lib.concatStringsSep " " config.devShell.hlintCheck.dirs} '' else null;