stack-3.11.1: src/Stack/Config/Nix.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Stack.Config.Nix
Description : Nix configuration.
License : BSD-3-Clause
Nix configuration.
-}
module Stack.Config.Nix
( ConfigNixPrettyException
, nixCompiler
, nixCompilerVersion
, nixOptsFromMonoid
) where
import Control.Monad.Extra ( ifM, whenJust )
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Distribution.System ( OS (..) )
import Stack.Constants ( osIsWindows )
import Stack.Prelude
import Stack.Types.Runner ( HasRunner )
import Stack.Types.Nix ( NixOpts (..), NixOptsMonoid (..) )
import System.Directory ( doesFileExist )
-- | Type representing \'pretty\' exceptions thrown by functions exported by the
-- "Stack.Config.Nix" module.
data ConfigNixPrettyException
= NixCannotUseShellFileAndPackagesException !FilePath ![Text]
-- ^ Nix can't be given packages and a shell file at the same time
| GHCMajorVersionUnspecified
| OnlyGHCSupported
deriving Show
instance Pretty ConfigNixPrettyException where
pretty (NixCannotUseShellFileAndPackagesException initFile packages) =
"[S-2726]"
<> line
<> flow "The configuration of Stack's Nix integration cannot specify both \
\a Nix shell file and Nix packages. You have specified:"
<> blankLine
<> spacedBulletedList
[ fillSep
[ flow "Shell file:"
, style File (fromString initFile) <> ";"
, "and"
]
, fillSep $
flow "Nix packages:"
: mkNarrativeList (Just Shell) False prettyPackages
]
where
prettyPackages :: [StyleDoc]
prettyPackages = map (fromString . T.unpack) packages
pretty GHCMajorVersionUnspecified =
"[S-9317]"
<> line
<> flow "Stack's Nix integration requires at least a major version of GHC \
\to be specified. No major version is specified."
pretty OnlyGHCSupported =
"[S-8605]"
<> line
<> flow "Stack's Nix integration supports only GHC binary distributions as \
\compiler."
instance Exception ConfigNixPrettyException
-- | Interprets NixOptsMonoid options.
nixOptsFromMonoid ::
(HasRunner env, HasTerm env)
=> NixOptsMonoid
-> OS
-> RIO env NixOpts
nixOptsFromMonoid nixMonoid os = do
let defaultPure = case os of
OSX -> False
_ -> True
pureShell = fromFirst defaultPure nixMonoid.pureShell
packages = fromFirst [] nixMonoid.packages
initFile = getFirst nixMonoid.initFile
shellOptions =
fromFirst [] nixMonoid.shellOptions
++ prefixAll (T.pack "-I") (fromFirst [] nixMonoid.path)
addGCRoots = fromFirstFalse nixMonoid.addGCRoots
-- Enable Nix-mode by default on NixOS, unless Docker-mode was specified
osIsNixOS <- isNixOS
let nixEnable0 = fromFirst osIsNixOS nixMonoid.enable
enable <-
if nixEnable0 && osIsWindows
then do
prettyNoteS
"Disabling Nix integration, since this is being run in Windows."
pure False
else pure nixEnable0
unless (null packages) $ whenJust initFile $ \fp ->
prettyThrowIO $ NixCannotUseShellFileAndPackagesException fp packages
pure NixOpts
{ enable
, pureShell
, packages
, initFile
, shellOptions
, addGCRoots
}
where
prefixAll p (x:xs) = p : x : prefixAll p xs
prefixAll _ _ = []
nixCompiler :: WantedCompiler -> Either ConfigNixPrettyException T.Text
nixCompiler compilerVersion =
case compilerVersion of
WCGhc version ->
case T.split (== '.') (fromString $ versionString version) of
x : y : minor ->
Right $
case minor of
[] ->
-- The minor version is not specified. Select the latest minor
-- version in Nixpkgs corresponding to the requested major
-- version.
let major = T.concat [x, y] in
"(let compilers = builtins.filter \
\(name: builtins.match \
\\"ghc" <> major <> "[[:digit:]]*\" name != null) \
\(lib.attrNames haskell.compiler); in \
\if compilers == [] \
\then abort \"No compiler found for GHC "
<> T.pack (versionString version) <> "\"\
\else haskell.compiler.${builtins.head compilers})"
_ -> "haskell.compiler.ghc" <> T.concat (x : y : minor)
_ -> Left GHCMajorVersionUnspecified
WCGhcjs{} -> Left OnlyGHCSupported
WCGhcGit{} -> Left OnlyGHCSupported
nixCompilerVersion :: WantedCompiler -> Either ConfigNixPrettyException T.Text
nixCompilerVersion compilerVersion =
case compilerVersion of
WCGhc version ->
case T.split (== '.') (fromString $ versionString version) of
x : y : minor -> Right $ "ghc" <> T.concat (x : y : minor)
_ -> Left GHCMajorVersionUnspecified
WCGhcjs{} -> Left OnlyGHCSupported
WCGhcGit{} -> Left OnlyGHCSupported
isNixOS :: MonadIO m => m Bool
isNixOS = liftIO $ do
let fp = "/etc/os-release"
ifM (doesFileExist fp)
(T.isInfixOf "ID=nixos" <$> TIO.readFile fp)
(pure False)