| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Distribution.Client.FileMonitor
Description
An abstraction to help with re-running actions when files or other input values they depend on have changed.
Synopsis
- data MonitorFilePath
- data MonitorKindFile
- data MonitorKindDir
- data RootedGlob = RootedGlob FilePathRoot Glob
- monitorFile :: FilePath -> MonitorFilePath
- monitorFileHashed :: FilePath -> MonitorFilePath
- monitorNonExistentFile :: FilePath -> MonitorFilePath
- monitorFileExistence :: FilePath -> MonitorFilePath
- monitorDirectory :: FilePath -> MonitorFilePath
- monitorNonExistentDirectory :: FilePath -> MonitorFilePath
- monitorDirectoryExistence :: FilePath -> MonitorFilePath
- monitorFileOrDirectory :: FilePath -> MonitorFilePath
- monitorFileGlob :: RootedGlob -> MonitorFilePath
- monitorFileGlobExistence :: RootedGlob -> MonitorFilePath
- monitorFileSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
- monitorFileHashedSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
- data FileMonitor a b = FileMonitor {}
- newFileMonitor :: Eq a => FilePath -> FileMonitor a b
- data MonitorChanged a b
- data MonitorChangedReason a
- checkFileMonitorChanged :: (Binary a, Structured a, Binary b, Structured b) => FileMonitor a b -> FilePath -> a -> IO (MonitorChanged a b)
- updateFileMonitor :: (Binary a, Structured a, Binary b, Structured b) => FileMonitor a b -> FilePath -> Maybe MonitorTimestamp -> [MonitorFilePath] -> a -> b -> IO ()
- data MonitorTimestamp
- beginUpdateFileMonitor :: IO MonitorTimestamp
- data MonitorStateFileSet
- data MonitorStateFile
- data MonitorStateGlob
Declaring files to monitor
data MonitorFilePath #
A description of a file (or set of files) to monitor for changes.
Where file paths are relative they are relative to a common directory (e.g. project root), not necessarily the process current directory.
Constructors
| MonitorFile | |
Fields | |
| MonitorFileGlob | |
Fields | |
Instances
data MonitorKindFile #
Constructors
| FileExists | |
| FileModTime | |
| FileHashed | |
| FileNotExists |
Instances
| Structured MonitorKindFile # | |||||
Defined in Distribution.Client.FileMonitor Methods structure :: Proxy MonitorKindFile -> Structure # structureHash' :: Tagged MonitorKindFile MD5 | |||||
| Binary MonitorKindFile # | |||||
Defined in Distribution.Client.FileMonitor Methods put :: MonitorKindFile -> Put # get :: Get MonitorKindFile # putList :: [MonitorKindFile] -> Put # | |||||
| Generic MonitorKindFile # | |||||
Defined in Distribution.Client.FileMonitor Associated Types
Methods from :: MonitorKindFile -> Rep MonitorKindFile x # to :: Rep MonitorKindFile x -> MonitorKindFile # | |||||
| Show MonitorKindFile # | |||||
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorKindFile -> ShowS # show :: MonitorKindFile -> String # showList :: [MonitorKindFile] -> ShowS # | |||||
| Eq MonitorKindFile # | |||||
Defined in Distribution.Client.FileMonitor Methods (==) :: MonitorKindFile -> MonitorKindFile -> Bool # (/=) :: MonitorKindFile -> MonitorKindFile -> Bool # | |||||
| type Rep MonitorKindFile # | |||||
Defined in Distribution.Client.FileMonitor type Rep MonitorKindFile = D1 ('MetaData "MonitorKindFile" "Distribution.Client.FileMonitor" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" 'False) ((C1 ('MetaCons "FileExists" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "FileModTime" 'PrefixI 'False) (U1 :: Type -> Type)) :+: (C1 ('MetaCons "FileHashed" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "FileNotExists" 'PrefixI 'False) (U1 :: Type -> Type))) | |||||
data MonitorKindDir #
Constructors
| DirExists | |
| DirModTime | |
| DirNotExists |
Instances
| Structured MonitorKindDir # | |||||
Defined in Distribution.Client.FileMonitor | |||||
| Binary MonitorKindDir # | |||||
Defined in Distribution.Client.FileMonitor Methods put :: MonitorKindDir -> Put # get :: Get MonitorKindDir # putList :: [MonitorKindDir] -> Put # | |||||
| Generic MonitorKindDir # | |||||
Defined in Distribution.Client.FileMonitor Associated Types
Methods from :: MonitorKindDir -> Rep MonitorKindDir x # to :: Rep MonitorKindDir x -> MonitorKindDir # | |||||
| Show MonitorKindDir # | |||||
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorKindDir -> ShowS # show :: MonitorKindDir -> String # showList :: [MonitorKindDir] -> ShowS # | |||||
| Eq MonitorKindDir # | |||||
Defined in Distribution.Client.FileMonitor Methods (==) :: MonitorKindDir -> MonitorKindDir -> Bool # (/=) :: MonitorKindDir -> MonitorKindDir -> Bool # | |||||
| type Rep MonitorKindDir # | |||||
Defined in Distribution.Client.FileMonitor type Rep MonitorKindDir = D1 ('MetaData "MonitorKindDir" "Distribution.Client.FileMonitor" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" 'False) (C1 ('MetaCons "DirExists" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "DirModTime" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "DirNotExists" 'PrefixI 'False) (U1 :: Type -> Type))) | |||||
data RootedGlob #
A file path specified by globbing, relative to some root directory.
Constructors
| RootedGlob | |
Fields
| |
Instances
| Parsec RootedGlob # | |||||
Defined in Distribution.Client.Glob Methods parsec :: CabalParsing m => m RootedGlob # | |||||
| Pretty RootedGlob # | |||||
Defined in Distribution.Client.Glob | |||||
| Structured RootedGlob # | |||||
Defined in Distribution.Client.Glob | |||||
| Binary RootedGlob # | |||||
Defined in Distribution.Client.Glob | |||||
| Generic RootedGlob # | |||||
Defined in Distribution.Client.Glob Associated Types
| |||||
| Show RootedGlob # | |||||
Defined in Distribution.Client.Glob Methods showsPrec :: Int -> RootedGlob -> ShowS # show :: RootedGlob -> String # showList :: [RootedGlob] -> ShowS # | |||||
| Eq RootedGlob # | |||||
Defined in Distribution.Client.Glob | |||||
| type Rep RootedGlob # | |||||
Defined in Distribution.Client.Glob type Rep RootedGlob = D1 ('MetaData "RootedGlob" "Distribution.Client.Glob" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" 'False) (C1 ('MetaCons "RootedGlob" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 FilePathRoot) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Glob))) | |||||
monitorFile :: FilePath -> MonitorFilePath #
Monitor a single file for changes, based on its modification time. The monitored file is considered to have changed if it no longer exists or if its modification time has changed.
monitorFileHashed :: FilePath -> MonitorFilePath #
Monitor a single file for changes, based on its modification time and content hash. The monitored file is considered to have changed if it no longer exists or if its modification time and content hash have changed.
monitorNonExistentFile :: FilePath -> MonitorFilePath #
Monitor a single non-existent file for changes. The monitored file is considered to have changed if it exists.
monitorFileExistence :: FilePath -> MonitorFilePath #
Monitor a single file for existence only. The monitored file is considered to have changed if it no longer exists.
monitorDirectory :: FilePath -> MonitorFilePath #
Monitor a single directory for changes, based on its modification time. The monitored directory is considered to have changed if it no longer exists or if its modification time has changed.
monitorNonExistentDirectory :: FilePath -> MonitorFilePath #
Monitor a single non-existent directory for changes. The monitored directory is considered to have changed if it exists.
monitorDirectoryExistence :: FilePath -> MonitorFilePath #
Monitor a single directory for existence. The monitored directory is considered to have changed only if it no longer exists.
monitorFileOrDirectory :: FilePath -> MonitorFilePath #
Monitor a single file or directory for changes, based on its modification time. The monitored file is considered to have changed if it no longer exists or if its modification time has changed.
monitorFileGlob :: RootedGlob -> MonitorFilePath #
Monitor a set of files (or directories) identified by a file glob. The monitored glob is considered to have changed if the set of files matching the glob changes (i.e. creations or deletions), or for files if the modification time and content hash of any matching file has changed.
monitorFileGlobExistence :: RootedGlob -> MonitorFilePath #
Monitor a set of files (or directories) identified by a file glob for existence only. The monitored glob is considered to have changed if the set of files matching the glob changes (i.e. creations or deletions).
monitorFileSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath] #
Creates a list of files to monitor when you search for a file which
unsuccessfully looked in notFoundAtPaths before finding it at
foundAtPath.
monitorFileHashedSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath] #
Similar to monitorFileSearchPath, but also instructs us to
monitor the hash of the found file.
Creating and checking sets of monitored files
data FileMonitor a b #
A monitor for detecting changes to a set of files. It can be used to
efficiently test if any of a set of files (specified individually or by
glob patterns) has changed since some snapshot. In addition, it also checks
for changes in a value (of type a), and when there are no changes in
either it returns a saved value (of type b).
The main use case looks like this: suppose we have some expensive action that depends on certain pure inputs and reads some set of files, and produces some pure result. We want to avoid re-running this action when it would produce the same result. So we need to monitor the files the action looked at, the other pure input values, and we need to cache the result. Then at some later point, if the input value didn't change, and none of the files changed, then we can re-use the cached result rather than re-running the action.
This can be achieved using a FileMonitor. Each FileMonitor instance
saves state in a disk file, so the file for that has to be specified,
making sure it is unique. The pattern is to use checkFileMonitorChanged
to see if there's been any change. If there is, re-run the action, keeping
track of the files, then use updateFileMonitor to record the current
set of files to monitor, the current input value for the action, and the
result of the action.
The typical occurrence of this pattern is captured by rerunIfChanged
and the Rebuild monad. More complicated cases may need to use
checkFileMonitorChanged and updateFileMonitor directly.
Constructors
| FileMonitor | |
Fields
| |
Arguments
| :: Eq a | |
| => FilePath | The file to cache the state of the file monitor. Must be unique. |
| -> FileMonitor a b |
Define a new file monitor.
It's best practice to define file monitor values once, and then use the
same value for checkFileMonitorChanged and updateFileMonitor as this
ensures you get the same types a and b for reading and writing.
The path of the file monitor itself must be unique because it keeps state on disk and these would clash.
data MonitorChanged a b #
The result of checkFileMonitorChanged: either the monitored files or
value changed (and it tells us which it was) or nothing changed and we get
the cached result.
Constructors
| MonitorUnchanged b [MonitorFilePath] | The monitored files and value did not change. The cached result is
The set of monitored files is also returned. This is useful
for composing or nesting |
| MonitorChanged (MonitorChangedReason a) | The monitor found that something changed. The reason is given. |
Instances
| (Show b, Show a) => Show (MonitorChanged a b) # | |
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorChanged a b -> ShowS # show :: MonitorChanged a b -> String # showList :: [MonitorChanged a b] -> ShowS # | |
data MonitorChangedReason a #
What kind of change checkFileMonitorChanged detected.
Constructors
| MonitoredFileChanged FilePath | One of the files changed (existence, file type, mtime or file
content, depending on the |
| MonitoredValueChanged a | The pure input value changed. The previous cached key value is also returned. This is sometimes
useful when using a |
| MonitorFirstRun | There was no saved monitor state, cached value etc. Ie the file
for the |
| MonitorCorruptCache | There was existing state, but we could not read it. This typically
happens when the code has changed compared to an existing |
Instances
| Functor MonitorChangedReason # | |
Defined in Distribution.Client.FileMonitor Methods fmap :: (a -> b) -> MonitorChangedReason a -> MonitorChangedReason b # (<$) :: a -> MonitorChangedReason b -> MonitorChangedReason a # | |
| Show a => Show (MonitorChangedReason a) # | |
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorChangedReason a -> ShowS # show :: MonitorChangedReason a -> String # showList :: [MonitorChangedReason a] -> ShowS # | |
| Eq a => Eq (MonitorChangedReason a) # | |
Defined in Distribution.Client.FileMonitor Methods (==) :: MonitorChangedReason a -> MonitorChangedReason a -> Bool # (/=) :: MonitorChangedReason a -> MonitorChangedReason a -> Bool # | |
Arguments
| :: (Binary a, Structured a, Binary b, Structured b) | |
| => FileMonitor a b | cache file path |
| -> FilePath | root directory |
| -> a | guard or key value |
| -> IO (MonitorChanged a b) | did the key or any paths change? |
Test if the input value or files monitored by the FileMonitor have
changed. If not, return the cached value.
See FileMonitor for a full explanation.
Arguments
| :: (Binary a, Structured a, Binary b, Structured b) | |
| => FileMonitor a b | cache file path |
| -> FilePath | root directory |
| -> Maybe MonitorTimestamp | timestamp when the update action started |
| -> [MonitorFilePath] | files of interest relative to root |
| -> a | the current key value |
| -> b | the current result value |
| -> IO () |
Update the input value and the set of files monitored by the
FileMonitor, plus the cached value that may be returned in future.
This takes a snapshot of the state of the monitored files right now, so
checkFileMonitorChanged will look for file system changes relative to
this snapshot.
This is typically done once the action has been completed successfully and
we have the action's result and we know what files it looked at. See
FileMonitor for a full explanation.
If we do take the snapshot after the action has completed then we have a
problem. The problem is that files might have changed while the action was
running but after the action read them. If we take the snapshot after the
action completes then we will miss these changes. The solution is to record
a timestamp before beginning execution of the action and then we make the
conservative assumption that any file that has changed since then has
already changed, ie the file monitor state for these files will be such that
checkFileMonitorChanged will report that they have changed.
So if you do use updateFileMonitor after the action (so you can discover
the files used rather than predicting them in advance) then use
beginUpdateFileMonitor to get a timestamp and pass that. Alternatively,
if you take the snapshot in advance of the action, or you're not monitoring
any files then you can use Nothing for the timestamp parameter.
data MonitorTimestamp #
A timestamp to help with the problem of file changes during actions.
See updateFileMonitor for details.
beginUpdateFileMonitor :: IO MonitorTimestamp #
Record a timestamp at the beginning of an action, and when the action
completes call updateFileMonitor passing it the timestamp.
See updateFileMonitor for details.
Internal
data MonitorStateFileSet #
The state necessary to determine whether a set of monitored files has changed. It consists of two parts: a set of specific files to be monitored (index by their path), and a list of globs, which monitor may files at once.
Instances
| Structured MonitorStateFileSet # | |||||
Defined in Distribution.Client.FileMonitor Methods structure :: Proxy MonitorStateFileSet -> Structure # structureHash' :: Tagged MonitorStateFileSet MD5 | |||||
| Binary MonitorStateFileSet # | |||||
Defined in Distribution.Client.FileMonitor Methods put :: MonitorStateFileSet -> Put # get :: Get MonitorStateFileSet # putList :: [MonitorStateFileSet] -> Put # | |||||
| Generic MonitorStateFileSet # | |||||
Defined in Distribution.Client.FileMonitor Associated Types
Methods from :: MonitorStateFileSet -> Rep MonitorStateFileSet x # to :: Rep MonitorStateFileSet x -> MonitorStateFileSet # | |||||
| Show MonitorStateFileSet # | |||||
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorStateFileSet -> ShowS # show :: MonitorStateFileSet -> String # showList :: [MonitorStateFileSet] -> ShowS # | |||||
| type Rep MonitorStateFileSet # | |||||
Defined in Distribution.Client.FileMonitor type Rep MonitorStateFileSet = D1 ('MetaData "MonitorStateFileSet" "Distribution.Client.FileMonitor" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" 'False) (C1 ('MetaCons "MonitorStateFileSet" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 [MonitorStateFile]) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 [MonitorStateGlob]))) | |||||
data MonitorStateFile #
The state necessary to determine whether a monitored file has changed.
This covers all the cases of MonitorFilePath except for globs which is
covered separately by MonitorStateGlob.
The Maybe ModTime is to cover the case where we already consider the
file to have changed, either because it had already changed by the time we
did the snapshot (i.e. too new, changed since start of update process) or it
no longer exists at all.
Instances
| Structured MonitorStateFile # | |||||
Defined in Distribution.Client.FileMonitor Methods structure :: Proxy MonitorStateFile -> Structure # structureHash' :: Tagged MonitorStateFile MD5 | |||||
| Binary MonitorStateFile # | |||||
Defined in Distribution.Client.FileMonitor Methods put :: MonitorStateFile -> Put # get :: Get MonitorStateFile # putList :: [MonitorStateFile] -> Put # | |||||
| Generic MonitorStateFile # | |||||
Defined in Distribution.Client.FileMonitor Associated Types
Methods from :: MonitorStateFile -> Rep MonitorStateFile x # to :: Rep MonitorStateFile x -> MonitorStateFile # | |||||
| Show MonitorStateFile # | |||||
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorStateFile -> ShowS # show :: MonitorStateFile -> String # showList :: [MonitorStateFile] -> ShowS # | |||||
| type Rep MonitorStateFile # | |||||
Defined in Distribution.Client.FileMonitor | |||||
data MonitorStateGlob #
The state necessary to determine whether the files matched by a globbing match have changed.
Instances
| Structured MonitorStateGlob # | |||||
Defined in Distribution.Client.FileMonitor Methods structure :: Proxy MonitorStateGlob -> Structure # structureHash' :: Tagged MonitorStateGlob MD5 | |||||
| Binary MonitorStateGlob # | |||||
Defined in Distribution.Client.FileMonitor Methods put :: MonitorStateGlob -> Put # get :: Get MonitorStateGlob # putList :: [MonitorStateGlob] -> Put # | |||||
| Generic MonitorStateGlob # | |||||
Defined in Distribution.Client.FileMonitor Associated Types
Methods from :: MonitorStateGlob -> Rep MonitorStateGlob x # to :: Rep MonitorStateGlob x -> MonitorStateGlob # | |||||
| Show MonitorStateGlob # | |||||
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorStateGlob -> ShowS # show :: MonitorStateGlob -> String # showList :: [MonitorStateGlob] -> ShowS # | |||||
| type Rep MonitorStateGlob # | |||||
Defined in Distribution.Client.FileMonitor | |||||