Cabal-hooks 3.16 → 3.18
raw patch · 4 files changed
+559/−138 lines, 4 filesdep ~Cabaldep ~Cabal-syntaxdep ~basenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: Cabal, Cabal-syntax, base
API changes (from Hackage documentation)
- Distribution.Simple.SetupHooks: [DynamicRuleCommands] :: forall depsArg depsRes arg (deps :: Scope -> Type -> Type -> Type) (ruleCmd :: Scope -> Type -> Type -> Type) (scope :: Scope). If (scope == 'System) (depsArg ~ ByteString, depsRes ~ ByteString, arg ~ ByteString) () => !Static scope (Dict (Binary depsRes, Show depsRes, Eq depsRes)) -> !deps scope depsArg depsRes -> !ruleCmd scope arg (depsRes -> IO ()) -> !If (scope == 'System) SomeTypeRep (TypeRep (depsArg, depsRes, arg)) -> RuleCommands scope deps ruleCmd
- Distribution.Simple.SetupHooks: [StaticRuleCommand] :: forall arg (deps :: Scope -> Type -> Type -> Type) (ruleCmd :: Scope -> Type -> Type -> Type) (scope :: Scope). If (scope == 'System) (arg ~ ByteString) () => !ruleCmd scope arg (IO ()) -> !If (scope == 'System) SomeTypeRep (TypeRep arg) -> RuleCommands scope deps ruleCmd
+ Distribution.Simple.SetupHooks: (<.>) :: FileLike p => p -> String -> p
+ Distribution.Simple.SetupHooks: (</>) :: PathLike p q r => p -> q -> r
+ Distribution.Simple.SetupHooks: DirExists :: MonitorKindDir
+ Distribution.Simple.SetupHooks: DirModTime :: MonitorKindDir
+ Distribution.Simple.SetupHooks: DirNotExists :: MonitorKindDir
+ Distribution.Simple.SetupHooks: EmptyGlob :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: FileExists :: MonitorKindFile
+ Distribution.Simple.SetupHooks: FileHashed :: MonitorKindFile
+ Distribution.Simple.SetupHooks: FileModTime :: MonitorKindFile
+ Distribution.Simple.SetupHooks: FileNotExists :: MonitorKindFile
+ Distribution.Simple.SetupHooks: FilePathHomeDir :: FilePathRoot
+ Distribution.Simple.SetupHooks: FilePathRelative :: FilePathRoot
+ Distribution.Simple.SetupHooks: FilePathRoot :: FilePath -> FilePathRoot
+ Distribution.Simple.SetupHooks: GlobDir :: !GlobPieces -> !Glob -> Glob
+ Distribution.Simple.SetupHooks: GlobDirRecursive :: !GlobPieces -> Glob
+ Distribution.Simple.SetupHooks: GlobDirTrailing :: Glob
+ Distribution.Simple.SetupHooks: GlobFile :: !GlobPieces -> Glob
+ Distribution.Simple.SetupHooks: Literal :: String -> GlobPiece
+ Distribution.Simple.SetupHooks: LiteralFileNameGlobStar :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: MonitorFile :: !MonitorKindFile -> !MonitorKindDir -> !FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: MonitorFileGlob :: !MonitorKindFile -> !MonitorKindDir -> !RootedGlob -> MonitorFilePath
+ Distribution.Simple.SetupHooks: NoExtensionOnStar :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: RootedGlob :: FilePathRoot -> Glob -> RootedGlob
+ Distribution.Simple.SetupHooks: StarInDirectory :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: StarInExtension :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: StarInFileName :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: Union :: [GlobPieces] -> GlobPiece
+ Distribution.Simple.SetupHooks: VersionDoesNotSupportGlob :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: VersionDoesNotSupportGlobStar :: GlobSyntaxError
+ Distribution.Simple.SetupHooks: WildCard :: GlobPiece
+ Distribution.Simple.SetupHooks: [compilerWiredInUnitIds] :: Compiler -> Maybe [(PackageName, UnitId)]
+ Distribution.Simple.SetupHooks: [configBytecodeLib] :: ConfigFlags -> Flag Bool
+ Distribution.Simple.SetupHooks: [monitorKindDir] :: MonitorFilePath -> !MonitorKindDir
+ Distribution.Simple.SetupHooks: [monitorKindFile] :: MonitorFilePath -> !MonitorKindFile
+ Distribution.Simple.SetupHooks: [monitorPathGlob] :: MonitorFilePath -> !RootedGlob
+ Distribution.Simple.SetupHooks: [monitorPath] :: MonitorFilePath -> !FilePath
+ Distribution.Simple.SetupHooks: class Binary t
+ Distribution.Simple.SetupHooks: class FileLike p
+ Distribution.Simple.SetupHooks: class PathLike p q r | q r -> p, p r -> q, p q -> r
+ Distribution.Simple.SetupHooks: componentBuildInfo :: Component -> BuildInfo
+ Distribution.Simple.SetupHooks: createDirectoryIfMissingVerbose :: Verbosity -> Bool -> FilePath -> IO ()
+ Distribution.Simple.SetupHooks: data Build
+ Distribution.Simple.SetupHooks: data CWD
+ Distribution.Simple.SetupHooks: data FilePathRoot
+ Distribution.Simple.SetupHooks: data Glob
+ Distribution.Simple.SetupHooks: data GlobPiece
+ Distribution.Simple.SetupHooks: data GlobSyntaxError
+ Distribution.Simple.SetupHooks: data ModuleName
+ Distribution.Simple.SetupHooks: data MonitorFilePath
+ Distribution.Simple.SetupHooks: data MonitorKindDir
+ Distribution.Simple.SetupHooks: data MonitorKindFile
+ Distribution.Simple.SetupHooks: data Pkg
+ Distribution.Simple.SetupHooks: data RootedGlob
+ Distribution.Simple.SetupHooks: data Source
+ Distribution.Simple.SetupHooks: data VerbosityFlags
+ Distribution.Simple.SetupHooks: data VerbosityHandles
+ Distribution.Simple.SetupHooks: defaultVerbosityHandles :: VerbosityHandles
+ Distribution.Simple.SetupHooks: findAndMonitorDirFileGlob :: forall (m :: Type -> Type) dir. MonadIO m => Maybe (SymbolicPath CWD ('Dir Pkg)) -> Verbosity -> [SymbolicPath Pkg ('Dir dir)] -> Glob -> RulesT m [Location]
+ Distribution.Simple.SetupHooks: findAndMonitorSourceDirsFileExts :: forall (m :: Type -> Type). MonadIO m => PreBuildComponentInputs -> NonEmpty String -> RulesT m [Location]
+ Distribution.Simple.SetupHooks: getSymbolicPath :: forall (allowAbsolute :: AllowAbsolute) from (to :: FileOrDir). SymbolicPathX allowAbsolute from to -> FilePath
+ Distribution.Simple.SetupHooks: globMatches :: [GlobResult a] -> [a]
+ Distribution.Simple.SetupHooks: infixr 5 </>
+ Distribution.Simple.SetupHooks: infixr 7 <.>
+ Distribution.Simple.SetupHooks: interpretSymbolicPathLBI :: forall (allowAbsolute :: AllowAbsolute) (to :: FileOrDir). LocalBuildInfo -> SymbolicPathX allowAbsolute Pkg to -> FilePath
+ Distribution.Simple.SetupHooks: localPkgDescr :: LocalBuildInfo -> PackageDescription
+ Distribution.Simple.SetupHooks: lookupProgram :: Program -> ProgramDb -> Maybe ConfiguredProgram
+ Distribution.Simple.SetupHooks: lookupProgramByName :: String -> ProgramDb -> Maybe ConfiguredProgram
+ Distribution.Simple.SetupHooks: makeRelativePathEx :: forall from (to :: FileOrDir). HasCallStack => FilePath -> RelativePath from to
+ Distribution.Simple.SetupHooks: mbWorkDirLBI :: LocalBuildInfo -> Maybe (SymbolicPath CWD ('Dir Pkg))
+ Distribution.Simple.SetupHooks: mkVerbosity :: VerbosityHandles -> VerbosityFlags -> Verbosity
+ Distribution.Simple.SetupHooks: moduleNameSymbolicPath :: forall (allowAbsolute :: AllowAbsolute). ModuleName -> SymbolicPathX allowAbsolute Source 'File
+ Distribution.Simple.SetupHooks: monitorDirectory :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorDirectoryExistence :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorFile :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorFileExistence :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorFileGlob :: RootedGlob -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorFileGlobExistence :: RootedGlob -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorFileHashed :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorFileHashedSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
+ Distribution.Simple.SetupHooks: monitorFileOrDirectory :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorFileSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]
+ Distribution.Simple.SetupHooks: monitorNonExistentDirectory :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: monitorNonExistentFile :: FilePath -> MonitorFilePath
+ Distribution.Simple.SetupHooks: parseFileGlob :: CabalSpecVersion -> FilePath -> Either GlobSyntaxError Glob
+ Distribution.Simple.SetupHooks: rewriteFileEx :: Verbosity -> FilePath -> String -> IO ()
+ Distribution.Simple.SetupHooks: runDirFileGlob :: Verbosity -> Maybe CabalSpecVersion -> FilePath -> Glob -> IO [GlobResult FilePath]
+ Distribution.Simple.SetupHooks: runProgramCwd :: Verbosity -> Maybe (SymbolicPath CWD ('Dir to)) -> ConfiguredProgram -> [ProgArg] -> IO ()
+ Distribution.Simple.SetupHooks: sameDirectory :: forall (allowAbsolute :: AllowAbsolute) from to. SymbolicPathX allowAbsolute from ('Dir to)
+ Distribution.Simple.SetupHooks: type RelativePath = SymbolicPathX 'OnlyRelative
+ Distribution.Simple.SetupHooks: type ShortText = Text
+ Distribution.Simple.SetupHooks: warn :: Verbosity -> String -> IO ()
+ Distribution.Simple.SetupHooks: withPrograms :: LocalBuildInfo -> ProgramDb
- Distribution.Simple.SetupHooks: Compiler :: CompilerId -> AbiTag -> [CompilerId] -> [(Language, CompilerFlag)] -> [(Extension, Maybe CompilerFlag)] -> Map String String -> Compiler
+ Distribution.Simple.SetupHooks: Compiler :: CompilerId -> AbiTag -> [CompilerId] -> [(Language, CompilerFlag)] -> [(Extension, Maybe CompilerFlag)] -> Maybe [(PackageName, UnitId)] -> Map String String -> Compiler
- Distribution.Simple.SetupHooks: ConfigFlags :: !CommonSetupFlags -> Option' (Last' ProgramDb) -> [(String, FilePath)] -> [(String, [String])] -> NubList FilePath -> Flag CompilerFlavor -> Flag FilePath -> Flag FilePath -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag ProfDetailLevel -> Flag ProfDetailLevel -> [String] -> Flag OptimisationLevel -> Flag PathTemplate -> Flag PathTemplate -> InstallDirs (Flag PathTemplate) -> Flag FilePath -> [SymbolicPath Pkg ('Dir Lib)] -> [SymbolicPath Pkg ('Dir Lib)] -> [SymbolicPath Pkg ('Dir Framework)] -> [SymbolicPath Pkg ('Dir Include)] -> Flag String -> Flag ComponentId -> Flag Bool -> Flag Bool -> [Maybe PackageDB] -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> [PackageVersionConstraint] -> [GivenComponent] -> [PromisedComponent] -> [(ModuleName, Module)] -> FlagAssignment -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag String -> Flag Bool -> Flag DebugInfoLevel -> Flag DumpBuildInfo -> Flag Bool -> Flag Bool -> Flag [UnitId] -> Flag Bool -> ConfigFlags
+ Distribution.Simple.SetupHooks: ConfigFlags :: !CommonSetupFlags -> Maybe (Last ProgramDb) -> [(String, FilePath)] -> [(String, [String])] -> NubList FilePath -> Flag CompilerFlavor -> Flag FilePath -> Flag FilePath -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag ProfDetailLevel -> Flag ProfDetailLevel -> [String] -> Flag OptimisationLevel -> Flag PathTemplate -> Flag PathTemplate -> InstallDirs (Flag PathTemplate) -> Flag FilePath -> [SymbolicPath Pkg ('Dir Lib)] -> [SymbolicPath Pkg ('Dir Lib)] -> [SymbolicPath Pkg ('Dir Framework)] -> [SymbolicPath Pkg ('Dir Include)] -> Flag String -> Flag ComponentId -> Flag Bool -> Flag Bool -> [Maybe PackageDB] -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> [PackageVersionConstraint] -> [GivenComponent] -> [PromisedComponent] -> [(ModuleName, Module)] -> FlagAssignment -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag String -> Flag Bool -> Flag DebugInfoLevel -> Flag DumpBuildInfo -> Flag Bool -> Flag Bool -> Flag [UnitId] -> Flag Bool -> ConfigFlags
- Distribution.Simple.SetupHooks: [configPrograms_] :: ConfigFlags -> Option' (Last' ProgramDb)
+ Distribution.Simple.SetupHooks: [configPrograms_] :: ConfigFlags -> Maybe (Last ProgramDb)
- Distribution.Simple.SetupHooks: buildingWhatVerbosity :: BuildingWhat -> Verbosity
+ Distribution.Simple.SetupHooks: buildingWhatVerbosity :: BuildingWhat -> VerbosityFlags
- Distribution.Simple.SetupHooks: pattern BuildCommonFlags :: Flag Verbosity -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> BuildFlags
+ Distribution.Simple.SetupHooks: pattern BuildCommonFlags :: Flag VerbosityFlags -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> BuildFlags
- Distribution.Simple.SetupHooks: pattern ConfigCommonFlags :: Flag Verbosity -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> ConfigFlags
+ Distribution.Simple.SetupHooks: pattern ConfigCommonFlags :: Flag VerbosityFlags -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> ConfigFlags
- Distribution.Simple.SetupHooks: pattern CopyCommonFlags :: Flag Verbosity -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> CopyFlags
+ Distribution.Simple.SetupHooks: pattern CopyCommonFlags :: Flag VerbosityFlags -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> CopyFlags
- Distribution.Simple.SetupHooks: pattern HaddockCommonFlags :: Flag Verbosity -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> HaddockFlags
+ Distribution.Simple.SetupHooks: pattern HaddockCommonFlags :: Flag VerbosityFlags -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> HaddockFlags
- Distribution.Simple.SetupHooks: pattern HscolourCommonFlags :: Flag Verbosity -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> HscolourFlags
+ Distribution.Simple.SetupHooks: pattern HscolourCommonFlags :: Flag VerbosityFlags -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> HscolourFlags
- Distribution.Simple.SetupHooks: pattern ReplCommonFlags :: Flag Verbosity -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> ReplFlags
+ Distribution.Simple.SetupHooks: pattern ReplCommonFlags :: Flag VerbosityFlags -> Flag (SymbolicPath Pkg ('Dir Dist)) -> Flag (SymbolicPath CWD ('Dir Pkg)) -> Flag (SymbolicPath Pkg 'File) -> [String] -> ReplFlags
Files
- CHANGELOG.md +92/−1
- Cabal-hooks.cabal +68/−68
- LICENSE +34/−34
- src/Distribution/Simple/SetupHooks.hs +365/−35
CHANGELOG.md view
@@ -1,5 +1,97 @@ # Changelog for `Cabal-hooks` +## 3.18.1.0 — July 2026++- Stop exposing constructors of `RuleCommands` [#11461](https://github.com/haskell/cabal/issues/11461) [#11771](https://github.com/haskell/cabal/pull/11771)++ The constructors of the `SetupHooks` `RuleCommands` are no longer exposed+ from `Distribution.Simple.SetupHooks`. These were rather gnarly internal+ constructors; the intended public API is via `staticRule` and `dynamicRule`.++- Make Cabal-hooks library more self-sufficient [#11772](https://github.com/haskell/cabal/pull/11772)++ The `Distribution.Simple.SetupHooks` module from `Cabal-hooks` now re-exports+ a lot of the functionality that is commonly needed when writing `SetupHooks`:++ - File-path related functionality from `Distribution.Utils.Path`.+ - Functionality related to the program database: `lookupProgram`, `runProgramCwd`.+ - IO utilities such as `warn`, `createDirectoryIfMissingVerbose`, and `rewriteFileEx`.+ - Various types frequently used in pre-build rules, such as `Binary`,+ `ModuleName`.+ - Functions that extract information from `LocalBuildInfo` such as+ `localPkgDescr`, `mbWorkDirLBI`, `withPrograms`, `interpretSymbolicPathLBI`+ and `componentBuildInfo`.++ In addition, new file monitoring helper functions `findAndMonitorDirFileGlob`+ and `findAndMonitorSourceDirsFileExts` have been added. These make it very+ simple and convenient to search for a file glob or files with a particular+ extension in the source directories, for pre-build rules.++- Recompilation checking for SetupHooks pre-build rules [#11730](https://github.com/haskell/cabal/issues/11730) [#11731](https://github.com/haskell/cabal/pull/11731)++ Pre-build rules are now only re-run when stale, according to the conditions+ described in the [SetupHooks+ API](https://hackage.haskell.org/package/Cabal-hooks/docs/Distribution-Simple-SetupHooks.html).+ That is, a rule is re-run if any of the following conditions are satisfied:++ - The rule is new, or+ - A dependency of the rule is stale.+ That is, either we have re-run another rule that this rule depends on,+ or one of the file inputs to the rule is newer than the oldest output of the+ rule (or the rule output doesn't exist at all), or+ - The rule itself has changed, e.g. the parameters stored in `RuleData`+ have changed.++ In particular, Cabal will now write per-component caches of pre-build rules+ in order to compute which rules have changed between runs, with file name+ "setup-hooks-rules.cache".++- Pre-build rules can generate extra sources and bundled libraries [#10791](https://github.com/haskell/cabal/issues/10791) [#11607](https://github.com/haskell/cabal/issues/11607) [#11573](https://github.com/haskell/cabal/pull/11573)++ It is now possible to write pre-build rules that generate source files other+ than Haskell files, as well as extra bundled library files.++ Because there is no counterpart to `autogen-modules` for non-Haskell source+ files, you will need to proceed in two steps:++ 1. In a per-component pre-configure hook, add the files you want to generate+ to the relevant fields, e.g. the `cSources` field of `BuildInfo` for a+ C source file.++ These files must be relative to `autogenCompModulesDir`.++ 2. Pre-build rules generating these files will now be demanded. This avoids+ getting an error message "The following pre-build rules are not demanded+ and will not be run".++ Note that include files (such as `.h` files) are a bit different: any files+ listed under `includes`/`autogen-includes` are required at **configure**+ time. This gives `SetupHooks` authors two choices: either list the include+ files in `autogen-includes` but generate them in a pre-configure hook, or+ don't list them there and generate them in a pre-build rule, relying on the+ files getting picked up from included directories (this may be brittle).++- Fixed binary format mismatch resulting in `runRuleDynDepsCmd` build error [#12053](https://github.com/haskell/cabal/issues/12053) [#12054](https://github.com/haskell/cabal/pull/12054)+++ A build containing a `dynamicRule` would fail with the following error:++ ```+ Error: [Cabal-7125]+ Failed to build hooks-0.1.0.0. The exception was:+ Missing ByteString argument in 'ruleExecCmd'.+ Run 'runRuleDynDepsCmd' on the rule to obtain this data.+ ```++ This was caused by a mismatch between the binary formats used to encode+ and decode the rules. The hook produced a result of type `([Dependency],+ LBS.ByteString)`, which was then encoded into a `Binary` blob. However,+ `hooks-exe` decoded this data as `Maybe ([Dependency], LBS.ByteString)`,+ causing the build error.++## 3.16.1 – December 2025+ * No changes+ ## 3.16.0 – July 2025 * No changes @@ -9,4 +101,3 @@ ## 3.14 – November 2024 * Initial release of the `Hooks` API.-
Cabal-hooks.cabal view
@@ -1,68 +1,68 @@-cabal-version: 3.6 -name: Cabal-hooks -version: 3.16 -copyright: 2025, Cabal Development Team -license: BSD-3-Clause -license-file: LICENSE -author: Cabal Development Team <cabal-devel@haskell.org> -maintainer: cabal-devel@haskell.org -homepage: http://www.haskell.org/cabal/ -bug-reports: https://github.com/haskell/cabal/issues -synopsis: API for the Hooks build-type -description: - User-facing API for the Hooks build-type. -category: Distribution -build-type: Simple - -extra-doc-files: - README.md CHANGELOG.md - -source-repository head - type: git - location: https://github.com/haskell/cabal/ - subdir: Cabal-hooks - -library - default-language: Haskell2010 - hs-source-dirs: src - - build-depends: - , Cabal-syntax >= 3.16 && < 3.17 - , Cabal >= 3.16 && < 3.17 - , base >= 4.13 && < 5 - , containers >= 0.5.0.0 && < 0.9 - , transformers >= 0.5.6.0 && < 0.7 - - ghc-options: -Wall -fno-ignore-asserts -Wtabs -Wincomplete-uni-patterns -Wincomplete-record-updates - - exposed-modules: - Distribution.Simple.SetupHooks - - other-extensions: - BangPatterns - CPP - DefaultSignatures - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveTraversable - ExistentialQuantification - FlexibleContexts - FlexibleInstances - GeneralizedNewtypeDeriving - ImplicitParams - KindSignatures - LambdaCase - NondecreasingIndentation - OverloadedStrings - PatternSynonyms - RankNTypes - RecordWildCards - ScopedTypeVariables - StandaloneDeriving - Trustworthy - TypeFamilies - TypeOperators - TypeSynonymInstances - UndecidableInstances +cabal-version: 3.6+name: Cabal-hooks+version: 3.18+copyright: 2003-2026, Cabal Development Team+license: BSD-3-Clause+license-file: LICENSE+author: Cabal Development Team <cabal-devel@haskell.org>+maintainer: cabal-devel@haskell.org+homepage: http://www.haskell.org/cabal/+bug-reports: https://github.com/haskell/cabal/issues+synopsis: API for the Hooks build-type+description:+ User-facing API for the Hooks build-type.+category: Distribution+build-type: Simple++extra-doc-files:+ README.md CHANGELOG.md++source-repository head+ type: git+ location: https://github.com/haskell/cabal/+ subdir: Cabal-hooks++library+ default-language: Haskell2010+ hs-source-dirs: src++ build-depends:+ , Cabal-syntax >= 3.18 && < 3.19+ , Cabal >= 3.18 && < 3.19+ , base >= 4.17 && < 5+ , containers >= 0.5.0.0 && < 0.9+ , transformers >= 0.5.6.0 && < 0.7++ ghc-options: -Wall -fno-ignore-asserts -Wtabs -Wincomplete-uni-patterns -Wincomplete-record-updates++ exposed-modules:+ Distribution.Simple.SetupHooks++ other-extensions:+ BangPatterns+ CPP+ DefaultSignatures+ DeriveDataTypeable+ DeriveFoldable+ DeriveFunctor+ DeriveGeneric+ DeriveTraversable+ ExistentialQuantification+ FlexibleContexts+ FlexibleInstances+ GeneralizedNewtypeDeriving+ ImplicitParams+ KindSignatures+ LambdaCase+ NondecreasingIndentation+ OverloadedStrings+ PatternSynonyms+ RankNTypes+ RecordWildCards+ ScopedTypeVariables+ StandaloneDeriving+ Trustworthy+ TypeFamilies+ TypeOperators+ TypeSynonymInstances+ UndecidableInstances
LICENSE view
@@ -1,34 +1,34 @@-Copyright (c) 2003-2025, Cabal Development Team. -See the AUTHORS file for the full list of copyright holders. - -See */LICENSE for the copyright holders of the subcomponents. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Isaac Jones nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2003-2026, Cabal Development Team.+See the AUTHORS file for the full list of copyright holders.++See */LICENSE for the copyright holders of the subcomponents.++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Isaac Jones nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
src/Distribution/Simple/SetupHooks.hs view
@@ -1,10 +1,14 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE StaticPointers #-} +{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}+ {-| Module: Distribution.Simple.SetupHooks Description: Interface for the @Hooks@ @build-type@.@@ -16,7 +20,8 @@ This is a record that declares actions that should be hooked into the cabal build process. -See 'SetupHooks' for more details.+See also the introductory [how to guide](https://cabal.readthedocs.io/en/latest/how-to-use-setup-hooks.html)+in the Cabal user manual. -} module Distribution.Simple.SetupHooks ( -- * Hooks@@ -25,6 +30,9 @@ SetupHooks(..) , noSetupHooks + -- * Usage overview+ -- $usage+ -- * Configure hooks -- $configureHooks@@ -66,33 +74,77 @@ , Rules , rules , noRules++ -- *** Rules API++ -- $rulesAPI+ , RulesM+ -- | Rule names (use @OverloadedStrings@ or 'Data.String.fromString')+ , ShortText+ , registerRule+ , registerRule_++ -- *** Rule construction , Rule+ , staticRule, dynamicRule++ -- **** Example usage of 'dynamicRule'+ -- $dynamicRules++ -- *** Rule inputs/outputs++ -- **** Rule dependencies+ -- $rulesDeps , Dependency (..) , RuleOutput (..) , RuleId- , staticRule, dynamicRule- -- *** Rule inputs/outputs - -- $rulesDemand+ -- **** Filesystem locations , Location(..) , location , autogenComponentModulesDir , componentBuildDir + -- **** Path types and utilities+ , RelativePath+ , sameDirectory+ , getSymbolicPath+ , makeRelativePathEx+ , moduleNameSymbolicPath+ , FileLike(..)+ , PathLike(..)++ -- ***** Directory types+ , Source, Build, Pkg, CWD++ -- **** File search+ , findAndMonitorDirFileGlob+ , findAndMonitorSourceDirsFileExts++ -- ***** File globbing re-exports+ , Glob(..)+ , GlobPiece(..)+ , GlobSyntaxError(..)+ , parseFileGlob+ , globMatches+ , runDirFileGlob+ -- *** Actions- , RuleCommands(..)+ , RuleCommands -- gnarly constructors not exposed; API is via 'staticRule' and 'dynamicRule' , Command , mkCommand , Dict(..)+ -- | Custom datatypes used as rule command arguments must implement+ -- serialisation. Derive these instances using @DeriveAnyClass@ and+ -- @DeriveGeneric@:+ --+ -- > data MyInput = MyInput { .. }+ -- > deriving stock ( Eq, Show, Generic )+ -- > deriving anyclass Binary+ , Binary - -- *** Rules API - -- $rulesAPI- , RulesM- , registerRule- , registerRule_-- -- **** File/directory monitoring+ -- *** File/directory monitoring , addRuleMonitors , module Distribution.Simple.FileMonitor.Types @@ -125,11 +177,23 @@ , ProgramDb , addKnownPrograms , configureUnconfiguredProgram+ , lookupProgram+ , lookupProgramByName , simpleProgram+ , runProgramCwd + -- *** IO utilities+ , warn+ , createDirectoryIfMissingVerbose+ , rewriteFileEx+ -- ** General @Cabal@ datatypes- , Verbosity, Compiler(..), Platform(..), Suffix(..)+ , Compiler(..), Platform(..), Suffix(..) + -- *** Verbosity+ , Verbosity, VerbosityFlags, VerbosityHandles+ , mkVerbosity, defaultVerbosityHandles+ -- *** Package information , LocalBuildConfig, LocalBuildInfo, PackageBuildDescr -- NB: we can't simply re-export all the fields of LocalBuildConfig etc,@@ -139,8 +203,16 @@ , PackageDescription(..) + -- **** LocalBuildInfo utilities+ , localPkgDescr+ , mbWorkDirLBI+ , withPrograms+ , interpretSymbolicPathLBI+ , componentBuildInfo+ -- *** Component information , Component(..), ComponentName(..), componentName+ , ModuleName , BuildInfo(..), emptyBuildInfo , TargetInfo(..), ComponentLocalBuildInfo(..) @@ -153,6 +225,10 @@ ) where+import Distribution.Compat.Binary+ ( Binary )+import Distribution.ModuleName+ ( ModuleName ) import Distribution.PackageDescription ( PackageDescription(..) , Library(..), ForeignLib(..)@@ -167,17 +243,31 @@ import Distribution.Simple.Compiler ( Compiler(..) ) import Distribution.Simple.Errors- ( CabalException(SetupHooksException) )+ ( CabalException(SetupHooksException)+ ) import Distribution.Simple.FileMonitor.Types+ hiding ( Glob )+import Distribution.Simple.Glob+ ( Glob, GlobSyntaxError(..)+ , globMatches, runDirFileGlob, parseFileGlob+ )+import Distribution.Simple.Glob.Internal+ ( Glob(..), GlobPiece(..)+ ) import Distribution.Simple.Install ( installFileGlob ) import Distribution.Simple.LocalBuildInfo- ( componentBuildDir )+ ( componentBuildDir, componentBuildInfo+ , mbWorkDirLBI, interpretSymbolicPathLBI+ ) import Distribution.Simple.PreProcess.Types ( Suffix(..) )+import Distribution.Simple.Program+ ( runProgramCwd ) import Distribution.Simple.Program.Db ( ProgramDb, addKnownPrograms , configureUnconfiguredProgram+ , lookupProgram, lookupProgramByName ) import Distribution.Simple.Program.Find ( simpleProgram )@@ -198,7 +288,11 @@ import Distribution.Simple.SetupHooks.Internal import Distribution.Simple.SetupHooks.Rule as Rule import Distribution.Simple.Utils- ( dieWithException )+ ( createDirectoryIfMissingVerbose+ , dieWithException+ , rewriteFileEx+ , warn+ ) import Distribution.System ( Platform(..) ) import Distribution.Types.Component@@ -206,15 +300,25 @@ import Distribution.Types.ComponentLocalBuildInfo ( ComponentLocalBuildInfo(..) ) import Distribution.Types.LocalBuildInfo- ( LocalBuildInfo(..) )+ ( LocalBuildInfo(..), withPrograms ) import Distribution.Types.LocalBuildConfig ( LocalBuildConfig, PackageBuildDescr ) import Distribution.Types.TargetInfo ( TargetInfo(..) )+import Distribution.Utils.Path+ ( SymbolicPath, CWD, Pkg, FileOrDir(..)+ , interpretSymbolicPath, makeRelativePathEx+ , RelativePath, Source, Build+ , getSymbolicPath, sameDirectory, moduleNameSymbolicPath+ , FileLike(..), PathLike(..)+ ) import Distribution.Utils.ShortText ( ShortText ) import Distribution.Verbosity- ( Verbosity )+ ( Verbosity+ , VerbosityFlags, VerbosityHandles+ , mkVerbosity, defaultVerbosityHandles+ ) import Control.Monad ( void )@@ -224,13 +328,12 @@ ( lift ) import qualified Control.Monad.Trans.Reader as Reader import qualified Control.Monad.Trans.State as State-#if MIN_VERSION_transformers(0,5,6) import qualified Control.Monad.Trans.Writer.CPS as Writer-#else-import qualified Control.Monad.Trans.Writer.Strict as Writer-#endif+import qualified Data.List.NonEmpty as NE import Data.Foldable ( for_ )+import Data.Traversable+ ( for ) import Data.Map.Strict as Map ( insertLookupWithKey ) @@ -241,7 +344,7 @@ A Cabal package with @Hooks@ @build-type@ must define the Haskell module @SetupHooks@ which defines a value @setupHooks :: 'SetupHooks'@. -These *setup hooks* allow package authors to customise the configuration and+These __setup hooks__ allow package authors to customise the configuration and building of a package by providing certain hooks that get folded into the general package configuration and building logic within @Cabal@. @@ -283,6 +386,65 @@ > mySetupHooks = ... -} +{- $usage+'SetupHooks' allow hooks into the following phases:++ * The configure phase, via 'ConfigureHooks'.++ There are three hooks into the configure phase:++ * Package-wide pre-configure hook.++ This hook enables custom logic in the style of traditional @./configure@+ scripts, e.g. finding out information about the system and configuring+ dependencies.++ * Package-wide post-configure hook.++ This hook is mostly used to write write custom package-wide information+ to disk so that the next step can read it without repeating work.++ * Per-component pre-configure hooks.++ These hooks are used to modify individual components, e.g. declaring+ new modules (such as autogenerated modules whose module names are not+ statically known) or specifying per-component flags to be used when+ building each component.++ You can think of this step as dynamically updating the stanza for a+ single component in the .cabal file of a package.++ * The build phase, via 'BuildHooks'.++ There are two hooks into the build phase:++ * Per-component pre-build rules.++ A rule can be thought of an invocation of a code generator; each rule+ specifies how to build a particular output.++ You can have rules that don't depend on any inputs (e.g. directly+ generate a collection of modules programmatically, perhaps from some+ kind of parsed schema), as well as preprocessor-like rules that take in+ input files, e.g. the Happy parser generator that takes in a .y+ file and generates a corresponding .hs file.++ This rather elaborate setup (compared to a one-shot program that+ generates the required modules) allows proper recompilation checking.+ See also <https://well-typed.com/blog/2025/01/cabal-hooks/#pre-build-rules>+ for some worked examples of pre-build rules.++ * Per-component post-build hooks.++ These can be thought of as "pre-linking hooks" and allow injecting+ additional data into the final executable.++ * The install phase, via 'InstallHooks'.++ There is a single, per-component install hook. This allows copying over+ additional files when installing a component (library/executable).+-}+ {- $configureHooks Configure hooks can be used to augment the Cabal configure logic with package-specific logic. The main principle is that the configure hooks can@@ -318,18 +480,24 @@ - a specification of its static dependencies and outputs, - the commands that execute the rule. +You can think of a t'Rule' as describing a particular invocation of a code+generator or preprocessor, with the t'Command' specifying the particular+command that will be executed.+ Rules are constructed using either one of the 'staticRule' or 'dynamicRule' smart constructors. Directly constructing a t'Rule' using the constructors of that data type is not advised, as this relies on internal implementation details-which are subject to change in between versions of the `Cabal-hooks` library.+which are subject to change in between versions of the @Cabal-hooks@ library. Note that: - To declare the dependency on the output of a rule, one must refer to the rule directly, and not to the path to the output executing that rule will eventually produce.- To do so, registering a t'Rule' with the API returns a unique identifier- for that rule, in the form of a t'RuleId'.++ This is achieved by using the t'RuleId' returned by 'registerRule', which+ is the unique identifier for that rule.+ - File dependencies and outputs are not specified directly by 'FilePath', but rather use the 'Location' type (which is more convenient when working with preprocessors).@@ -339,11 +507,96 @@ when to re-compute the entire set of rules. -} -{- $rulesDemand+{- $dynamicRules+The 'dynamicRule' smart constructor allows specifying rules that have dynamic+dependencies. This is the most complex part of the API, so let's work through+a representative example.++Suppose for example that we have preprocessor for files that may depend on+each other via explicit import statements at the start of the file. To properly+preprocess these, we need two commands:++ 1. A "find dependencies" command that parses the header to find the dependencies.+ 2. A "run preprocessor" command. This command might need to be passed the+ dependencies as arguments, so the command in (1) should make that information+ available to (2).++This is exactly what 'dynamicRule' allows us to do. The first command is (1),+which returns dynamic dependencies and additional data passed to the second+command, (2).++The rules for such a preprocessor would thus look something like (sketch):++@+ppDynPreBuildRules :: PreBuildComponentInputs -> RulesM ()+ppDynPreBuildRules pbci = mdo -- NB: using RecursiveDo++ -- Scan filesystem for files to preprocess, e.g. with a monitored file glob.+ inputModNms <- ...++ let++ -- (1): the "find dependencies" action+ computeDepsAction+ :: (Map ModuleName RuleId, FilePath)+ -> IO ([Dependency], [ModuleName])+ computeDepsAction (modNmToRuleId, inFile) = do+ src <- readFile inFile+ let dynDeps = parseHeaderImports src+ return+ ( [ RuleDependency $ RuleOutput rId 1+ | dep <- dynDeps+ , let rId = modNmToRuleId Map.! dep ]+ , dynDeps )++ -- (2): the "run preprocessor" action+ runPPAction :: (FilePath, FilePath) -> [ModuleName] -> IO ()+ runPPAction (inFile, outFile) dynDeps = do+ src <- readFile inFile+ let ppResult = preprocessWithDependencies dynDeps src+ writeFile outFile ppResult++ -- Construct a rule with dynamic dependencies using 'dynamicRule'+ mkRule modNm =+ dynamicRule (static Dict)+ (mkCommand (static Dict) (static computeDepsAction) (allRuleIDs, inFile))+ (mkCommand (static Dict) (static runPPAction) (inFile, outFile))+ [ FileDependency $ Location sameDirectory (modPath <.> "myPp") ]+ ( Location autogenDir (modPath <.> "hs" )+ NE.:| [ Location buildDir (unsafeCoerceSymbolicPath modPath <.> "myPpIface") ] )+ where+ modPath = moduleNameSymbolicPath modNm+ inFile = getSymbolicPath (sameDirectory </> modPath <.> "myPp")+ outFile = getSymbolicPath (autogenDir </> modPath <.> "hs")++ autogenDir = ... -- derived from pbci+ buildDir = ... -- derived from pbci++ registerOne :: ModuleName -> RulesM (ModuleName, RuleId)+ registerOne modNm = do+ rId <- registerRule ("MyPP: " <> show modNm) (mkRule modNm)+ return (modNm, rId)++ -- Return a map from ModuleName to RuleId (used above via RecursiveDo).+ allRuleIDs <- Map.fromList <$> traverse registerOne inputModNms+ return ()+@++Note the usage of @RecursiveDo@ to allow indexing into the set of all registered+rules in order to declare the dynamic dependencies of the rules.++In this example we use tuples for the command arguments and @[ModuleName]@ for+the result of the dynamic dependency command; these have the required instances+needed for serialisation. If you use custom datatypes for these, you will need+to derive @Binary@, @Show@, @Eq@ to satisfy the API requirements (enforced by+the various calls to @static Dict@).+-}++{- $rulesDeps Rules can declare various kinds of dependencies: - - 'staticDependencies': files or other rules that a rule statically depends on,- - extra dynamic dependencies, using the 'DynamicRuleCommands' constructor,+ - static dependencies: files or other rules that a rule statically depends on,+ - extra dynamic dependencies, using 'dynamicRule' smart constructor, - 'MonitorFilePath': additional files and directories to monitor. Rules are considered __out-of-date__ precisely when any of the following@@ -363,8 +616,8 @@ [N] the rule is new, or [S] the rule matches with an old rule, and either: - [S1] a file dependency of the rule has been modified/created/deleted, or- a (transitive) rule dependency of the rule is itself stale, or+ [S1] a file dependency of the rule has been modified\/created\/deleted,+ or a (transitive) rule dependency of the rule is itself stale, or [S2] the rule is different from the old rule, e.g. the argument stored in the rule command has changed, or the pointer to the action to run the rule has changed. (This is determined using the @Eq Rule@ instance.)@@ -377,6 +630,30 @@ 1. Any time the rules are out-of-date, query the rules to obtain up-to-date rules. 2. Re-run stale rules.++Cabal will execute all **demanded** rules in dependency order. A rule is+demanded if it satisfies one of the following conditions:++ 1. It is a dependency of another demanded rule.+ 2. The rule generates a Haskell file declared in the autogen-modules field.+ In this case, the rule **must** place the generated source file in the+ 'autogenComponentModulesDir' appropriate for the component.+ 3. (Since Cabal 3.18 only) The rule generates a non-Haskell source file, such+ as a C or JavaScript source. In this case (because there is no+ "autogen-c-sources" field), the following steps must be taken:+ a. Add the file to the 'c-sources' (or 'js-sources', etc) field of the+ package description in a per-component pre-configure hook, declaring it+ in the same 'autogenComponentModulesDir' directory (as if it was a @.hs@ file).+ b. Add a pre-build rule that generates the source file and puts it in+ this same 'autogenComponentModulesDir' directory.+ Note that any file declared in the 'includes'/'autogen-includes' fields+ must be present at **configure** time, so cannot be generated in a+ pre-build rule. In that case, either use a pre-configure hook or don't+ declare it under the 'includes' field (if possible).+ 4. (Since Cabal 3.18 only) The rule generates an extra bundled library file,+ placed in the 'autogenComponentModulesDir' and with a filename matching+ one of the libraries named in the 'extra-bundled-libraries' field (ignoring+ file extension). -} {- $rulesAPI@@ -402,7 +679,8 @@ We use 'addRuleMonitors' to declare a monitored directory that the collection of rules as a whole depends on. In this case, we declare that they depend on the contents of the "searchDir" directory. This means that the rules will be-computed anew whenever the contents of this directory change.+computed anew whenever the contents of this directory change. (This does not+mean all the rules will be re-run; only the out-of-date rules will be re-run.) -} {- Note [Not hiding SetupHooks constructors]@@ -428,7 +706,7 @@ registerRule nm !newRule = RulesT $ do RulesEnv { rulesEnvNameSpace = ns , rulesEnvVerbosity = verbosity } <- Reader.ask- oldRules <- lift $ State.get+ oldRules <- lift State.get let rId = RuleId { ruleNameSpace = ns, ruleName = nm } (mbDup, newRules) = Map.insertLookupWithKey (\ _ new _old -> new) rId newRule oldRules for_ mbDup $ \ oldRule ->@@ -454,9 +732,61 @@ -- | Declare additional monitored objects for the collection of all rules. -- -- When these monitored objects change, the rules are re-computed.+--+-- See also 'findAndMonitorDirFileGlob' which combines the search and the+-- monitoring. addRuleMonitors :: Monad m => [MonitorFilePath] -> RulesT m () addRuleMonitors = RulesT . lift . lift . Writer.tell {-# INLINEABLE addRuleMonitors #-} --- TODO: add API functions that search and declare the appropriate monitoring--- at the same time.+-- | Retrieve all files matching the given 'Glob' in the specified search+-- directories.+--+-- See also the canned 'findAndMonitorSourceDirsFileExts' for the simple+-- case of monitoring a file extension in the source directories of a component.+findAndMonitorDirFileGlob+ :: MonadIO m+ => Maybe (SymbolicPath CWD (Dir Pkg))+ -> Verbosity+ -> [SymbolicPath Pkg (Dir dir)]+ -- ^ search directories+ -> Glob+ -- ^ pattern to match against+ -> RulesT m [Location]+findAndMonitorDirFileGlob mbWorkDir verb searchDirs glob = do+ matchingFiles <- fmap concat $ liftIO $ for searchDirs $ \srcDir -> do+ let root = interpretSymbolicPath mbWorkDir srcDir+ matches <- runDirFileGlob verb Nothing root glob+ return+ [ Location srcDir (makeRelativePathEx match)+ | match <- globMatches matches+ ]+ addRuleMonitors [monitorFileGlobExistence $ RootedGlob FilePathRelative glob]+ return matchingFiles+{-# INLINEABLE findAndMonitorDirFileGlob #-}++-- | Scans the component source directories for files with the given extensions,+-- and monitors the resulting file glob.+findAndMonitorSourceDirsFileExts+ :: MonadIO m+ => PreBuildComponentInputs+ -> NE.NonEmpty String -- ^ extensions (not including the @.@)+ -> RulesT m [Location]+findAndMonitorSourceDirsFileExts+ PreBuildComponentInputs+ { localBuildInfo+ , buildingWhat = what+ , targetInfo+ } exts =+ let+ comp = targetComponent targetInfo+ verbosity = mkVerbosity defaultVerbosityHandles (buildingWhatVerbosity what)+ mbWorkDir = mbWorkDirLBI localBuildInfo+ searchDirs = hsSourceDirs $ componentBuildInfo comp+ fileGlob = case exts of+ e NE.:| [] -> [WildCard, Literal ("." ++ e)]+ e NE.:| es -> [WildCard, Literal ".", Union (map (\ext -> [Literal ext]) (e : es))]+ glob = GlobDirRecursive fileGlob+ in+ findAndMonitorDirFileGlob mbWorkDir verbosity searchDirs glob+{-# INLINEABLE findAndMonitorSourceDirsFileExts #-}