{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
module Stack.Nix
( nixCmdName
, nixHelpOptName
, runShellAndExit
) where
import qualified Data.Text as T
import Path.IO ( resolveFile )
import RIO.Process ( exec, processContextL )
import Stack.Config ( getInContainer, withBuildConfig )
import Stack.Config.Nix ( nixCompiler, nixCompilerVersion )
import Stack.Constants
( inContainerEnvVar, inNixShellEnvVar
, platformVariantEnvVar
)
import Stack.Prelude
import Stack.Types.BuildConfig ( wantedCompilerVersionL )
import Stack.Types.Config
( Config (..), HasConfig (..), configProjectRoot )
import Stack.Types.Docker ( reExecArgName )
import Stack.Types.Runner ( viewExecutablePath )
import Stack.Types.Nix ( NixOpts (..) )
import Stack.Types.Version ( showStackVersion )
import System.Environment ( getArgs, lookupEnv )
import qualified System.FilePath as F
data NixException
= CannotDetermineProjectRoot
deriving Int -> NixException -> ShowS
[NixException] -> ShowS
NixException -> [Char]
(Int -> NixException -> ShowS)
-> (NixException -> [Char])
-> ([NixException] -> ShowS)
-> Show NixException
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> NixException -> ShowS
showsPrec :: Int -> NixException -> ShowS
$cshow :: NixException -> [Char]
show :: NixException -> [Char]
$cshowList :: [NixException] -> ShowS
showList :: [NixException] -> ShowS
Show
instance Exception NixException where
displayException :: NixException -> [Char]
displayException NixException
CannotDetermineProjectRoot =
[Char]
"Error: [S-7384]\n"
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
"Cannot determine project root directory."
runShellAndExit :: RIO Config void
runShellAndExit :: forall void. RIO Config void
runShellAndExit = do
inContainer <- RIO Config Bool
forall (m :: * -> *). MonadIO m => m Bool
getInContainer
origArgs <- liftIO getArgs
let args | Bool
inContainer = [[Char]]
origArgs
| Bool
otherwise =
([Char]
"--" [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
reExecArgName [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
"=" [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
showStackVersion) [Char] -> [[Char]] -> [[Char]]
forall a. a -> [a] -> [a]
: [[Char]]
origArgs
exePath <- toFilePath <$> viewExecutablePath
config <- view configL
envOverride <- view processContextL
local (set processContextL envOverride) $ do
let cmnd = ShowS
escape [Char]
exePath
args' = ShowS -> [[Char]] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ShowS
escape [[Char]]
args
mshellFile <- case configProjectRoot config of
Just Path Abs Dir
projectRoot ->
([Char] -> RIO Config (Path Abs File))
-> Maybe [Char] -> RIO Config (Maybe (Path Abs File))
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Maybe a -> f (Maybe b)
traverse (Path Abs Dir -> [Char] -> RIO Config (Path Abs File)
forall (m :: * -> *).
MonadIO m =>
Path Abs Dir -> [Char] -> m (Path Abs File)
resolveFile Path Abs Dir
projectRoot) Config
config.nix.initFile
Maybe (Path Abs Dir)
Nothing -> Maybe (Path Abs File) -> RIO Config (Maybe (Path Abs File))
forall a. a -> RIO Config a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Path Abs File)
forall a. Maybe a
Nothing
compilerVersion <- withBuildConfig $ view wantedCompilerVersionL
ghc <- either throwIO pure $ nixCompiler compilerVersion
ghcVersion <- either throwIO pure $ nixCompilerVersion compilerVersion
let pkgsInConfig = Config
config.nix.packages
pkgs = [Text]
pkgsInConfig [Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [Text
ghc, Text
"git", Text
"gcc", Text
"gmp"]
pkgsStr = Text
"[" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
T.intercalate Text
" " [Text]
pkgs Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"]"
pureShell = Config
config.nix.pureShell
addGCRoots = Config
config.nix.addGCRoots
nixopts = case Maybe (Path Abs File)
mshellFile of
Just Path Abs File
fp ->
[ Path Abs File -> [Char]
forall b t. Path b t -> [Char]
toFilePath Path Abs File
fp
, [Char]
"--arg"
, [Char]
"ghc"
, [Char]
"with (import <nixpkgs> {}); " [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> [Char]
T.unpack Text
ghc
, [Char]
"--argstr", [Char]
"ghcVersion", Text -> [Char]
T.unpack Text
ghcVersion
]
Maybe (Path Abs File)
Nothing ->
[ [Char]
"-E"
, Text -> [Char]
T.unpack (Text -> [Char]) -> Text -> [Char]
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat
[ Text
"with (import <nixpkgs> {}); "
, Text
"let inputs = ",Text
pkgsStr,Text
"; "
, Text
"libPath = lib.makeLibraryPath inputs; "
, Text
"stackExtraArgs = lib.concatMap (pkg: "
, Text
"[ ''--extra-lib-dirs=${lib.getLib pkg}/lib'' "
, Text
" ''--extra-include-dirs=${lib.getDev pkg}/include'' ]"
, Text
") inputs; in "
, Text
"runCommand ''myEnv'' { "
, Text
"buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; "
, [Char] -> Text
T.pack [Char]
platformVariantEnvVar Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"=''nix''; "
, [Char] -> Text
T.pack [Char]
inNixShellEnvVar Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"=1; "
, if Bool
inContainer
then [Char] -> Text
T.pack [Char]
inContainerEnvVar Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"=1; "
else Text
""
, Text
"LD_LIBRARY_PATH = libPath;"
, Text
"STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; "
, Text
"LANG=\"en_US.UTF-8\";"
, Text
"} \"\""
]
]
fullArgs = [[[Char]]] -> [[Char]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
[ [ [Char]
"--pure" | Bool
pureShell ]
, if Bool
addGCRoots
then [ [Char]
"--indirect"
, [Char]
"--add-root"
, Path Rel Dir -> [Char]
forall b t. Path b t -> [Char]
toFilePath
Config
config.workDir
[Char] -> ShowS
F.</> [Char]
"nix-gc-symlinks"
[Char] -> ShowS
F.</> [Char]
"gc-root"
]
else []
, (Text -> [Char]) -> [Text] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map Text -> [Char]
T.unpack Config
config.nix.shellOptions
, [[Char]]
nixopts
, [[Char]
"--run", [[Char]] -> [Char]
unwords ([Char]
cmnd[Char] -> [[Char]] -> [[Char]]
forall a. a -> [a] -> [a]
:[Char]
"$STACK_IN_NIX_EXTRA_ARGS"[Char] -> [[Char]] -> [[Char]]
forall a. a -> [a] -> [a]
:[[Char]]
args')]
]
pathVar <- liftIO $ lookupEnv "PATH"
logDebug $ "PATH is: " <> displayShow pathVar
logDebug $
"Using a nix-shell environment "
<> ( case mshellFile of
Just Path Abs File
path ->
Utf8Builder
"from file: "
Utf8Builder -> Utf8Builder -> Utf8Builder
forall a. Semigroup a => a -> a -> a
<> [Char] -> Utf8Builder
forall a. IsString a => [Char] -> a
fromString (Path Abs File -> [Char]
forall b t. Path b t -> [Char]
toFilePath Path Abs File
path)
Maybe (Path Abs File)
Nothing ->
Utf8Builder
"with nix packages: "
Utf8Builder -> Utf8Builder -> Utf8Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Utf8Builder
forall a. Display a => a -> Utf8Builder
display (Text -> [Text] -> Text
T.intercalate Text
", " [Text]
pkgs)
)
exec "nix-shell" fullArgs
escape :: String -> String
escape :: ShowS
escape [Char]
str =
[Char]
"'"
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ (Char -> ShowS) -> [Char] -> ShowS
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\Char
c -> if Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\'' then ([Char]
"'\"'\"'"[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++) else (Char
cChar -> ShowS
forall a. a -> [a] -> [a]
:)) [Char]
"" [Char]
str
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
"'"
nixCmdName :: String
nixCmdName :: [Char]
nixCmdName = [Char]
"nix"
nixHelpOptName :: String
nixHelpOptName :: [Char]
nixHelpOptName = [Char]
nixCmdName [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
"-help"