{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} -- | Clean a project. module Stack.Clean ( clean , CleanOpts (..) , CleanCommand (..) ) where import Data.List ( (\\), intercalate ) import qualified Data.Map.Strict as Map import Path.IO ( ignoringAbsence, removeDirRecur ) import Stack.Config ( withBuildConfig ) import Stack.Constants.Config ( rootDistDirFromDir, workDirFromDir ) import Stack.Prelude import Stack.Types.Config import Stack.Types.SourceMap -- | Type representing exceptions thrown by functions exported by the -- "Stack.Clean" module. data CleanException = NonLocalPackages [PackageName] | DeletionFailures [(Path Abs Dir, SomeException)] deriving (Show, Typeable) instance Exception CleanException where displayException (NonLocalPackages pkgs) = concat [ "Error: [S-9463]\n" , "The following packages are not part of this project: " , intercalate ", " (map show pkgs) ] displayException (DeletionFailures failures) = concat [ "Error: [S-6321]\n" , "Exception while recursively deleting:\n" , concatMap (\(dir, e) -> toFilePath dir <> "\n" <> displayException e <> "\n") failures , "Perhaps you do not have permission to delete these files or they \ \are in use?" ] -- | Deletes build artifacts in the current project. clean :: CleanOpts -> RIO Config () clean cleanOpts = do toDelete <- withBuildConfig $ dirsToDelete cleanOpts logDebug $ "Need to delete: " <> fromString (show (map toFilePath toDelete)) failures <- catMaybes <$> mapM cleanDir toDelete case failures of [] -> pure () _ -> throwIO $ DeletionFailures failures cleanDir :: Path Abs Dir -> RIO Config (Maybe (Path Abs Dir, SomeException)) cleanDir dir = do logDebug $ "Deleting directory: " <> fromString (toFilePath dir) liftIO (ignoringAbsence (removeDirRecur dir) >> pure Nothing) `catchAny` \ex -> pure $ Just (dir, ex) dirsToDelete :: CleanOpts -> RIO BuildConfig [Path Abs Dir] dirsToDelete cleanOpts = do packages <- view $ buildConfigL.to (smwProject . bcSMWanted) case cleanOpts of CleanShallow [] -> -- Filter out packages listed as extra-deps mapM (rootDistDirFromDir . ppRoot) $ Map.elems packages CleanShallow targets -> do let localPkgNames = Map.keys packages getPkgDir pkgName' = fmap ppRoot (Map.lookup pkgName' packages) case targets \\ localPkgNames of [] -> mapM rootDistDirFromDir (mapMaybe getPkgDir targets) xs -> throwM (NonLocalPackages xs) CleanFull -> do pkgWorkDirs <- mapM (workDirFromDir . ppRoot) $ Map.elems packages projectWorkDir <- getProjectWorkDir pure (projectWorkDir : pkgWorkDirs) -- | Options for @stack clean@. data CleanOpts = CleanShallow [PackageName] -- ^ Delete the "dist directories" as defined in 'Stack.Constants.Config.distRelativeDir' -- for the given local packages. If no packages are given, all project packages -- should be cleaned. | CleanFull -- ^ Delete all work directories in the project. -- | Clean commands data CleanCommand = Clean | Purge