Skip to content

Commit 0238dae

Browse files
committed
Re #6771 Add --[no-]omit-this to stack clean
1 parent 770df2f commit 0238dae

File tree

5 files changed

+127
-31
lines changed

5 files changed

+127
-31
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ Other enhancements:
2727
* From GHC 9.12.1, `base` is not a GHC wired-in package. In configuration files,
2828
the `notify-if-base-not-boot` key is introduced, to allow the exisitng
2929
notification to be muted if unwanted when using such GHC versions.
30+
* Add flag `--[no-]omit-this` (default: disabled) to Stack's `clean` command to
31+
omit directories currently in use from cleaning (when `--full` is not
32+
specified).
3033

3134
Bug fixes:
3235

doc/commands/clean_command.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Either
66

77
~~~text
8-
stack clean [PACKAGE]
8+
stack clean [PACKAGE] [--[no-]omit-this]
99
~~~
1010

1111
or
@@ -14,8 +14,17 @@ or
1414
stack clean --full
1515
~~~
1616

17-
`stack clean` deletes build artefacts for one or more project packages specified
18-
as arguments. If no project packages are specified, all project packages are
19-
cleaned.
17+
`stack clean` deletes build artefacts for one or more project packages.
2018

21-
`stack clean --full` deletes the project's Stack working directory.
19+
By default:
20+
21+
* all project packages are cleaned. Pass one or more project package names to
22+
specify individual project packages; and
23+
24+
* the `dist` directory and all of its subdirectories in the Stack work directory
25+
for each relevant project package are deleted. Pass the flag `--omit-this` to
26+
omit, from cleaning, the `dist` work directory (see `stack path --dist-dir`)
27+
and its subdirectories currently in use.
28+
29+
`stack clean --full` deletes the Stack work directories of the project and its
30+
project packages.

src/Stack/CLI.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ commandLineHandler currentDir progName mExecutablePath isInterpreter =
230230

231231
clean = addCommand'
232232
"clean"
233-
"Delete build artefacts for the project packages."
233+
"Delete build artefacts for project packages."
234234
cleanCmd
235235
(cleanOptsParser Clean)
236236

src/Stack/Clean.hs

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,29 @@ Types and functions related to Stack's @clean@ and @purge@ commands.
1212

1313
module Stack.Clean
1414
( CleanOpts (..)
15+
, CleanDepth (..)
1516
, CleanCommand (..)
1617
, cleanCmd
1718
, clean
1819
) where
1920

21+
import Control.Monad.Extra ( concatMapM )
2022
import Data.List ( (\\) )
2123
import qualified Data.Map.Strict as Map
22-
import Path.IO ( ignoringAbsence, removeDirRecur )
24+
import Path ( (</>), isProperPrefixOf )
25+
import Path.IO ( ignoringAbsence, listDirRecur, removeDirRecur )
2326
import Stack.Config ( withBuildConfig )
24-
import Stack.Constants.Config ( rootDistDirFromDir, workDirFromDir )
27+
import Stack.Constants.Config
28+
( distRelativeDir, rootDistDirFromDir, workDirFromDir )
2529
import Stack.Prelude
26-
import Stack.Runners ( ShouldReexec (..), withConfig )
30+
import Stack.Runners
31+
( ShouldReexec (..), withConfig, withDefaultEnvConfig )
2732
import Stack.Types.BuildConfig
2833
( BuildConfig (..), HasBuildConfig (..), getWorkDir )
2934
import Stack.Types.Config ( Config )
35+
import Stack.Types.EnvConfig ( EnvConfig )
3036
import Stack.Types.Runner ( Runner )
31-
import Stack.Types.SourceMap ( SMWanted (..), ppRoot )
37+
import Stack.Types.SourceMap ( ProjectPackage, SMWanted (..), ppRoot )
3238

3339
-- | Type representing \'pretty\' exceptions thrown by functions exported by the
3440
-- "Stack.Clean" module.
@@ -64,7 +70,13 @@ instance Pretty CleanPrettyException where
6470
instance Exception CleanPrettyException
6571

