hwm-0.2.0: src/HWM/Integrations/Toolchain/Nix.hs
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoImplicitPrelude #-}
module HWM.Integrations.Toolchain.Nix (syncNixFile) where
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import HWM.Core.Common (Name)
import HWM.Core.Formatting (format, toCamelCase)
import HWM.Core.Options (Options (..))
import HWM.Core.Pkg (Pkg (..))
import HWM.Core.Version (Era (eraNixpkgs), Version, formatNixGhc, selectEra)
import HWM.Domain.Config (Config (Config, cfgName))
import HWM.Domain.ConfigT (ConfigT, Env (..))
import HWM.Domain.Environments (BuildEnvironment (..), getBuildEnvironment)
import Relude
syncNixFile :: ConfigT ()
syncNixFile = do
Config {..} <- asks config
ops <- asks options
BuildEnvironment {buildPkgs, buildGHC} <- getBuildEnvironment Nothing
liftIO $ TIO.writeFile (optionsNix ops) (deriveFlakeNix cfgName buildGHC buildPkgs)
renderNixName :: Text -> Text
renderNixName name = toCamelCase name <> "WorkspacePackages"
pkgsNixName :: Text -> Text
pkgsNixName name = "pkgs." <> renderNixName name
generateOverlay :: Name -> Version -> [Pkg] -> [Text]
generateOverlay projectName ghc pkgs =
[ "haskellOverlay = final: prev: {",
" " <> renderNixName projectName <> " = prev.haskell.packages." <> formatNixGhc ghc <> ".extend (hfinal: hprev: {"
]
<> map renderPackage pkgs
<> [ " });",
"};"
]
where
renderPackage pkg =
" " <> format (pkgName pkg) <> " = hfinal.callCabal2nix \"" <> format (pkgName pkg) <> "\" ./" <> format (pkgDirPath pkg) <> " {};"
deriveFlakeNix :: Name -> Version -> [Pkg] -> Text
deriveFlakeNix projectName version pkgs =
T.unlines
$ braces
( [ "description = \"A Haskell " <> projectName <> " workspace generated by HWM(Haskell Workspace Manager)\";",
"inputs = {",
" nixpkgs.url = \"github:NixOS/nixpkgs/" <> eraNixpkgs (selectEra version) <> "\";",
"};",
"outputs = { self, nixpkgs }:"
]
<> letBlock
( [ "supportedSystems = [ \"x86_64-linux\" \"aarch64-linux\" \"x86_64-darwin\" \"aarch64-darwin\" ];",
"forAllSystems = nixpkgs.lib.genAttrs supportedSystems;"
]
<> generateOverlay projectName version pkgs
)
( forAllSystems "packages" (generatePublicPackages projectName pkgs)
<> forAllSystems "devShells" (generateDevShell projectName pkgs)
)
False
)
braces :: [Text] -> [Text]
braces body =
["{"] <> map (" " <>) body <> ["}"]
generatePublicPackages :: Name -> [Pkg] -> [Text]
generatePublicPackages _ [] = []
generatePublicPackages projectName pkgs = map renderPublicPackage pkgs <> renderDefaultPackage
where
renderPublicPackage pkg = format (pkgName pkg) <> " = " <> pkgsNixName projectName <> "." <> format (pkgName pkg) <> ";"
renderDefaultPackage =
let defaultPkg = maybeToList $ find ((projectName ==) . format . pkgName) pkgs
in map (\pkg -> "default = " <> pkgsNixName projectName <> "." <> format (pkgName pkg) <> ";") defaultPkg
generateDevShell :: Name -> [Pkg] -> [Text]
generateDevShell _ [] = [] -- Handle empty workspace
generateDevShell projectName pkgs =
[ "default = " <> pkgsNixName projectName <> ".shellFor {",
" packages = p: [ " <> renderPackageList pkgs <> " ];",
" buildInputs = with " <> pkgsNixName projectName <> "; [",
" cabal-install",
" haskell-language-server",
" hlint",
" ];",
"};"
]
where
renderPackageList = T.intercalate " " . map (\pkg -> "p." <> format (pkgName pkg))
letBlock :: [Text] -> [Text] -> Bool -> [Text]
letBlock h body end =
[" let"]
<> map (" " <>) h
<> [ " in",
" {"
]
<> map (" " <>) body
<> if end then [" });"] else [" };"]
forAllSystems :: Text -> [Text] -> [Text]
forAllSystems system body =
[system <> " = forAllSystems (system:"]
<> letBlock
["pkgs = import nixpkgs { inherit system; overlays = [ haskellOverlay ]; };"]
body
True