{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NoImplicitPrelude #-} module HWM.Integrations.Toolchain.Nix (syncNixFile) where import qualified Data.Text as T import HWM.Core.Common (Name) import HWM.Core.Formatting (Status, 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 HWM.Runtime.Files (syncFile) import Relude syncNixFile :: ConfigT Status syncNixFile = do Config {..} <- asks config ops <- asks options BuildEnvironment {buildPkgs, buildGHC} <- getBuildEnvironment Nothing syncFile (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