Distribution.Client.RebuildMonad

cabal-install-3.12.1.0: The command-line interface for Cabal and Hackage.

Safe Haskell | None
Language | Haskell2010

Distribution.Client.RebuildMonad

Contents

- Rebuild monad
- Setting up file monitoring
  - Monitoring file globs
- Using a file monitor
- Utils

Description

An abstraction for re-running actions if values or files have changed.

This is not a full-blown make-style incremental build system, it's a bit more ad-hoc than that, but it's easier to integrate with existing code.

It's a convenient interface to the Distribution.Client.FileMonitor functions.

Synopsis

- data Rebuild a
- runRebuild :: FilePath -> Rebuild a -> IO a
- execRebuild :: FilePath -> Rebuild a -> IO [MonitorFilePath]
- askRoot :: Rebuild FilePath
- monitorFiles :: [MonitorFilePath] -> Rebuild ()
- data MonitorFilePath
- monitorFile :: FilePath -> MonitorFilePath
- monitorFileHashed :: FilePath -> MonitorFilePath
- monitorNonExistentFile :: FilePath -> MonitorFilePath
- monitorDirectory :: FilePath -> MonitorFilePath
- monitorNonExistentDirectory :: FilePath -> MonitorFilePath
- monitorDirectoryExistence :: FilePath -> MonitorFilePath
- monitorFileOrDirectory :: FilePath -> MonitorFilePath
- monitorFileSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
- monitorFileHashedSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
- monitorFileGlob :: RootedGlob -> MonitorFilePath
- monitorFileGlobExistence :: RootedGlob -> MonitorFilePath
- data RootedGlob = RootedGlob FilePathRoot Glob
- data FilePathRoot
  - = FilePathRelative
  - | FilePathRoot FilePath
  - | FilePathHomeDir
- data Glob
  - = GlobDir !GlobPieces !Glob
  - | GlobDirRecursive !GlobPieces
  - | GlobFile !GlobPieces
  - | GlobDirTrailing
- data GlobPiece
  - = WildCard
  - | Literal String
  - | Union [GlobPieces]
- data FileMonitor a b = FileMonitor {
  - fileMonitorCacheFile :: FilePath
  - fileMonitorKeyValid :: a -> a -> Bool
  - fileMonitorCheckIfOnlyValueChanged :: Bool
  }
- newFileMonitor :: Eq a => FilePath -> FileMonitor a b
- rerunIfChanged :: (Binary a, Structured a, Binary b, Structured b) => Verbosity -> FileMonitor a b -> a -> Rebuild b -> Rebuild b
- delayInitSharedResource :: IO a -> Rebuild (Rebuild a)
- delayInitSharedResources :: Ord k => (k -> IO v) -> Rebuild (k -> Rebuild v)
- matchFileGlob :: RootedGlob -> Rebuild [FilePath]
- getDirectoryContentsMonitored :: FilePath -> Rebuild [FilePath]
- createDirectoryMonitored :: Bool -> FilePath -> Rebuild ()
- monitorDirectoryStatus :: FilePath -> Rebuild Bool
- doesFileExistMonitored :: FilePath -> Rebuild Bool
- need :: FilePath -> Rebuild ()
- needIfExists :: FilePath -> Rebuild ()
- findFileWithExtensionMonitored :: [Suffix] -> [FilePath] -> FilePath -> Rebuild (Maybe FilePath)
- findFirstFileMonitored :: (a -> FilePath) -> [a] -> Rebuild (Maybe a)
- findFileMonitored :: [FilePath] -> FilePath -> Rebuild (Maybe FilePath)

# Rebuild monad

data Rebuild a #

A monad layered on top of `IO` to help with re-running actions when the input files and values they depend on change. The crucial operations are `rerunIfChanged` and `monitorFiles`.

#### Instances

Instances details

