Safe Haskell | Safe-Inferred |
---|---|
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
- module Distribution.Simple.FileMonitor.Types
- data FileMonitor a b = FileMonitor {}
- newFileMonitor :: Eq a => FilePath -> FileMonitor a b
- data MonitorChanged a b
- data MonitorChangedReason a
- checkFileMonitorChanged :: forall a b. (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
Creating and checking sets of monitored files
data FileMonitor a b Source #
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 Source #
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) Source # | |
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 Source #
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 Source # | |
Defined in Distribution.Client.FileMonitor Methods fmap :: (a -> b) -> MonitorChangedReason a -> MonitorChangedReason b # (<$) :: a -> MonitorChangedReason b -> MonitorChangedReason a # | |
Show a => Show (MonitorChangedReason a) Source # | |
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorChangedReason a -> ShowS # show :: MonitorChangedReason a -> String # showList :: [MonitorChangedReason a] -> ShowS # | |
Eq a => Eq (MonitorChangedReason a) Source # | |
Defined in Distribution.Client.FileMonitor Methods (==) :: MonitorChangedReason a -> MonitorChangedReason a -> Bool # (/=) :: MonitorChangedReason a -> MonitorChangedReason a -> Bool # |
checkFileMonitorChanged Source #
Arguments
:: forall a b. (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 Source #
A timestamp to help with the problem of file changes during actions.
See updateFileMonitor
for details.
beginUpdateFileMonitor :: IO MonitorTimestamp Source #
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 Source #
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
data MonitorStateFile Source #
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 Source # | |
Defined in Distribution.Client.FileMonitor Methods structure :: Proxy MonitorStateFile -> Structure # structureHash' :: Tagged MonitorStateFile MD5 | |
Generic MonitorStateFile Source # | |
Defined in Distribution.Client.FileMonitor Associated Types type Rep MonitorStateFile :: Type -> Type # Methods from :: MonitorStateFile -> Rep MonitorStateFile x # to :: Rep MonitorStateFile x -> MonitorStateFile # | |
Show MonitorStateFile Source # | |
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorStateFile -> ShowS # show :: MonitorStateFile -> String # showList :: [MonitorStateFile] -> ShowS # | |
Binary MonitorStateFile Source # | |
Defined in Distribution.Client.FileMonitor Methods put :: MonitorStateFile -> Put # get :: Get MonitorStateFile # putList :: [MonitorStateFile] -> Put # | |
type Rep MonitorStateFile Source # | |
Defined in Distribution.Client.FileMonitor |
data MonitorStateGlob Source #
The state necessary to determine whether the files matched by a globbing match have changed.
Instances
Structured MonitorStateGlob Source # | |
Defined in Distribution.Client.FileMonitor Methods structure :: Proxy MonitorStateGlob -> Structure # structureHash' :: Tagged MonitorStateGlob MD5 | |
Generic MonitorStateGlob Source # | |
Defined in Distribution.Client.FileMonitor Associated Types type Rep MonitorStateGlob :: Type -> Type # Methods from :: MonitorStateGlob -> Rep MonitorStateGlob x # to :: Rep MonitorStateGlob x -> MonitorStateGlob # | |
Show MonitorStateGlob Source # | |
Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorStateGlob -> ShowS # show :: MonitorStateGlob -> String # showList :: [MonitorStateGlob] -> ShowS # | |
Binary MonitorStateGlob Source # | |
Defined in Distribution.Client.FileMonitor Methods put :: MonitorStateGlob -> Put # get :: Get MonitorStateGlob # putList :: [MonitorStateGlob] -> Put # | |
type Rep MonitorStateGlob Source # | |
Defined in Distribution.Client.FileMonitor |