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.
Modulecabal-install-3.18.1.0Haskell2010
Distribution.Client.FileMonitor
An abstraction to help with re-running actions when files or other input values they depend on have changed.
- 13 types
- 16 values
- Packagecabal-install-3.18.1.0
- Exports29
- LanguageHaskell2010
- LicenceBSD-3-Clause
- SourceFileMonitor.hs
Declaring files to monitor
18 declarationsMonitor a single directory for existence. The monitored directory is considered to have changed only if it no longer exists.
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.
Monitor a single file for existence only. The monitored file is considered to have changed if it no longer exists.
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.
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).
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.
Similar to monitorFileSearchPath, but also instructs us to monitor the hash of the found file.
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.
Creates a list of files to monitor when you search for a file which
unsuccessfully looked in notFoundAtPaths before finding it at
foundAtPath.
Monitor a single non-existent directory for changes. The monitored directory is considered to have changed if it exists.
Monitor a single non-existent file for changes. The monitored file is considered to have changed if it exists.
Constructors
FilePathRelativeFilePathRoot FilePathe.g.
"/","c:"or result oftakeDriveFilePathHomeDir
Instances8Eq, Show, Generic, Binary, Parsec, Pretty, …
Eq FilePathRootDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesShow FilePathRootDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesGeneric FilePathRootDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesBinary FilePathRootDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesParsec FilePathRootDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesPretty FilePathRootDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesStructured FilePathRootDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Typestype Rep FilePathRoot = D1 ('MetaDataDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Types"FilePathRoot"
"Distribution.Simple.FileMonitor.Types"
"Cabal-3.18.1.0-cd98"
'False) (C1 ('MetaCons"FilePathRelative"
'PrefixI 'False) U1 :+: (C1 ('MetaCons"FilePathRoot"
'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 FilePath)) :+: C1 ('MetaCons"FilePathHomeDir"
'PrefixI 'False) U1))
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.
Instances6Eq, Show, Generic, Binary, Structured, Rep
Eq MonitorFilePathDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesShow MonitorFilePathDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesGeneric MonitorFilePathDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesBinary MonitorFilePathDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesStructured MonitorFilePathDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Typestype Rep MonitorFilePath = D1 ('MetaDataDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Types"MonitorFilePath"
"Distribution.Simple.FileMonitor.Types"
"Cabal-3.18.1.0-cd98"
'False) (C1 ('MetaCons"MonitorFile"
'PrefixI 'True) (S1 ('MetaSel ('Just"monitorKindFile"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindFile) :*: (S1 ('MetaSel ('Just"monitorKindDir"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindDir) :*: S1 ('MetaSel ('Just"monitorPath"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 FilePath))) :+: C1 ('MetaCons"MonitorFileGlob"
'PrefixI 'True) (S1 ('MetaSel ('Just"monitorKindFile"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindFile) :*: (S1 ('MetaSel ('Just"monitorKindDir"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindDir) :*: S1 ('MetaSel ('Just"monitorPathGlob"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 RootedGlob))))
Instances6Eq, Show, Generic, Binary, Structured, Rep
Eq MonitorKindDirDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesShow MonitorKindDirDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesGeneric MonitorKindDirDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesBinary MonitorKindDirDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesStructured MonitorKindDirDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Typestype Rep MonitorKindDir = D1 ('MetaDataDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Types"MonitorKindDir"
"Distribution.Simple.FileMonitor.Types"
"Cabal-3.18.1.0-cd98"
'False) (C1 ('MetaCons"DirExists"
'PrefixI 'False) U1 :+: (C1 ('MetaCons"DirModTime"
'PrefixI 'False) U1 :+: C1 ('MetaCons"DirNotExists"
'PrefixI 'False) U1))
Instances6Eq, Show, Generic, Binary, Structured, Rep
Eq MonitorKindFileDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesShow MonitorKindFileDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesGeneric MonitorKindFileDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesBinary MonitorKindFileDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesStructured MonitorKindFileDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Typestype Rep MonitorKindFile = D1 ('MetaDataDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Types"MonitorKindFile"
"Distribution.Simple.FileMonitor.Types"
"Cabal-3.18.1.0-cd98"
'False) ((C1 ('MetaCons"FileExists"
'PrefixI 'False) U1 :+: C1 ('MetaCons"FileModTime"
'PrefixI 'False) U1) :+: (C1 ('MetaCons"FileHashed"
'PrefixI 'False) U1 :+: C1 ('MetaCons"FileNotExists"
'PrefixI 'False) U1))
A file path specified by globbing, relative to some root directory.
Constructors
Instances8Eq, Show, Generic, Binary, Parsec, Pretty, …
Eq RootedGlobDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesShow RootedGlobDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesGeneric RootedGlobDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesBinary RootedGlobDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesParsec RootedGlobDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesPretty RootedGlobDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.TypesStructured RootedGlobDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Typestype Rep RootedGlob = D1 ('MetaDataDefined in Cabal-3.18.1.0 · Distribution.Simple.FileMonitor.Types"RootedGlob"
"Distribution.Simple.FileMonitor.Types"
"Cabal-3.18.1.0-cd98"
'False) (C1 ('MetaCons"RootedGlob"
'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 FilePathRoot) :*: S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Glob)))
A filepath specified by globbing.
Instances8Eq, Show, Generic, Binary, Parsec, Pretty, …
Eq GlobDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.InternalShow GlobDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.InternalGeneric GlobDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.InternalBinary GlobDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.InternalParsec GlobDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.InternalPretty GlobDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.InternalStructured GlobDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.Internaltype Rep Glob = D1 ('MetaDataDefined in Cabal-3.18.1.0 · Distribution.Simple.Glob.Internal"Glob"
"Distribution.Simple.Glob.Internal"
"Cabal-3.18.1.0-cd98"
'False) ((C1 ('MetaCons"GlobDir"
'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces) :*: S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Glob)) :+: C1 ('MetaCons"GlobDirRecursive"
'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces))) :+: (C1 ('MetaCons"GlobFile"
'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces)) :+: C1 ('MetaCons"GlobDirTrailing"
'PrefixI 'False) U1))
Creating and checking sets of monitored files
8 declarationsA 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
FileMonitorfileMonitorCacheFile :: FilePathThe file where this FileMonitor should store its state.
fileMonitorKeyValid :: a -> a -> BoolCompares a new cache key with old one to determine if a corresponding cached value is still valid.
Typically this is just an equality test, but in some circumstances it can make sense to do things like subset comparisons.
The first arg is the new value, the second is the old cached value.
fileMonitorCheckIfOnlyValueChanged :: BoolWhen this mode is enabled, if checkFileMonitorChanged returns MonitoredValueChanged then we have the guarantee that no files changed, that the value change was the only change. In the default mode no such guarantee is provided which is slightly faster.
newFileMonitor :: Eq a=> FilePathThe 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.
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
b.The set of monitored files is also returned. This is useful for composing or nesting FileMonitors.
MonitorChanged (MonitorChangedReason a)The monitor found that something changed. The reason is given.
Instances1Show
(Show b, Show a) => Show (MonitorChanged a b)Defined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitor
What kind of change checkFileMonitorChanged detected.
Constructors
MonitoredFileChanged FilePathOne of the files changed (existence, file type, mtime or file content, depending on the MonitorFilePath in question)
MonitoredValueChanged aThe pure input value changed.
The previous cached key value is also returned. This is sometimes useful when using a fileMonitorKeyValid function that is not simply
(==), when invalidation can be partial. In such cases it can make sense to updateFileMonitor with a key value that's a combination of the new and old (e.g. set union).MonitorFirstRunThere was no saved monitor state, cached value etc. Ie the file for the FileMonitor does not exist.
MonitorCorruptCacheThere was existing state, but we could not read it. This typically happens when the code has changed compared to an existing FileMonitor cache file and type of the input value or cached value has changed such that we cannot decode the values. This is completely benign as we can treat is just as if there were no cache file and re-run.
Instances3Functor, Eq, Show
Functor MonitorChangedReasonDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorEq a => Eq (MonitorChangedReason a)Defined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorShow a => Show (MonitorChangedReason a)Defined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitor
checkFileMonitorChanged :: (Binary a, Structured a, Binary b, Structured b)=> FileMonitor a bcache file path
-> FilePathroot directory
-> aguard 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.
updateFileMonitor :: (Binary a, Structured a, Binary b, Structured b)=> FileMonitor a bcache file path
-> FilePathroot directory
-> Maybe MonitorTimestamptimestamp when the update action started
-> [MonitorFilePath]files of interest relative to root
-> athe current key value
-> bthe 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.
A timestamp to help with the problem of file changes during actions. See updateFileMonitor for details.
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
3 declarationsThe 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.
Instances5Show, Generic, Binary, Structured, Rep
Show MonitorStateFileSetDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorGeneric MonitorStateFileSetDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorBinary MonitorStateFileSetDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorStructured MonitorStateFileSetDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitortype Rep MonitorStateFileSet = D1 ('MetaDataDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitor"MonitorStateFileSet"
"Distribution.Client.FileMonitor"
"cabal-install-3.18.1.0-7ZOq76AEqY2DvdiklqxZ41"
'False) (C1 ('MetaCons"MonitorStateFileSet"
'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 [MonitorStateFile]) :*: S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 [MonitorStateGlob])))
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.
Instances5Show, Generic, Binary, Structured, Rep
Show MonitorStateFileDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorGeneric MonitorStateFileDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorBinary MonitorStateFileDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorStructured MonitorStateFileDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitortype Rep MonitorStateFile = D1 ('MetaDataDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitor"MonitorStateFile"
"Distribution.Client.FileMonitor"
"cabal-install-3.18.1.0-7ZOq76AEqY2DvdiklqxZ41"
'False) (C1 ('MetaCons"MonitorStateFile"
'PrefixI 'False) ((S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindFile) :*: S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindDir)) :*: (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 FilePath) :*: S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorStateFileStatus))))
The state necessary to determine whether the files matched by a globbing match have changed.
Instances5Show, Generic, Binary, Structured, Rep
Show MonitorStateGlobDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorGeneric MonitorStateGlobDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorBinary MonitorStateGlobDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitorStructured MonitorStateGlobDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitortype Rep MonitorStateGlob = D1 ('MetaDataDefined in cabal-install-3.18.1.0 · Distribution.Client.FileMonitor"MonitorStateGlob"
"Distribution.Client.FileMonitor"
"cabal-install-3.18.1.0-7ZOq76AEqY2DvdiklqxZ41"
'False) (C1 ('MetaCons"MonitorStateGlob"
'PrefixI 'False) ((S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindFile) :*: S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorKindDir)) :*: (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 FilePathRoot) :*: S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 MonitorStateGlobRel))))