MonadIO Rebuild # |
Instance details Defined in Distribution.Client.RebuildMonad Methods liftIO :: IO a -> Rebuild a #
Applicative Rebuild # |
Instance details Defined in Distribution.Client.RebuildMonad Methods pure :: a -> Rebuild a # (<*>) :: Rebuild (a -> b) -> Rebuild a -> Rebuild b # liftA2 :: (a -> b -> c) -> Rebuild a -> Rebuild b -> Rebuild c # (*>) :: Rebuild a -> Rebuild b -> Rebuild b # (<*) :: Rebuild a -> Rebuild b -> Rebuild a #
Functor Rebuild # |
Instance details Defined in Distribution.Client.RebuildMonad Methods fmap :: (a -> b) -> Rebuild a -> Rebuild b # (<$) :: a -> Rebuild b -> Rebuild a #
Monad Rebuild # |
Instance details Defined in Distribution.Client.RebuildMonad Methods (>>=) :: Rebuild a -> (a -> Rebuild b) -> Rebuild b # (>>) :: Rebuild a -> Rebuild b -> Rebuild b # return :: a -> Rebuild a #

runRebuild :: FilePath -> Rebuild a -> IO a #

Run a `Rebuild` IO action.

execRebuild :: FilePath -> Rebuild a -> IO [MonitorFilePath] #

Run a `Rebuild` IO action.

askRoot :: Rebuild FilePath #

The root that relative paths are interpreted as being relative to.

# Setting up file monitoring

monitorFiles :: [MonitorFilePath] -> Rebuild () #

Use this within the body action of `rerunIfChanged` to declare that the action depends on the given files. This can be based on what the action actually did. It is these files that will be checked for changes next time `rerunIfChanged` is called for that `FileMonitor`.

Relative paths are interpreted as relative to an implicit root, ultimately passed in to `runRebuild`.

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.

#### Instances

Instances details

Structured MonitorFilePath # |
Instance details Defined in Distribution.Client.FileMonitor Methods structure :: Proxy MonitorFilePath -> Structure # structureHash' :: Tagged MonitorFilePath MD5
Binary MonitorFilePath # |
Instance details Defined in Distribution.Client.FileMonitor Methods put :: MonitorFilePath -> Put # get :: Get MonitorFilePath # putList :: [MonitorFilePath] -> Put #
Generic MonitorFilePath # |
Instance details Defined in Distribution.Client.FileMonitor Associated Types
type Rep MonitorFilePath |
Instance details Defined in Distribution.Client.FileMonitor type Rep MonitorFilePath = D1 ('MetaData "MonitorFilePath" "Distribution.Client.FileMonitor" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" '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))))
Methods from :: MonitorFilePath -> Rep MonitorFilePath x # to :: Rep MonitorFilePath x -> MonitorFilePath #
Show MonitorFilePath # |
Instance details Defined in Distribution.Client.FileMonitor Methods showsPrec :: Int -> MonitorFilePath -> ShowS # show :: MonitorFilePath -> String # showList :: [MonitorFilePath] -> ShowS #
Eq MonitorFilePath # |
Instance details Defined in Distribution.Client.FileMonitor Methods (==) :: MonitorFilePath -> MonitorFilePath -> Bool # (/=) :: MonitorFilePath -> MonitorFilePath -> Bool #
type Rep MonitorFilePath # |
Instance details Defined in Distribution.Client.FileMonitor type Rep MonitorFilePath = D1 ('MetaData "MonitorFilePath" "Distribution.Client.FileMonitor" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" '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))))

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.

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.

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.

## Monitoring file globs

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).

data RootedGlob #

A file path specified by globbing, relative to some root directory.

Constructors

RootedGlob |
Fields FilePathRoot what the glob is relative to Glob the glob

#### Instances

Instances details

