1 {-# LANGUAGE LambdaCase #-}
2 {-# LANGUAGE ViewPatterns #-}
4 module Distribution
.Client
.Nix
11 import Distribution
.Client
.Compat
.Prelude
13 import Control
.Exception
(bracket)
14 import System
.Directory
16 , createDirectoryIfMissing
19 , removeDirectoryRecursive
22 import System
.Environment
(getArgs, getExecutablePath
)
23 import System
.FilePath
29 import System
.IO (IOMode (..), hClose, openFile)
30 import System
.IO.Error
(isDoesNotExistError)
31 import System
.Process
(showCommandForUser
)
33 import Distribution
.Compat
.Environment
39 import Distribution
.Simple
.Program
49 import Distribution
.Simple
.Setup
(fromFlagOrDefault
)
50 import Distribution
.Simple
.Utils
(debug
, existsAndIsMoreRecentThan
, warn
)
52 import Distribution
.Client
.Config
(SavedConfig
(..))
53 import Distribution
.Client
.GlobalFlags
(GlobalFlags
(..))
55 configureOneProgram
:: Verbosity
-> Program
-> IO ProgramDb
56 configureOneProgram verb prog
=
57 configureProgram verb prog
(addKnownProgram prog emptyProgramDb
)
59 touchFile
:: FilePath -> IO ()
61 catch (removeFile path
) (\e
-> when (isDoesNotExistError e
) (return ()))
62 createDirectoryIfMissing
True (takeDirectory path
)
63 openFile path WriteMode
>>= hClose
65 findNixExpr
:: GlobalFlags
-> SavedConfig
-> IO (Maybe FilePath)
66 findNixExpr globalFlags config
= do
67 -- criteria for deciding to run nix-shell
71 (globalNix
(savedGlobalFlags config
) <> globalNix globalFlags
)
75 let exprPaths
= ["shell.nix", "default.nix"]
76 filterM doesFileExist exprPaths
>>= \case
78 (path
: _
) -> return (Just path
)
81 -- set IN_NIX_SHELL so that builtins.getEnv in Nix works as in nix-shell
82 inFakeNixShell
:: IO a
-> IO a
84 bracket (fakeEnv
"IN_NIX_SHELL" "1") (resetEnv
"IN_NIX_SHELL") (\_
-> f
)
90 resetEnv var
= maybe (unsetEnv var
) (setEnv var
)
99 nixInstantiate verb dist force
' globalFlags config
=
100 findNixExpr globalFlags config
>>= \case
103 alreadyInShell
<- inNixShell
104 shellDrv
<- drvPath dist shellNix
105 instantiated
<- doesFileExist shellDrv
106 -- an extra timestamp file is necessary because the derivation lives in
107 -- the store so its mtime is always 1.
108 let timestamp
= timestampPath dist shellNix
109 upToDate
<- existsAndIsMoreRecentThan timestamp shellNix
111 let ready
= alreadyInShell ||
(instantiated
&& upToDate
&& not force
')
113 let prog
= simpleProgram
"nix-instantiate"
114 progdb
<- configureOneProgram verb prog
116 removeGCRoots verb dist
125 ["--add-root", shellDrv
, "--indirect", shellNix
]
135 -- ^ The action to perform inside a nix-shell. This is also the action
136 -- that will be performed immediately if Nix is disabled.
138 nixShell verb dist globalFlags config go
= do
139 alreadyInShell
<- inNixShell
144 findNixExpr globalFlags config
>>= \case
147 -- Nix integration never worked with cabal-install v2 commands ...
148 warn verb
"Nix integration has been deprecated and will be removed in a future release. You can learn more about it here: https://cabal.readthedocs.io/en/latest/nix-integration.html"
150 let prog
= simpleProgram
"nix-shell"
151 progdb
<- configureOneProgram verb prog
153 cabal
<- getExecutablePath
155 -- alreadyInShell == True in child process
156 setEnv
"CABAL_IN_NIX_SHELL" "1"
158 -- Run cabal with the same arguments inside nix-shell.
159 -- When the child process reaches the top of nixShell, it will
160 -- detect that it is running inside the shell and fall back
162 shellDrv
<- drvPath dist shellNix
169 , gcrootPath dist
</> "result"
173 , showCommandForUser cabal args
176 drvPath
:: FilePath -> FilePath -> IO FilePath
177 drvPath dist path
= do
178 -- We do not actually care about canonicity, but makeAbsolute is only
179 -- available in newer versions of directory.
180 -- We expect the path to be a symlink if it exists, so we do not canonicalize
181 -- the entire path because that would dereference the symlink.
182 distNix
<- canonicalizePath
(dist
</> "nix")
183 -- Nix garbage collector roots must be absolute paths
184 return (distNix
</> replaceExtension
(takeFileName path
) "drv")
186 timestampPath
:: FilePath -> FilePath -> FilePath
187 timestampPath dist path
=
188 dist
</> "nix" </> replaceExtension
(takeFileName path
) "drv.timestamp"
190 gcrootPath
:: FilePath -> FilePath
191 gcrootPath dist
= dist
</> "nix" </> "gcroots"
193 inNixShell
:: IO Bool
194 inNixShell
= isJust <$> lookupEnv
"CABAL_IN_NIX_SHELL"
196 removeGCRoots
:: Verbosity
-> FilePath -> IO ()
197 removeGCRoots verb dist
= do
198 let tgt
= gcrootPath dist
199 exists
<- doesDirectoryExist tgt
201 debug verb
("removing Nix gcroots from " ++ tgt
)
202 removeDirectoryRecursive tgt