77
88-- | Run commands in a nix-shell
99module Stack.Nix
10- (nixCmdName
11- ,nixHelpOptName
12- ,runShellAndExit
10+ ( nixCmdName
11+ , nixHelpOptName
12+ , runShellAndExit
1313 ) where
1414
15- import Stack.Prelude
1615import qualified Data.Text as T
17- import Path.IO
18- import Stack.Config (getInContainer , withBuildConfig )
19- import Stack.Config.Nix (nixCompiler , nixCompilerVersion )
20- import Stack.Constants (platformVariantEnvVar ,inNixShellEnvVar ,inContainerEnvVar )
16+ import Path.IO ( resolveFile )
17+ import RIO.Process ( exec , processContextL )
18+ import Stack.Config ( getInContainer , withBuildConfig )
19+ import Stack.Config.Nix ( nixCompiler , nixCompilerVersion )
20+ import Stack.Constants
21+ ( inContainerEnvVar , inNixShellEnvVar
22+ , platformVariantEnvVar
23+ )
24+ import Stack.Prelude
2125import Stack.Types.Config
2226import Stack.Types.Docker
2327import Stack.Types.Nix
2428import Stack.Types.Version ( showStackVersion )
25- import System.Environment (getArgs ,getExecutablePath ,lookupEnv )
26- import qualified System.FilePath as F
27- import RIO.Process (processContextL , exec )
29+ import System.Environment ( getArgs , getExecutablePath , lookupEnv )
30+ import qualified System.FilePath as F
2831
2932-- | Type representing exceptions thrown by functions exported by the
3033-- "Stack.Nix" module.
@@ -40,91 +43,116 @@ instance Exception NixException where
4043
4144runShellAndExit :: RIO Config void
4245runShellAndExit = do
43- inContainer <- getInContainer -- TODO we can probably assert that this is False based on Stack.Runners now
44- origArgs <- liftIO getArgs
45- let args | inContainer = origArgs -- internal-re-exec version already passed
46- -- first stack when restarting in the container
47- | otherwise =
48- (" --" ++ reExecArgName ++ " =" ++ showStackVersion) : origArgs
49- exePath <- liftIO getExecutablePath
50- config <- view configL
51- envOverride <- view processContextL
52- local (set processContextL envOverride) $ do
53- let cmnd = escape exePath
54- args' = map escape args
46+ inContainer <- getInContainer -- TODO we can probably assert that this is False based on Stack.Runners now
47+ origArgs <- liftIO getArgs
48+ let args | inContainer = origArgs -- internal-re-exec version already passed
49+ -- first stack when restarting in the container
50+ | otherwise =
51+ (" --" ++ reExecArgName ++ " =" ++ showStackVersion) : origArgs
52+ exePath <- liftIO getExecutablePath
53+ config <- view configL
54+ envOverride <- view processContextL
55+ local (set processContextL envOverride) $ do
56+ let cmnd = escape exePath
57+ args' = map escape args
58+
59+ mshellFile <- case configProjectRoot config of
60+ Just projectRoot ->
61+ traverse (resolveFile projectRoot) $ nixInitFile (configNix config)
62+ Nothing -> pure Nothing
5563
56- mshellFile <- case configProjectRoot config of
57- Just projectRoot ->
58- traverse (resolveFile projectRoot) $ nixInitFile (configNix config)
59- Nothing -> pure Nothing
64+ -- This will never result in double loading the build config, since:
65+ --
66+ -- 1. This function explicitly takes a Config, not a HasConfig
67+ --
68+ -- 2. This function ends up exiting before running other code
69+ -- (thus the void return type)
70+ compilerVersion <- withBuildConfig $ view wantedCompilerVersionL
6071
61- -- This will never result in double loading the build config, since:
62- --
63- -- 1. This function explicitly takes a Config, not a HasConfig
64- --
65- -- 2. This function ends up exiting before running other code
66- -- (thus the void return type)
67- compilerVersion <- withBuildConfig $ view wantedCompilerVersionL
72+ ghc <- either throwIO pure $ nixCompiler compilerVersion
73+ ghcVersion <- either throwIO pure $ nixCompilerVersion compilerVersion
74+ let pkgsInConfig = nixPackages (configNix config)
75+ pkgs = pkgsInConfig ++ [ghc, " git" , " gcc" , " gmp" ]
76+ pkgsStr = " [" <> T. intercalate " " pkgs <> " ]"
77+ pureShell = nixPureShell (configNix config)
78+ addGCRoots = nixAddGCRoots (configNix config)
79+ nixopts = case mshellFile of
80+ Just fp ->
81+ [ toFilePath fp
82+ , " --arg"
83+ , " ghc"
84+ , " with (import <nixpkgs> {}); " ++ T. unpack ghc
85+ , " --argstr" , " ghcVersion" , T. unpack ghcVersion
86+ ]
87+ Nothing ->
88+ [ " -E"
89+ , T. unpack $ T. concat
90+ [ " with (import <nixpkgs> {}); "
91+ , " let inputs = " ,pkgsStr," ; "
92+ , " libPath = lib.makeLibraryPath inputs; "
93+ , " stackExtraArgs = lib.concatMap (pkg: "
94+ , " [ ''--extra-lib-dirs=${lib.getLib pkg}/lib'' "
95+ , " ''--extra-include-dirs=${lib.getDev pkg}/include'' ]"
96+ , " ) inputs; in "
97+ , " runCommand ''myEnv'' { "
98+ , " buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; "
99+ -- glibcLocales is necessary on Linux to avoid warnings about
100+ -- GHC being incapable to set the locale.
101+ , T. pack platformVariantEnvVar <> " =''nix''; "
102+ , T. pack inNixShellEnvVar <> " =1; "
103+ , if inContainer
104+ -- If shell is pure, this env var would not
105+ -- be seen by stack inside nix
106+ then T. pack inContainerEnvVar <> " =1; "
107+ else " "
108+ , " LD_LIBRARY_PATH = libPath;"
109+ -- LD_LIBRARY_PATH is set because for now it's needed by
110+ -- builds using Template Haskell
111+ , " STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; "
112+ -- overriding default locale so Unicode output using base
113+ -- won't be broken
114+ , " LANG=\" en_US.UTF-8\" ;"
115+ , " } \"\" "
116+ ]
117+ ]
68118
69- ghc <- either throwIO pure $ nixCompiler compilerVersion
70- ghcVersion <- either throwIO pure $ nixCompilerVersion compilerVersion
71- let pkgsInConfig = nixPackages (configNix config)
72- pkgs = pkgsInConfig ++ [ghc, " git" , " gcc" , " gmp" ]
73- pkgsStr = " [" <> T. intercalate " " pkgs <> " ]"
74- pureShell = nixPureShell (configNix config)
75- addGCRoots = nixAddGCRoots (configNix config)
76- nixopts = case mshellFile of
77- Just fp -> [toFilePath fp
78- ," --arg" , " ghc" , " with (import <nixpkgs> {}); " ++ T. unpack ghc
79- ," --argstr" , " ghcVersion" , T. unpack ghcVersion]
80- Nothing -> [" -E" , T. unpack $ T. concat
81- [" with (import <nixpkgs> {}); "
82- ," let inputs = " ,pkgsStr," ; "
83- , " libPath = lib.makeLibraryPath inputs; "
84- , " stackExtraArgs = lib.concatMap (pkg: "
85- , " [ ''--extra-lib-dirs=${lib.getLib pkg}/lib'' "
86- , " ''--extra-include-dirs=${lib.getDev pkg}/include'' ]"
87- , " ) inputs; in "
88- ," runCommand ''myEnv'' { "
89- ," buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; "
90- ,T. pack platformVariantEnvVar <> " =''nix''; "
91- ,T. pack inNixShellEnvVar <> " =1; "
92- ,if inContainer
93- -- If shell is pure, this env var would not
94- -- be seen by stack inside nix
95- then T. pack inContainerEnvVar <> " =1; "
96- else " "
97- ," LD_LIBRARY_PATH = libPath;" -- LD_LIBRARY_PATH is set because for now it's
98- -- needed by builds using Template Haskell
99- ," STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; "
100- -- overriding default locale so Unicode output using base won't be broken
101- ," LANG=\" en_US.UTF-8\" ;"
102- ," } \"\" " ]]
103- -- glibcLocales is necessary on Linux to avoid warnings about GHC being incapable to set the locale.
104- fullArgs = concat [if pureShell then [" --pure" ] else []
105- ,if addGCRoots then [" --indirect" , " --add-root"
106- ,toFilePath (configWorkDir config)
107- F. </> " nix-gc-symlinks" F. </> " gc-root" ] else []
108- ,map T. unpack (nixShellOptions (configNix config))
109- ,nixopts
110- ,[" --run" , unwords (cmnd: " $STACK_IN_NIX_EXTRA_ARGS" : args')]
111- ]
112- -- Using --run instead of --command so we cannot
113- -- end up in the nix-shell if stack build is Ctrl-C'd
114- pathVar <- liftIO $ lookupEnv " PATH"
115- logDebug $ " PATH is: " <> displayShow pathVar
116- logDebug $
117- " Using a nix-shell environment " <> (case mshellFile of
118- Just path -> " from file: " <> fromString (toFilePath path)
119- Nothing -> " with nix packages: " <> display (T. intercalate " , " pkgs))
120- exec " nix-shell" fullArgs
119+ fullArgs = concat
120+ [ if pureShell then [" --pure" ] else []
121+ , if addGCRoots
122+ then [ " --indirect"
123+ , " --add-root"
124+ , toFilePath
125+ (configWorkDir config)
126+ F. </> " nix-gc-symlinks"
127+ F. </> " gc-root"
128+ ]
129+ else []
130+ , map T. unpack (nixShellOptions (configNix config))
131+ , nixopts
132+ , [" --run" , unwords (cmnd: " $STACK_IN_NIX_EXTRA_ARGS" : args')]
133+ -- Using --run instead of --command so we cannot end up in the
134+ -- nix-shell if stack build is Ctrl-C'd
135+ ]
136+ pathVar <- liftIO $ lookupEnv " PATH"
137+ logDebug $ " PATH is: " <> displayShow pathVar
138+ logDebug $
139+ " Using a nix-shell environment "
140+ <> ( case mshellFile of
141+ Just path ->
142+ " from file: "
143+ <> fromString (toFilePath path)
144+ Nothing ->
145+ " with nix packages: "
146+ <> display (T. intercalate " , " pkgs)
147+ )
148+ exec " nix-shell" fullArgs
121149
122150-- | Shell-escape quotes inside the string and enclose it in quotes.
123151escape :: String -> String
124- escape str = " ' " ++ foldr ( \ c -> if c == ' \' ' then
125- ( " ' \" ' \" ' " ++ )
126- else (c: )) " " str
127- ++ " '"
152+ escape str =
153+ " ' "
154+ ++ foldr ( \ c -> if c == ' \' ' then ( " ' \" ' \" ' " ++ ) else (c: )) " " str
155+ ++ " '"
128156
129157-- | Command-line argument for "nix"
130158nixCmdName :: String
0 commit comments