Parsec RootedGlob # |
Instance details Defined in Distribution.Client.Glob Methods parsec :: CabalParsing m => m RootedGlob #
Pretty RootedGlob # |
Instance details Defined in Distribution.Client.Glob Methods pretty :: RootedGlob -> Doc # prettyVersioned :: CabalSpecVersion -> RootedGlob -> Doc #
Structured RootedGlob # |
Instance details Defined in Distribution.Client.Glob Methods structure :: Proxy RootedGlob -> Structure # structureHash' :: Tagged RootedGlob MD5
Binary RootedGlob # |
Instance details Defined in Distribution.Client.Glob Methods put :: RootedGlob -> Put # get :: Get RootedGlob # putList :: [RootedGlob] -> Put #
Generic RootedGlob # |
Instance details Defined in Distribution.Client.Glob Associated Types
type Rep RootedGlob |
Instance details 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)))
Methods from :: RootedGlob -> Rep RootedGlob x # to :: Rep RootedGlob x -> RootedGlob #
Show RootedGlob # |
Instance details Defined in Distribution.Client.Glob Methods showsPrec :: Int -> RootedGlob -> ShowS # show :: RootedGlob -> String # showList :: [RootedGlob] -> ShowS #
Eq RootedGlob # |
Instance details Defined in Distribution.Client.Glob Methods (==) :: RootedGlob -> RootedGlob -> Bool # (/=) :: RootedGlob -> RootedGlob -> Bool #
type Rep RootedGlob # |
Instance details 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)))

data FilePathRoot #

Constructors

FilePathRelative |
FilePathRoot FilePath | e.g. `"/"`, `"c:"` or result of `takeDrive`
FilePathHomeDir |

#### Instances

Instances details

Parsec FilePathRoot # |
Instance details Defined in Distribution.Client.Glob Methods parsec :: CabalParsing m => m FilePathRoot #
Pretty FilePathRoot # |
Instance details Defined in Distribution.Client.Glob Methods pretty :: FilePathRoot -> Doc # prettyVersioned :: CabalSpecVersion -> FilePathRoot -> Doc #
Structured FilePathRoot # |
Instance details Defined in Distribution.Client.Glob Methods structure :: Proxy FilePathRoot -> Structure # structureHash' :: Tagged FilePathRoot MD5
Binary FilePathRoot # |
Instance details Defined in Distribution.Client.Glob Methods put :: FilePathRoot -> Put # get :: Get FilePathRoot # putList :: [FilePathRoot] -> Put #
Generic FilePathRoot # |
Instance details Defined in Distribution.Client.Glob Associated Types
type Rep FilePathRoot |
Instance details Defined in Distribution.Client.Glob type Rep FilePathRoot = D1 ('MetaData "FilePathRoot" "Distribution.Client.Glob" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" 'False) (C1 ('MetaCons "FilePathRelative" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "FilePathRoot" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 FilePath)) :+: C1 ('MetaCons "FilePathHomeDir" 'PrefixI 'False) (U1 :: Type -> Type)))
Methods from :: FilePathRoot -> Rep FilePathRoot x # to :: Rep FilePathRoot x -> FilePathRoot #
Show FilePathRoot # |
Instance details Defined in Distribution.Client.Glob Methods showsPrec :: Int -> FilePathRoot -> ShowS # show :: FilePathRoot -> String # showList :: [FilePathRoot] -> ShowS #
Eq FilePathRoot # |
Instance details Defined in Distribution.Client.Glob Methods (==) :: FilePathRoot -> FilePathRoot -> Bool # (/=) :: FilePathRoot -> FilePathRoot -> Bool #
type Rep FilePathRoot # |
Instance details Defined in Distribution.Client.Glob type Rep FilePathRoot = D1 ('MetaData "FilePathRoot" "Distribution.Client.Glob" "cabal-install-3.12.1.0-G554PkJj2SpDqyarXp9tbt" 'False) (C1 ('MetaCons "FilePathRelative" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "FilePathRoot" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 FilePath)) :+: C1 ('MetaCons "FilePathHomeDir" 'PrefixI 'False) (U1 :: Type -> Type)))

data Glob #

A filepath specified by globbing.

Constructors

GlobDir !GlobPieces !Glob |
    dirGlob/glob
GlobDirRecursive !GlobPieces | `**/glob`, where `**` denotes recursively traversing all directories and matching filenames on glob.
GlobFile !GlobPieces | A file glob.
GlobDirTrailing | Trailing dir; a glob ending in `/`.

#### Instances

Instances details