6672
-- | Type representing command line options for the @stack clean@ command.
67-
data CleanOpts
73+
data CleanOpts = CleanOpts
74+
{ depth :: !CleanDepth
75+
, omitThis :: !Bool
76+
}
77+
78+
-- | Type representing depths of cleaning for the @stack clean@ command.
79+
data CleanDepth
6880
= CleanShallow [PackageName]
6981
-- ^ Delete the "dist directories" as defined in
7082
-- 'Stack.Constants.Config.distRelativeDir' for the given project packages.
@@ -84,7 +96,11 @@ cleanCmd = withConfig NoReexec . clean
8496
-- | Deletes build artifacts in the current project.
8597
clean :: CleanOpts -> RIO Config ()
8698
clean cleanOpts = do
87-
toDelete <- withBuildConfig $ dirsToDelete cleanOpts
99+
toDelete <- if cleanOpts.omitThis
100+
then
101+
withDefaultEnvConfig $ dirsToDeleteGivenConfig cleanOpts.depth
102+
else
103+
withBuildConfig $ dirsToDeleteSimple cleanOpts.depth
88104
logDebug $ "Need to delete: " <> fromString (show (map toFilePath toDelete))
89105
failures <- catMaybes <$> mapM cleanDir toDelete
90106
case failures of
@@ -97,20 +113,62 @@ cleanDir dir = do
97113
liftIO (ignoringAbsence (removeDirRecur dir) >> pure Nothing) `catchAny` \ex ->
98114
pure $ Just (dir, ex)
99115

100-
dirsToDelete :: CleanOpts -> RIO BuildConfig [Path Abs Dir]
101-
dirsToDelete cleanOpts = do
116+
dirsToDeleteSimple :: CleanDepth -> RIO BuildConfig [Path Abs Dir]
117+
dirsToDeleteSimple depth = do
118+
packages <- view $ buildConfigL . to (.smWanted.project)
119+
case depth of
120+
CleanShallow [] -> do
121+
-- Filter out packages listed as extra-deps
122+
let pkgNames = Map.elems packages
123+
mapM (rootDistDirFromDir . ppRoot) pkgNames
124+
CleanShallow targets -> do
125+
let localPkgNames = Map.keys packages
126+
getPkgDir pkgName' = fmap ppRoot (Map.lookup pkgName' packages)
127+
pkgNames = mapMaybe getPkgDir targets
128+
case targets \\ localPkgNames of
129+
[] -> mapM rootDistDirFromDir pkgNames
130+
xs -> prettyThrowM (NonLocalPackages xs)
131+
CleanFull -> allWorkDirs $ Map.elems packages
132+
133+
dirsToDeleteGivenConfig :: CleanDepth -> RIO EnvConfig [Path Abs Dir]
134+
dirsToDeleteGivenConfig depth = do
102135
packages <- view $ buildConfigL . to (.smWanted.project)
103-
case cleanOpts of
104-
CleanShallow [] ->
136+
case depth of
137+
CleanShallow [] -> do
105138
-- Filter out packages listed as extra-deps
106-
mapM (rootDistDirFromDir . ppRoot) $ Map.elems packages
139+
let pkgNames = Map.elems packages
140+
concatMapM (unusedRootDistDirsFromDir . ppRoot) pkgNames
107141
CleanShallow targets -> do
108142
let localPkgNames = Map.keys packages
109143
getPkgDir pkgName' = fmap ppRoot (Map.lookup pkgName' packages)
144+
pkgNames = mapMaybe getPkgDir targets
110145
case targets \\ localPkgNames of
111-
[] -> mapM rootDistDirFromDir (mapMaybe getPkgDir targets)
146+
[] -> concatMapM unusedRootDistDirsFromDir pkgNames
112147
xs -> prettyThrowM (NonLocalPackages xs)
113-
CleanFull -> do
114-
pkgWorkDirs <- mapM (workDirFromDir . ppRoot) $ Map.elems packages
115-
projectWorkDir <- getWorkDir
116-
pure (projectWorkDir : pkgWorkDirs)
148+
CleanFull -> allWorkDirs $ Map.elems packages
149+
150+
allWorkDirs :: HasBuildConfig env => [ProjectPackage] -> RIO env [Path Abs Dir]
151+
allWorkDirs pps = do
152+
pkgWorkDirs <- mapM (workDirFromDir . ppRoot) pps
153+
projectWorkDir <- getWorkDir
154+
pure (projectWorkDir : pkgWorkDirs)
155+
156+
unusedRootDistDirsFromDir :: Path Abs Dir -> RIO EnvConfig [Path Abs Dir]
157+
unusedRootDistDirsFromDir pkgDir = do
158+
rootDistDir <- rootDistDirFromDir pkgDir
159+
omitDir <- fmap (pkgDir </>) distRelativeDir
160+
allDirsOmittingDirs rootDistDir omitDir
161+
162+
allDirsOmittingDirs ::
163+
MonadIO m
164+
=> Path Abs Dir
165+
-> Path Abs Dir
166+
-> m [Path Abs Dir]
167+
allDirsOmittingDirs topDir subDir = do
168+
allDirs <- (topDir :) . fst <$> listDirRecur topDir
169+
let isNotInSubDir dir = not
170+
( isProperPrefixOf dir subDir
171+
|| subDir == dir
172+
|| isProperPrefixOf subDir dir
173+
)
174+
pure $ filter isNotInSubDir allDirs

