1 {-# LANGUAGE BangPatterns #-}
3 {-# LANGUAGE DerivingStrategies #-}
4 {-# LANGUAGE DuplicateRecordFields #-}
5 {-# LANGUAGE RankNTypes #-}
6 {-# LANGUAGE StaticPointers #-}
9 Module: Distribution.Simple.SetupHooks
10 Description: Interface for the @Hooks@ @build-type@.
12 This module defines the interface for the @Hooks@ @build-type@.
14 To write a package that implements @build-type: Hooks@, you should define
15 a module @SetupHooks.hs@ which exports a value @setupHooks :: 'SetupHooks'@.
16 This is a record that declares actions that should be hooked into the
19 See 'SetupHooks' for more details.
21 module Distribution
.Simple
.SetupHooks
33 -- ** Per-package configure hooks
34 , PreConfPackageInputs
(..)
35 , PreConfPackageOutputs
(..) -- See Note [Not hiding SetupHooks constructors]
36 , noPreConfPackageOutputs
38 , PostConfPackageInputs
(..)
40 -- ** Per-component configure hooks
41 , PreConfComponentInputs
(..)
42 , PreConfComponentOutputs
(..) -- See Note [Not hiding SetupHooks constructors]
43 , noPreConfComponentOutputs
44 , PreConfComponentHook
45 , ComponentDiff
(..), emptyComponentDiff
, buildInfoComponentDiff
46 , LibraryDiff
, ForeignLibDiff
, ExecutableDiff
47 , TestSuiteDiff
, BenchmarkDiff
52 , BuildHooks
(..), noBuildHooks
53 , BuildingWhat
(..), buildingWhatVerbosity
, buildingWhatDistPref
58 , PreBuildComponentInputs
(..)
59 , PreBuildComponentRules
61 -- ** Post-build hooks
62 , PostBuildComponentInputs
(..)
63 , PostBuildComponentHook
73 , staticRule
, dynamicRule
74 -- *** Rule inputs/outputs
79 , autogenComponentModulesDir
95 -- **** File/directory monitoring
97 , module Distribution
.Simple
.FileMonitor
.Types
100 , InstallHooks
(..), noInstallHooks
101 , InstallComponentInputs
(..), InstallComponentHook
106 -- *** Configure hooks
109 , BuildFlags
(..), ReplFlags
(..), HaddockFlags
(..), HscolourFlags
(..)
115 -- | These are functions provided as part of the @Hooks@ API.
116 -- It is recommended to import them from this module as opposed to
117 -- manually importing them from inside the Cabal module hierarchy.
119 -- *** Copy/install functions
122 -- *** Interacting with the program database
123 , Program
(..), ConfiguredProgram
(..), ProgArg
124 , ProgramLocation
(..)
127 , configureUnconfiguredProgram
130 -- ** General @Cabal@ datatypes
131 , Verbosity
, Compiler
(..), Platform
(..), Suffix
(..)
133 -- *** Package information
134 , LocalBuildConfig
, LocalBuildInfo
, PackageBuildDescr
135 -- NB: we can't simply re-export all the fields of LocalBuildConfig etc,
136 -- due to the presence of duplicate record fields.
137 -- Ideally, we'd like to e.g. re-export LocalBuildConfig qualified,
138 -- but qualified re-exports aren't a thing currently.
140 , PackageDescription
(..)
142 -- *** Component information
143 , Component
(..), ComponentName
(..), componentName
144 , BuildInfo
(..), emptyBuildInfo
145 , TargetInfo
(..), ComponentLocalBuildInfo
(..)
148 , Library
(..), ForeignLib
(..), Executable
(..)
149 , TestSuite
(..), Benchmark
(..)
151 , emptyLibrary
, emptyForeignLib
, emptyExecutable
152 , emptyTestSuite
, emptyBenchmark
156 import Distribution
.PackageDescription
157 ( PackageDescription
(..)
158 , Library
(..), ForeignLib
(..)
159 , Executable
(..), TestSuite
(..), Benchmark
(..)
160 , emptyLibrary
, emptyForeignLib
161 , emptyExecutable
, emptyBenchmark
, emptyTestSuite
162 , BuildInfo
(..), emptyBuildInfo
163 , ComponentName
(..), LibraryName
(..)
165 import Distribution
.Simple
.BuildPaths
166 ( autogenComponentModulesDir
)
167 import Distribution
.Simple
.Compiler
169 import Distribution
.Simple
.Errors
170 ( CabalException
(SetupHooksException
) )
171 import Distribution
.Simple
.FileMonitor
.Types
172 import Distribution
.Simple
.Install
174 import Distribution
.Simple
.LocalBuildInfo
175 ( componentBuildDir
)
176 import Distribution
.Simple
.PreProcess
.Types
178 import Distribution
.Simple
.Program
.Db
179 ( ProgramDb
, addKnownPrograms
180 , configureUnconfiguredProgram
182 import Distribution
.Simple
.Program
.Find
184 import Distribution
.Simple
.Program
.Types
185 ( Program
(..), ConfiguredProgram
(..)
187 , ProgramLocation
(..)
189 import Distribution
.Simple
.Setup
197 import Distribution
.Simple
.SetupHooks
.Errors
198 import Distribution
.Simple
.SetupHooks
.Internal
199 import Distribution
.Simple
.SetupHooks
.Rule
as Rule
200 import Distribution
.Simple
.Utils
202 import Distribution
.System
204 import Distribution
.Types
.Component
205 ( Component
(..), componentName
)
206 import Distribution
.Types
.ComponentLocalBuildInfo
207 ( ComponentLocalBuildInfo
(..) )
208 import Distribution
.Types
.LocalBuildInfo
209 ( LocalBuildInfo
(..) )
210 import Distribution
.Types
.LocalBuildConfig
211 ( LocalBuildConfig
, PackageBuildDescr
)
212 import Distribution
.Types
.TargetInfo
214 import Distribution
.Utils
.ShortText
216 import Distribution
.Verbosity
221 import Control
.Monad
.IO.Class
223 import Control
.Monad
.Trans
.Class
225 import qualified Control
.Monad
.Trans
.Reader
as Reader
226 import qualified Control
.Monad
.Trans
.State
as State
227 #if MIN_VERSION_transformers
(0,5,6)
228 import qualified Control
.Monad
.Trans
.Writer
.CPS
as Writer
230 import qualified Control
.Monad
.Trans
.Writer
.Strict
as Writer
234 import Data
.Map
.Strict
as Map
235 ( insertLookupWithKey
)
237 --------------------------------------------------------------------------------
238 -- Haddocks for the SetupHooks API
241 A Cabal package with @Hooks@ @build-type@ must define the Haskell module
242 @SetupHooks@ which defines a value @setupHooks :: 'SetupHooks'@.
244 These *setup hooks* allow package authors to customise the configuration and
245 building of a package by providing certain hooks that get folded into the
246 general package configuration and building logic within @Cabal@.
248 This mechanism replaces the @Custom@ @build-type@, providing better
249 integration with the rest of the Haskell ecosystem.
253 > -- In your .cabal file
258 > base >= 4.18 && < 5,
259 > Cabal-hooks >= 3.14 && < 3.15
261 > The declared Cabal version should also be at least 3.14.
263 > -- In SetupHooks.hs, next to your .cabal file
264 > module SetupHooks where
265 > import Distribution.Simple.SetupHooks ( SetupHooks, noSetupHooks )
267 > setupHooks :: SetupHooks
270 > { configureHooks = myConfigureHooks
271 > , buildHooks = myBuildHooks }
273 Note that 'SetupHooks' can be monoidally combined, e.g.:
275 > module SetupHooks where
276 > import Distribution.Simple.SetupHooks
277 > import qualified SomeOtherLibrary ( setupHooks )
279 > setupHooks :: SetupHooks
280 > setupHooks = SomeOtherLibrary.setupHooks <> mySetupHooks
282 > mySetupHooks :: SetupHooks
287 Configure hooks can be used to augment the Cabal configure logic with
288 package-specific logic. The main principle is that the configure hooks can
289 feed into updating the 'PackageDescription' of a @cabal@ package. From then on,
290 this package configuration is set in stone, and later hooks (e.g. hooks into
291 the build phase) can no longer modify this configuration; instead they will
292 receive this configuration in their inputs, and must honour it.
294 Configuration happens at two levels:
296 * global configuration covers the entire package,
297 * local configuration covers a single component.
299 Once the global package configuration is done, all hooks work on a
300 per-component level. The configuration hooks thus follow a simple philosophy:
302 * All modifications to global package options must use `preConfPackageHook`.
303 * All modifications to component configuration options must use `preConfComponentHook`.
305 For example, to generate modules inside a given component, you should:
307 * In the per-component configure hook, declare the modules you are going to
308 generate by adding them to the `autogenModules` field for that component
309 (unless you know them ahead of time, in which case they can be listed
310 textually in the @.cabal@ file of the project).
311 * In the build hooks, describe the actions that will generate these modules.
315 Pre-build hooks are specified as a collection of pre-build 'Rules'.
316 Each t'Rule' consists of:
318 - a specification of its static dependencies and outputs,
319 - the commands that execute the rule.
321 Rules are constructed using either one of the 'staticRule' or 'dynamicRule'
322 smart constructors. Directly constructing a t'Rule' using the constructors of
323 that data type is not advised, as this relies on internal implementation details
324 which are subject to change in between versions of the `Cabal-hooks` library.
328 - To declare the dependency on the output of a rule, one must refer to the
329 rule directly, and not to the path to the output executing that rule will
331 To do so, registering a t'Rule' with the API returns a unique identifier
332 for that rule, in the form of a t'RuleId'.
333 - File dependencies and outputs are not specified directly by
334 'FilePath', but rather use the 'Location' type (which is more convenient
335 when working with preprocessors).
336 - Rules refer to the actions that execute them using static pointers, in order
337 to enable serialisation/deserialisation of rules.
338 - Rules can additionally monitor files or directories, which determines
339 when to re-compute the entire set of rules.
343 Rules can declare various kinds of dependencies:
345 - 'staticDependencies': files or other rules that a rule statically depends on,
346 - extra dynamic dependencies, using the 'DynamicRuleCommands' constructor,
347 - 'MonitorFilePath': additional files and directories to monitor.
349 Rules are considered __out-of-date__ precisely when any of the following
352 [O1] there has been a (relevant) change in the files and directories
353 monitored by the rules,
354 [O2] the environment passed to the computation of rules has changed.
356 If the rules are out-of-date, the build system is expected to re-run the
357 computation that computes all rules.
359 After this re-computation of the set of all rules, we match up new rules
360 with old rules, by 'RuleId'. A rule is then considered __stale__ if any of
361 following conditions apply:
363 [N] the rule is new, or
364 [S] the rule matches with an old rule, and either:
366 [S1] a file dependency of the rule has been modified/created/deleted, or
367 a (transitive) rule dependency of the rule is itself stale, or
368 [S2] the rule is different from the old rule, e.g. the argument stored in
369 the rule command has changed, or the pointer to the action to run the
370 rule has changed. (This is determined using the @Eq Rule@ instance.)
372 A stale rule becomes no longer stale once we run its associated action. The
373 build system is responsible for re-running the actions associated with
374 each stale rule, in dependency order. This means the build system is expected
375 to behave as follows:
377 1. Any time the rules are out-of-date, query the rules to obtain
379 2. Re-run stale rules.
383 Defining pre-build rules can be done in the following style:
385 > {-# LANGUAGE BlockArguments, StaticPointers #-}
386 > myPreBuildRules
:: PreBuildComponentRules
387 > myPreBuildRules
= rules
(static
()) $ \ preBuildEnvironment
-> do
388 > let cmd1
= mkCommand
(static Dict
) $ static
\ arg
-> do { .. }
389 > cmd2
= mkCommand
(static Dict
) $ static
\ arg
-> do { .. }
390 > myData
<- liftIO someIOAction
391 > addRuleMonitors
[ monitorDirectory
"someSearchDir" ]
392 > registerRule_
"rule_1_1" $ staticRule
(cmd1 arg1
) deps1 outs1
393 > registerRule_
"rule_1_2" $ staticRule
(cmd1 arg2
) deps2 outs2
394 > registerRule_
"rule_1_3" $ staticRule
(cmd1 arg3
) deps3 outs3
395 > registerRule_
"rule_2_4" $ staticRule
(cmd2 arg4
) deps4 outs4
397 Here we use the
'rules
', 'staticRule
' and 'mkCommand
' smart constructors
,
398 rather than directly using the v
'Rules
', v
'Rule
' and v
'Command
' constructors
,
399 which insulates us from internal changes to the t
'Rules
', t
'Rule
' and t
'Command
'
400 datatypes
, respectively
.
402 We use
'addRuleMonitors
' to declare a monitored directory that the collection
403 of rules
as a whole depends on
. In this
case, we declare that they depend on the
404 contents
of the
"searchDir" directory
. This means that the rules will be
405 computed anew whenever the contents
of this directory change
.
408 {- Note [Not hiding SetupHooks constructors]
409 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
410 We would like to hide as many datatype constructors from the API as possible
411 and provide smart constructors instead, so that hook authors don't end up
412 depending on internal implementation details that are subject to change.
414 However, doing so significantly degrades the Haddock documentation. So we
415 instead opt for exposing the constructor, but suggesting users use the
416 corresponding smart constructor instead.
419 --------------------------------------------------------------------------------
422 -- | Register a rule. Returns an identifier for that rule.
424 :: ShortText
-- ^ user-given rule name;
425 -- these should be unique on a per-package level
426 -> Rule
-- ^ the rule to register
428 registerRule nm
!newRule
= RulesT
$ do
429 RulesEnv
{ rulesEnvNameSpace
= ns
430 , rulesEnvVerbosity
= verbosity
} <- Reader
.ask
431 oldRules
<- lift
$ State
.get
432 let rId
= RuleId
{ ruleNameSpace
= ns
, ruleName
= nm
}
433 (mbDup
, newRules
) = Map
.insertLookupWithKey
(\ _ new _old
-> new
) rId newRule oldRules
434 for_ mbDup
$ \ oldRule
->
435 liftIO
$ dieWithException verbosity
436 $ SetupHooksException
438 $ DuplicateRuleId rId oldRule newRule
439 lift
$ State
.put newRules
442 -- | Register a rule, discarding the produced 'RuleId'.
444 -- Using this function means that you don't expect any other rules to ever
445 -- depend on any outputs of this rule. Use 'registerRule' to retain the
448 :: ShortText
-- ^ user-given rule name;
449 -- these should be unique on a per-package level
450 -> Rule
-- ^ the rule to register
452 registerRule_ i r
= void
$ registerRule i r
454 -- | Declare additional monitored objects for the collection of all rules.
456 -- When these monitored objects change, the rules are re-computed.
457 addRuleMonitors
:: Monad m
=> [MonitorFilePath
] -> RulesT m
()
458 addRuleMonitors
= RulesT
. lift
. lift
. Writer
.tell
459 {-# INLINEABLE addRuleMonitors #-}
461 -- TODO: add API functions that search and declare the appropriate monitoring