Parsec Glob |
Instance details Defined in Distribution.Simple.Glob.Internal Methods parsec :: CabalParsing m => m Glob #
Pretty Glob |
Instance details Defined in Distribution.Simple.Glob.Internal Methods pretty :: Glob -> Doc # prettyVersioned :: CabalSpecVersion -> Glob -> Doc #
Structured Glob |
Instance details Defined in Distribution.Simple.Glob.Internal Methods structure :: Proxy Glob -> Structure # structureHash' :: Tagged Glob MD5
Binary Glob |
Instance details Defined in Distribution.Simple.Glob.Internal Methods put :: Glob -> Put # get :: Get Glob # putList :: [Glob] -> Put #
Generic Glob |
Instance details Defined in Distribution.Simple.Glob.Internal Associated Types
type Rep Glob |
Instance details Defined in Distribution.Simple.Glob.Internal type Rep Glob = D1 ('MetaData "Glob" "Distribution.Simple.Glob.Internal" "Cabal-3.12.0.0-inplace" 'False) ((C1 ('MetaCons "GlobDir" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Glob)) :+: C1 ('MetaCons "GlobDirRecursive" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces))) :+: (C1 ('MetaCons "GlobFile" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces)) :+: C1 ('MetaCons "GlobDirTrailing" 'PrefixI 'False) (U1 :: Type -> Type)))
Methods from :: Glob -> Rep Glob x # to :: Rep Glob x -> Glob #
Show Glob |
Instance details Defined in Distribution.Simple.Glob.Internal Methods showsPrec :: Int -> Glob -> ShowS # show :: Glob -> String # showList :: [Glob] -> ShowS #
Eq Glob |
Instance details Defined in Distribution.Simple.Glob.Internal Methods (==) :: Glob -> Glob -> Bool # (/=) :: Glob -> Glob -> Bool #
type Rep Glob |
Instance details Defined in Distribution.Simple.Glob.Internal type Rep Glob = D1 ('MetaData "Glob" "Distribution.Simple.Glob.Internal" "Cabal-3.12.0.0-inplace" 'False) ((C1 ('MetaCons "GlobDir" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Glob)) :+: C1 ('MetaCons "GlobDirRecursive" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces))) :+: (C1 ('MetaCons "GlobFile" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 GlobPieces)) :+: C1 ('MetaCons "GlobDirTrailing" 'PrefixI 'False) (U1 :: Type -> Type)))

data GlobPiece #

A piece of a globbing pattern

Constructors

WildCard | A wildcard `*`
Literal String | A literal string `dirABC`
Union [GlobPieces] | A union of patterns, e.g. `dir{a,*.txt,c}...`

#### Instances

Instances details

Structured GlobPiece |
Instance details Defined in Distribution.Simple.Glob.Internal Methods structure :: Proxy GlobPiece -> Structure # structureHash' :: Tagged GlobPiece MD5
Binary GlobPiece |
Instance details Defined in Distribution.Simple.Glob.Internal Methods put :: GlobPiece -> Put # get :: Get GlobPiece # putList :: [GlobPiece] -> Put #
Generic GlobPiece |
Instance details Defined in Distribution.Simple.Glob.Internal Associated Types
type Rep GlobPiece |
Instance details Defined in Distribution.Simple.Glob.Internal type Rep GlobPiece = D1 ('MetaData "GlobPiece" "Distribution.Simple.Glob.Internal" "Cabal-3.12.0.0-inplace" 'False) (C1 ('MetaCons "WildCard" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "Literal" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String)) :+: C1 ('MetaCons "Union" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [GlobPieces]))))
Methods from :: GlobPiece -> Rep GlobPiece x # to :: Rep GlobPiece x -> GlobPiece #
Show GlobPiece |
Instance details Defined in Distribution.Simple.Glob.Internal Methods showsPrec :: Int -> GlobPiece -> ShowS # show :: GlobPiece -> String # showList :: [GlobPiece] -> ShowS #
Eq GlobPiece |
Instance details Defined in Distribution.Simple.Glob.Internal Methods (==) :: GlobPiece -> GlobPiece -> Bool # (/=) :: GlobPiece -> GlobPiece -> Bool #
type Rep GlobPiece |
Instance details Defined in Distribution.Simple.Glob.Internal type Rep GlobPiece = D1 ('MetaData "GlobPiece" "Distribution.Simple.Glob.Internal" "Cabal-3.12.0.0-inplace" 'False) (C1 ('MetaCons "WildCard" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "Literal" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String)) :+: C1 ('MetaCons "Union" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [GlobPieces]))))

