diff options
-rw-r--r-- | lib/asserts.nix | 29 | ||||
-rw-r--r-- | pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix | 138 |
2 files changed, 152 insertions, 15 deletions
diff --git a/lib/asserts.nix b/lib/asserts.nix index 98e0b490acf..8d0a621f4c1 100644 --- a/lib/asserts.nix +++ b/lib/asserts.nix @@ -50,4 +50,33 @@ rec { lib.generators.toPretty {} xs}, but is: ${ lib.generators.toPretty {} val}"; + /* Specialized `assertMsg` for checking if every one of `vals` is one of the elements + of the list `xs`. Useful for checking lists of supported attributes. + + Example: + let sslLibraries = [ "libressl" "bearssl" ]; + in assertEachOneOf "sslLibraries" sslLibraries [ "openssl" "bearssl" ] + stderr> error: each element in sslLibraries must be one of [ + stderr> "openssl" + stderr> "bearssl" + stderr> ], but is: [ + stderr> "libressl" + stderr> "bearssl" + stderr> ] + + Type: + assertEachOneOf :: String -> List ComparableVal -> List ComparableVal -> Bool + */ + assertEachOneOf = + # The name of the variable the user entered `val` into, for inclusion in the error message + name: + # The list of values of what the user provided, to be compared against the values in `xs` + vals: + # The list of valid values + xs: + assertMsg + (lib.all (val: lib.elem val xs) vals) + "each element in ${name} must be one of ${ + lib.generators.toPretty {} xs}, but is: ${ + lib.generators.toPretty {} vals}"; } diff --git a/pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix b/pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix index a6f287c37b3..59a1303764b 100644 --- a/pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix +++ b/pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix @@ -1,10 +1,77 @@ { lib , stdenv -, supportedGhcVersions ? [ "94" ] -, dynamic ? true , haskellPackages , haskell + +# Which GHC versions this hls can support. +# These are looked up in nixpkgs as `pkgs.haskell.packages."ghc${version}`. +# Run +# $ nix-instantiate --eval -E 'with import <nixpkgs> {}; builtins.attrNames pkgs.haskell.packages' +# to list for your nixpkgs version. +, supportedGhcVersions ? [ "94" ] + +# Whether to build hls with the dynamic run-time system. +# See https://haskell-language-server.readthedocs.io/en/latest/troubleshooting.html#static-binaries for more information. +, dynamic ? true + +# Which formatters are supported. Pass `[]` to remove all formatters. +# +# Maintainers: if a new formatter is added, add it here and down in knownFormatters +, supportedFormatters ? [ "ormolu" "fourmolu" "floskell" "stylish-haskell" ] }: + +# make sure the user only sets GHC versions that actually exist +assert supportedGhcVersions != []; +assert + lib.asserts.assertEachOneOf + "supportedGhcVersions" + supportedGhcVersions + (lib.pipe haskell.packages [ + lib.attrNames + (lib.filter (lib.hasPrefix "ghc")) + (map (lib.removePrefix "ghc")) + ]); + +let + # A mapping from formatter name to + # - cabal flag to disable + # - formatter-specific packages that can be stripped from the build of hls if it is disabled + knownFormatters = { + ormolu = { + cabalFlag = "ormolu"; + packages = [ + "hls-ormolu-plugin" + ]; + }; + fourmolu = { + cabalFlag = "fourmolu"; + packages = [ + "hls-fourmolu-plugin" + ]; + }; + floskell = { + cabalFlag = "floskell"; + packages = [ + "hls-floskell-plugin" + ]; + }; + stylish-haskell = { + cabalFlag = "stylishhaskell"; + packages = [ + "hls-stylish-haskell-plugin" + ]; + }; + }; + +in + +# make sure any formatter that is set is actually supported by us +assert + lib.asserts.assertEachOneOf + "supportedFormatters" + supportedFormatters + (lib.attrNames knownFormatters); + # # The recommended way to override this package is # @@ -13,9 +80,43 @@ # for example. Read more about this in the haskell-language-server section of the nixpkgs manual. # let - inherit (lib) concatStringsSep concatMapStringsSep take splitString pipe optionals; - inherit (haskell.lib.compose) justStaticExecutables overrideCabal enableCabalFlag disableCabalFlag; + inherit (haskell.lib.compose) + justStaticExecutables + overrideCabal + enableCabalFlag + disableCabalFlag + ; + getPackages = version: haskell.packages."ghc${version}"; + + # Given the list of `supportedFormatters`, remove every formatter that we know of (knownFormatters) + # by disabling the cabal flag and also removing the formatter libraries. + removeUnnecessaryFormatters = + let + # only formatters that were not requested + unwanted = lib.pipe knownFormatters [ + (lib.filterAttrs (fmt: _: ! (lib.elem fmt supportedFormatters))) + lib.attrsToList + ]; + # all flags to disable + flags = map (fmt: fmt.value.cabalFlag) unwanted; + # all dependencies to remove from hls + deps = lib.concatMap (fmt: fmt.value.packages) unwanted; + + # remove nulls from a list + stripNulls = lib.filter (x: x != null); + + # remove all unwanted dependencies of formatters we don’t want + stripDeps = overrideCabal (drv: { + libraryHaskellDepends = lib.pipe (drv.libraryHaskellDepends or []) [ + # the existing list may contain nulls, so let’s strip them first + stripNulls + (lib.filter (dep: ! (lib.elem dep.pname deps))) + ]; + }); + + in drv: lib.pipe drv ([stripDeps] ++ map disableCabalFlag flags); + tunedHls = hsPkgs: lib.pipe hsPkgs.haskell-language-server ([ (haskell.lib.compose.overrideCabal (old: { @@ -27,32 +128,39 @@ let ''; })) ((if dynamic then enableCabalFlag else disableCabalFlag) "dynamic") - ] ++ optionals (!dynamic) [ + removeUnnecessaryFormatters + ] + ++ lib.optionals (!dynamic) [ justStaticExecutables ]); + targets = version: let packages = getPackages version; - in [ - "haskell-language-server-${packages.ghc.version}" - ]; + in [ "haskell-language-server-${packages.ghc.version}" ]; + makeSymlinks = version: - concatMapStringsSep "\n" (x: - "ln -s ${ - tunedHls (getPackages version) - }/bin/haskell-language-server $out/bin/${x}") (targets version); -in assert supportedGhcVersions != []; stdenv.mkDerivation { + lib.concatMapStringsSep "\n" + (x: + "ln -s ${ + tunedHls (getPackages version) + }/bin/haskell-language-server $out/bin/${x}") + (targets version); + +in stdenv.mkDerivation { pname = "haskell-language-server"; version = haskellPackages.haskell-language-server.version; + buildCommand = '' mkdir -p $out/bin ln -s ${tunedHls (getPackages (builtins.head supportedGhcVersions))}/bin/haskell-language-server-wrapper $out/bin/haskell-language-server-wrapper - ${concatMapStringsSep "\n" makeSymlinks supportedGhcVersions} + ${lib.concatMapStringsSep "\n" makeSymlinks supportedGhcVersions} ''; + meta = haskellPackages.haskell-language-server.meta // { maintainers = [ lib.maintainers.maralorn ]; longDescription = '' This package provides the executables ${ - concatMapStringsSep ", " (x: concatStringsSep ", " (targets x)) + lib.concatMapStringsSep ", " (x: lib.concatStringsSep ", " (targets x)) supportedGhcVersions } and haskell-language-server-wrapper. You can choose for which ghc versions to install hls with pkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "92" ]; }. |