src/Stack/Options/CleanParser.hs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE NoImplicitPrelude #-}
2+
{-# LANGUAGE ApplicativeDo #-}
23

34
{-|
45
Module : Stack.Options.CleanParser
@@ -9,25 +10,50 @@ module Stack.Options.CleanParser
910
( cleanOptsParser
1011
) where
1112

12-
import Options.Applicative ( Parser, flag', help, long, metavar )
13-
import Stack.Clean ( CleanCommand (..), CleanOpts (..) )
13+
import Options.Applicative ( Parser, flag', help, idm, long, metavar )
14+
import Options.Applicative.Builder.Extra ( boolFlags )
15+
import Stack.Clean
16+
( CleanCommand (..), CleanDepth (..), CleanOpts (..) )
1417
import Stack.Prelude
1518
import Stack.Types.PackageName ( packageNameArgument )
1619

1720
-- | Command-line parser for the clean command.
1821
cleanOptsParser :: CleanCommand -> Parser CleanOpts
19-
cleanOptsParser Clean = CleanShallow
20-
<$> packages
21-
<|> doFullClean
22+
cleanOptsParser Clean = shallowParser <|> fullParser
23+
24+
cleanOptsParser Purge = pure $ CleanOpts
25+
{ depth = CleanFull
26+
, omitThis = False
27+
}
28+
29+
shallowParser :: Parser CleanOpts
30+
shallowParser = do
31+
packages <- parsePackages
32+
omitThis <- parseOmitThis
33+
pure $ CleanOpts
34+
{ depth = CleanShallow packages
35+
, omitThis
36+
}
2237
where
23-
packages = many (packageNameArgument
38+
parsePackages = many (packageNameArgument
2439
( metavar "PACKAGE"
2540
<> help "If none specified, clean all project packages."
2641
))
42+
parseOmitThis = boolFlags False
43+
"omit-this"
44+
"the omission of directories currently in use"
45+
idm
46+
47+
fullParser :: Parser CleanOpts
48+
fullParser = do
49+
depth <- doFullClean
50+
pure $ CleanOpts
51+
{ depth
52+
, omitThis = False
53+
}
54+
where
2755
doFullClean = flag' CleanFull
2856
( long "full"
29-
<> help "Delete the project's Stack working directories (.stack-work by \
57+
<> help "Delete the project's Stack work directories (.stack-work by \
3058
\default)."
3159
)
32-
33-
cleanOptsParser Purge = pure CleanFull

0 commit comments

Comments
 (0)