# Using a file monitor

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 fileMonitorCacheFile :: FilePath The file where this `FileMonitor` should store its state. fileMonitorKeyValid :: a -> a -> Bool Compares 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 :: Bool When 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 #

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.

rerunIfChanged :: (Binary a, Structured a, Binary b, Structured b) => Verbosity -> FileMonitor a b -> a -> Rebuild b -> Rebuild b #

This captures the standard use pattern for a `FileMonitor`: given a monitor, an action and the input value the action depends on, either re-run the action to get its output, or if the value and files the action depends on have not changed then return a previously cached action result.

The result is still in the `Rebuild` monad, so these can be nested.

Do not share `FileMonitor`s between different uses of `rerunIfChanged`.

# Utils

delayInitSharedResource :: IO a -> Rebuild (Rebuild a) #

When using `rerunIfChanged` for each element of a list of actions, it is sometimes the case that each action needs to make use of some resource. e.g.

    sequence
      [ rerunIfChanged verbosity monitor key $ do
          resource <- mkResource
          ... -- use the resource
      | ... ]

For efficiency one would like to share the resource between the actions but the straightforward way of doing this means initialising it every time even when no actions need re-running.

    resource <- mkResource
    sequence
      [ rerunIfChanged verbosity monitor key $ do
          ... -- use the resource
      | ... ]

This utility allows one to get the best of both worlds:

    getResource <- delayInitSharedResource mkResource
    sequence
      [ rerunIfChanged verbosity monitor key $ do
          resource <- getResource
          ... -- use the resource
      | ... ]

delayInitSharedResources :: Ord k => (k -> IO v) -> Rebuild (k -> Rebuild v) #

Much like `delayInitSharedResource` but for a keyed set of resources.

    getResource <- delayInitSharedResource mkResource
    sequence
      [ rerunIfChanged verbosity monitor key $ do
          resource <- getResource key
          ... -- use the resource
      | ... ]

matchFileGlob :: RootedGlob -> Rebuild [FilePath] #

Utility to match a file glob against the file system, starting from a given root directory. The results are all relative to the given root.

Since this operates in the `Rebuild` monad, it also monitors the given glob for changes.

getDirectoryContentsMonitored :: FilePath -> Rebuild [FilePath] #

createDirectoryMonitored :: Bool -> FilePath -> Rebuild () #

monitorDirectoryStatus :: FilePath -> Rebuild Bool #

Monitor a directory as in `monitorDirectory` if it currently exists or as `monitorNonExistentDirectory` if it does not.

doesFileExistMonitored :: FilePath -> Rebuild Bool #

Like `doesFileExist`, but in the `Rebuild` monad. This does NOT track the contents of `FilePath`; use `need` in that case.

need :: FilePath -> Rebuild () #

Monitor a single file

needIfExists :: FilePath -> Rebuild () #

Monitor a file if it exists; otherwise check for when it gets created. This is a bit better for recompilation avoidance because sometimes users give bad package metadata, and we don't want to repeatedly rebuild in this case (which we would if we need'ed a non-existent file).

findFileWithExtensionMonitored :: [Suffix] -> [FilePath] -> FilePath -> Rebuild (Maybe FilePath) #

Like `findFileWithExtension`, but in the `Rebuild` monad.

findFirstFileMonitored :: (a -> FilePath) -> [a] -> Rebuild (Maybe a) #

Like `findFirstFile`, but in the `Rebuild` monad.

findFileMonitored :: [FilePath] -> FilePath -> Rebuild (Maybe FilePath) #

Like `findFile`, but in the `Rebuild` monad.