Shake+ Extended - Experimental Mechanisms For Shake
This library extends
shake-plus, which enriches
shake with ReaderT and the
Path library. This extended ruleset introduces
within, for better keeping track
of source and output directories, as well as batch loading mechanisms using
ixset-typed.
Using Within
One common complaint about Shake is having to keep track of source and output
directories and translating FilePaths when using the input to an Action,
leading to lots of repetition of the form (sourceFolder </>) . (-<.> ".ext") . dropDirectory1 which is prone to breakage. Using Path helps this to some
degree, but in some cases is even more annoying because lots of Path
functions use MonadThrow, leading to lots of monadic steps inside an
RAction.
To alleviate this somewhat, we use Within b (Path Rel File) as a standard
pattern for representing a file within a directory. Within is a type
available in the within package
that is simply a newtype wrapper over an Env comonad with the environment
specialized to Path b Dir. We provide variants of the file operations and
rules that typically accept or return Paths or contain callbacks that expect
paths and change these to Within values. These functions are generally
suffixed within. Here is the variant of getDirectoryFiles that
produces Within values.
getDirectoryFilesWithin' :: MonadAction m => Within Rel [FilePattern] -> m [Within b (Path Rel File)]
You can convert to and from this within-style using within and fromWithin.
let x = $(mkRelFile "a.txt") `within` $(mkRelDir "foo") -- Within Rel (Path Rel File)
fromWithin x -- produces a `Path Rel File`
and you can assert that an existing path lies in a directory by using asWithin, which throws
if the directory is not a proper prefix of the Path.
$(mkRelFile "foo/a.txt") `asWithin` $(mkRelDir "foo") -- fine
$(mkRelFile "a.txt") `asWithin` $(mkRelDir "foo") -- throws error
Filerules such as (%>) have within-style variants that accept an (Path b Dir) FilePattern on the left and carry that env to the callback.
(%^>) :: (Partial, MonadReader r m, MonadRules m) => Within Rel FilePattern -> (Within Rel (Path Rel File) -> RAction r ()) -> m ()
You change the underlying filepath with fmap or mapM, whilst you can move
to a new parent directory by using localDir, or localDirM which is defined
in the Within library for when the map between parent directories may throw.
The Within library also contains more functions and instances for more
precise changes between output and source directories.