Skip to content

Commit b2a62b9

Browse files
sheafMikolaj
authored andcommitted
Split off file monitoring types into Cabal library
This commit splits off the file monitoring types from cabal-install into the Cabal library, so that they can be referred to in pre-build rules for SetupHooks. This will allow package authors with Hooks build-type to monitor files and directories specified by globbing.
1 parent 58af854 commit b2a62b9

File tree

8 files changed

+1775
-561
lines changed

8 files changed

+1775
-561
lines changed

Cabal-hooks/src/Distribution/Simple/SetupHooks.hs

Lines changed: 460 additions & 0 deletions
Large diffs are not rendered by default.

Cabal/Cabal.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ library
106106
Distribution.Simple.Compiler
107107
Distribution.Simple.Configure
108108
Distribution.Simple.Errors
109+
Distribution.Simple.FileMonitor.Types
109110
Distribution.Simple.Flag
110111
Distribution.Simple.GHC
111112
Distribution.Simple.GHCJS
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
{-# LANGUAGE DeriveGeneric #-}
2+
3+
-- |
4+
-- Module: Distribution.Simple.FileMonitor.Types
5+
--
6+
-- Types for monitoring files and directories.
7+
module Distribution.Simple.FileMonitor.Types
8+
( -- * Globs with respect to a root
9+
RootedGlob (..)
10+
, FilePathRoot (..)
11+
, Glob
12+
13+
-- * File monitoring
14+
, MonitorFilePath (..)
15+
, MonitorKindFile (..)
16+
, MonitorKindDir (..)
17+
18+
-- ** Utility constructors of t'MonitorFilePath'
19+
, monitorFile
20+
, monitorFileHashed
21+
, monitorNonExistentFile
22+
, monitorFileExistence
23+
, monitorDirectory
24+
, monitorNonExistentDirectory
25+
, monitorDirectoryExistence
26+
, monitorFileOrDirectory
27+
, monitorFileGlob
28+
, monitorFileGlobExistence
29+
, monitorFileSearchPath
30+
, monitorFileHashedSearchPath
31+
)
32+
where
33+
34+
import Distribution.Compat.Prelude
35+
import Distribution.Simple.Glob.Internal
36+
( Glob (..)
37+
)
38+
39+
import qualified Distribution.Compat.CharParsing as P
40+
import Distribution.Parsec
41+
import Distribution.Pretty
42+
import qualified Text.PrettyPrint as Disp
43+
44+
--------------------------------------------------------------------------------
45+
-- Rooted globs.
46+
--
47+
48+
-- | A file path specified by globbing, relative
49+
-- to some root directory.
50+
data RootedGlob
51+
= RootedGlob
52+
FilePathRoot
53+
-- ^ what the glob is relative to
54+
Glob
55+
-- ^ the glob
56+
deriving (Eq, Show, Generic)
57+
58+
instance Binary RootedGlob
59+
instance Structured RootedGlob
60+
61+
data FilePathRoot
62+
= FilePathRelative
63+
| -- | e.g. @"/"@, @"c:\"@ or result of 'takeDrive'
64+
FilePathRoot FilePath
65+
| FilePathHomeDir
66+
deriving (Eq, Show, Generic)
67+
68+
instance Binary FilePathRoot
69+
instance Structured FilePathRoot
70+
71+
------------------------------------------------------------------------------
72+
-- Types for specifying files to monitor
73+
--
74+
75+
-- | A description of a file (or set of files) to monitor for changes.
76+
--
77+
-- Where file paths are relative they are relative to a common directory
78+
-- (e.g. project root), not necessarily the process current directory.
79+
data MonitorFilePath
80+
= MonitorFile
81+
{ monitorKindFile :: !MonitorKindFile
82+
, monitorKindDir :: !MonitorKindDir
83+
, monitorPath :: !FilePath
84+
}
85+
| MonitorFileGlob
86+
{ monitorKindFile :: !MonitorKindFile
87+
, monitorKindDir :: !MonitorKindDir
88+
, monitorPathGlob :: !RootedGlob
89+
}
90+
deriving (Eq, Show, Generic)
91+
92+
data MonitorKindFile
93+
= FileExists
94+
| FileModTime
95+
| FileHashed
96+
| FileNotExists
97+
deriving (Eq, Show, Generic)
98+
99+
data MonitorKindDir
100+
= DirExists
101+
| DirModTime
102+
| DirNotExists
103+
deriving (Eq, Show, Generic)
104+
105+
instance Binary MonitorFilePath
106+
instance Binary MonitorKindFile
107+
instance Binary MonitorKindDir
108+
109+
instance Structured MonitorFilePath
110+
instance Structured MonitorKindFile
111+
instance Structured MonitorKindDir
112+
113+
-- | Monitor a single file for changes, based on its modification time.
114+
-- The monitored file is considered to have changed if it no longer
115+
-- exists or if its modification time has changed.
116+
monitorFile :: FilePath -> MonitorFilePath
117+
monitorFile = MonitorFile FileModTime DirNotExists
118+
119+
-- | Monitor a single file for changes, based on its modification time
120+
-- and content hash. The monitored file is considered to have changed if
121+
-- it no longer exists or if its modification time and content hash have
122+
-- changed.
123+
monitorFileHashed :: FilePath -> MonitorFilePath
124+
monitorFileHashed = MonitorFile FileHashed DirNotExists
125+
126+
-- | Monitor a single non-existent file for changes. The monitored file
127+
-- is considered to have changed if it exists.
128+
monitorNonExistentFile :: FilePath -> MonitorFilePath
129+
monitorNonExistentFile = MonitorFile FileNotExists DirNotExists
130+
131+
-- | Monitor a single file for existence only. The monitored file is
132+
-- considered to have changed if it no longer exists.
133+
monitorFileExistence :: FilePath -> MonitorFilePath
134+
monitorFileExistence = MonitorFile FileExists DirNotExists
135+
136+
-- | Monitor a single directory for changes, based on its modification
137+
-- time. The monitored directory is considered to have changed if it no
138+
-- longer exists or if its modification time has changed.
139+
monitorDirectory :: FilePath -> MonitorFilePath
140+
monitorDirectory = MonitorFile FileNotExists DirModTime
141+
142+
-- | Monitor a single non-existent directory for changes. The monitored
143+
-- directory is considered to have changed if it exists.
144+
monitorNonExistentDirectory :: FilePath -> MonitorFilePath
145+
-- Just an alias for monitorNonExistentFile, since you can't
146+
-- tell the difference between a non-existent directory and
147+
-- a non-existent file :)
148+
monitorNonExistentDirectory = monitorNonExistentFile
149+
150+
-- | Monitor a single directory for existence. The monitored directory is
151+
-- considered to have changed only if it no longer exists.
152+
monitorDirectoryExistence :: FilePath -> MonitorFilePath
153+
monitorDirectoryExistence = MonitorFile FileNotExists DirExists
154+
155+
-- | Monitor a single file or directory for changes, based on its modification
156+
-- time. The monitored file is considered to have changed if it no longer
157+
-- exists or if its modification time has changed.
158+
monitorFileOrDirectory :: FilePath -> MonitorFilePath
159+
monitorFileOrDirectory = MonitorFile FileModTime DirModTime
160+
161+
-- | Monitor a set of files (or directories) identified by a file glob.
162+
-- The monitored glob is considered to have changed if the set of files
163+
-- matching the glob changes (i.e. creations or deletions), or for files if the
164+
-- modification time and content hash of any matching file has changed.
165+
monitorFileGlob :: RootedGlob -> MonitorFilePath
166+
monitorFileGlob = MonitorFileGlob FileHashed DirExists
167+
168+
-- | Monitor a set of files (or directories) identified by a file glob for
169+
-- existence only. The monitored glob is considered to have changed if the set
170+
-- of files matching the glob changes (i.e. creations or deletions).
171+
monitorFileGlobExistence :: RootedGlob -> MonitorFilePath
172+
monitorFileGlobExistence = MonitorFileGlob FileExists DirExists
173+
174+
-- | Creates a list of files to monitor when you search for a file which
175+
-- unsuccessfully looked in @notFoundAtPaths@ before finding it at
176+
-- @foundAtPath@.
177+
monitorFileSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
178+
monitorFileSearchPath notFoundAtPaths foundAtPath =
179+
monitorFile foundAtPath
180+
: map monitorNonExistentFile notFoundAtPaths
181+
182+
-- | Similar to 'monitorFileSearchPath', but also instructs us to
183+
-- monitor the hash of the found file.
184+
monitorFileHashedSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
185+
monitorFileHashedSearchPath notFoundAtPaths foundAtPath =
186+
monitorFileHashed foundAtPath
187+
: map monitorNonExistentFile notFoundAtPaths
188+
189+
------------------------------------------------------------------------------
190+
-- Parsing & pretty-printing
191+
--
192+
193+
instance Pretty RootedGlob where
194+
pretty (RootedGlob root pathglob) = pretty root Disp.<> pretty pathglob
195+
196+
instance Parsec RootedGlob where
197+
parsec = do
198+
root <- parsec
199+
case root of
200+
FilePathRelative -> RootedGlob root <$> parsec
201+
_ -> RootedGlob root <$> parsec <|> pure (RootedGlob root GlobDirTrailing)
202+
203+
instance Pretty FilePathRoot where
204+
pretty FilePathRelative = Disp.empty
205+
pretty (FilePathRoot root) = Disp.text root
206+
pretty FilePathHomeDir = Disp.char '~' Disp.<> Disp.char '/'
207+
208+
instance Parsec FilePathRoot where
209+
parsec = root <|> P.try home <|> P.try drive <|> pure FilePathRelative
210+
where
211+
root = FilePathRoot "/" <$ P.char '/'
212+
home = FilePathHomeDir <$ P.string "~/"
213+
drive = do
214+
dr <- P.satisfy $ \c -> (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
215+
_ <- P.char ':'
216+
_ <- P.char '/' <|> P.char '\\'
217+
return (FilePathRoot (toUpper dr : ":\\"))

0 commit comments

Comments
 (0)