1 {-# LANGUAGE DataKinds #-}
2 {-# LANGUAGE RecordWildCards #-}
4 module Distribution
.Client
.CmdClean
(cleanCommand
, cleanAction
) where
6 import Distribution
.Client
.Compat
.Prelude
9 import Distribution
.Client
.Config
10 ( defaultScriptBuildsDir
12 import Distribution
.Client
.DistDirLayout
14 , defaultDistDirLayout
16 import Distribution
.Client
.Errors
17 import Distribution
.Client
.ProjectConfig
20 import Distribution
.Client
.ProjectFlags
24 , removeIgnoreProjectOption
26 import Distribution
.Client
.Setup
29 import Distribution
.Compat
.Lens
33 import Distribution
.Simple
.Command
40 import Distribution
.Simple
.Setup
49 import Distribution
.Simple
.Utils
55 import Distribution
.System
59 import Distribution
.Utils
.Path
hiding
63 import Distribution
.Verbosity
67 import Control
.Exception
75 import qualified Data
.Set
as Set
76 import System
.Directory
80 , getDirectoryContents
82 , removeDirectoryRecursive
86 import System
.FilePath
89 import System
.IO.Error
92 import qualified System
.Process
as Process
94 data CleanFlags
= CleanFlags
95 { cleanSaveConfig
:: Flag
Bool
96 , cleanVerbosity
:: Flag Verbosity
97 , cleanDistDir
:: Flag
(SymbolicPath Pkg
(Dir Dist
))
101 defaultCleanFlags
:: CleanFlags
104 { cleanSaveConfig
= toFlag
False
105 , cleanVerbosity
= toFlag normal
106 , cleanDistDir
= NoFlag
109 cleanCommand
:: CommandUI
(ProjectFlags
, CleanFlags
)
112 { commandName
= "v2-clean"
113 , commandSynopsis
= "Clean the package store and remove temporary files."
114 , commandUsage
= \pname
->
115 "Usage: " ++ pname
++ " new-clean [FLAGS]\n"
116 , commandDescription
= Just
$ \_
->
118 "Removes all temporary files created during the building process "
119 ++ "(.hi, .o, preprocessed sources, etc.) and also empties out the "
120 ++ "local caches (by default).\n\n"
121 , commandNotes
= Nothing
122 , commandDefaultFlags
= (defaultProjectFlags
, defaultCleanFlags
)
123 , commandOptions
= \showOrParseArgs
->
126 (removeIgnoreProjectOption
(projectFlagsOptions showOrParseArgs
))
127 ++ map (liftOptionL _2
) (cleanOptions showOrParseArgs
)
130 cleanOptions
:: ShowOrParseArgs
-> [OptionField CleanFlags
]
131 cleanOptions showOrParseArgs
=
134 (\v flags
-> flags
{cleanVerbosity
= v
})
137 (\dd flags
-> flags
{cleanDistDir
= dd
})
142 "Save configuration, only remove build artifacts"
144 (\sc flags
-> flags
{cleanSaveConfig
= sc
})
148 cleanAction
:: (ProjectFlags
, CleanFlags
) -> [String] -> GlobalFlags
-> IO ()
149 cleanAction
(ProjectFlags
{..}, CleanFlags
{..}) extraArgs _
= do
150 let verbosity
= fromFlagOrDefault normal cleanVerbosity
151 saveConfig
= fromFlagOrDefault
False cleanSaveConfig
152 mdistDirectory
= fmap getSymbolicPath
$ flagToMaybe cleanDistDir
153 mprojectDir
= flagToMaybe flagProjectDir
154 mprojectFile
= flagToMaybe flagProjectFile
156 -- TODO interpret extraArgs as targets and clean those targets only (issue #7506)
158 -- For now assume all files passed are the names of scripts
159 notScripts
<- filterM (fmap not . doesFileExist) extraArgs
160 unless (null notScripts
) $
161 dieWithException verbosity
$
162 CleanAction notScripts
164 projectRoot
<- either throwIO
return =<< findProjectRoot verbosity mprojectDir mprojectFile
166 let distLayout
= defaultDistDirLayout projectRoot mdistDirectory Nothing
168 -- Do not clean a project if just running a script in it's directory
169 when (null extraArgs ||
isJust mdistDirectory
) $ do
172 let buildRoot
= distBuildRootDirectory distLayout
174 buildRootExists
<- doesDirectoryExist buildRoot
176 when buildRootExists
$ do
177 info verbosity
("Deleting build root (" ++ buildRoot
++ ")")
178 handleDoesNotExist
() $ removeDirectoryRecursive buildRoot
180 let distRoot
= distDirectory distLayout
182 info verbosity
("Deleting dist-newstyle (" ++ distRoot
++ ")")
183 handleDoesNotExist
() $ do
184 if buildOS
== Windows
186 -- Windows can't delete some git files #10182
188 Process
.createProcess_
"attrib" $
190 "attrib -s -h -r " <> distRoot
<> "\\*.* /s /d"
192 (removePathForcibly distRoot
)
193 (\e
-> if isPermissionError e
then removePathForcibly distRoot
else throw e
)
194 else removeDirectoryRecursive distRoot
196 removeEnvFiles
$ distProjectRootDirectory distLayout
198 -- Clean specified script build caches and orphaned caches.
199 -- There is currently no good way to specify to only clean orphaned caches.
200 -- It would be better as part of an explicit gc step (see issue #3333)
201 toClean
<- Set
.fromList
<$> mapM canonicalizePath extraArgs
202 cacheDir
<- defaultScriptBuildsDir
203 existsCD
<- doesDirectoryExist cacheDir
204 caches
<- if existsCD
then listDirectory cacheDir
else return []
205 paths
<- fmap concat . forM caches
$ \cache
-> do
206 let locFile
= cacheDir
</> cache
</> "scriptlocation"
207 exists
<- doesFileExist locFile
208 if exists
then pure
. (,) (cacheDir
</> cache
) <$> readFile locFile
else return []
209 forM_ paths
$ \(cache
, script
) -> do
210 exists
<- doesFileExist script
211 when (not exists || script `Set
.member` toClean
) $ do
212 info verbosity
("Deleting cache (" ++ cache
++ ") for script (" ++ script
++ ")")
213 removeDirectoryRecursive cache
215 removeEnvFiles
:: FilePath -> IO ()
217 (traverse_
(removeFile . (dir
</>)) . filter ((".ghc.environment" ==) . take 16))
218 =<< getDirectoryContents dir