1 {-# LANGUAGE BangPatterns #-}
3 {-# LANGUAGE DerivingStrategies #-}
4 {-# LANGUAGE DuplicateRecordFields #-}
5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6 {-# LANGUAGE RankNTypes #-}
7 {-# LANGUAGE RecursiveDo #-}
8 {-# LANGUAGE StaticPointers #-}
9 {-# LANGUAGE TupleSections #-}
10 {-# LANGUAGE OverloadedStrings #-}
13 Module: Distribution.Simple.SetupHooks
14 Description: Interface for the @Hooks@ @build-type@.
16 This module defines the interface for the @Hooks@ @build-type@.
18 To write a package that implements @build-type: Hooks@, you should define
19 a module @SetupHooks.hs@ which exports a value @setupHooks :: 'SetupHooks'@.
20 This is a record that declares actions that should be hooked into the
23 See 'SetupHooks' for more details.
25 module Distribution
.Simple
.SetupHooks
37 -- ** Per-package configure hooks
38 , PreConfPackageInputs
(..)
39 , PreConfPackageOutputs
(..) -- See Note [Not hiding SetupHooks constructors]
40 , noPreConfPackageOutputs
42 , PostConfPackageInputs
(..)
44 -- ** Per-component configure hooks
45 , PreConfComponentInputs
(..)
46 , PreConfComponentOutputs
(..) -- See Note [Not hiding SetupHooks constructors]
47 , noPreConfComponentOutputs
48 , PreConfComponentHook
49 , ComponentDiff
(..), emptyComponentDiff
, buildInfoComponentDiff
50 , LibraryDiff
, ForeignLibDiff
, ExecutableDiff
51 , TestSuiteDiff
, BenchmarkDiff
56 , BuildHooks
(..), noBuildHooks
57 , BuildingWhat
(..), buildingWhatVerbosity
, buildingWhatDistPref
62 , PreBuildComponentInputs
(..)
63 , PreBuildComponentRules
65 -- ** Post-build hooks
66 , PostBuildComponentInputs
(..)
67 , PostBuildComponentHook
77 , staticRule
, dynamicRule
78 -- *** Rule inputs/outputs
83 , autogenComponentModulesDir
99 -- **** File/directory monitoring
101 , module Distribution
.Simple
.FileMonitor
.Types
104 , InstallHooks
(..), noInstallHooks
105 , InstallComponentInputs
(..), InstallComponentHook
110 -- *** Configure hooks
113 , BuildFlags
(..), ReplFlags
(..), HaddockFlags
(..), HscolourFlags
(..)
119 -- | These are functions provided as part of the @Hooks@ API.
120 -- It is recommended to import them from this module as opposed to
121 -- manually importing them from inside the Cabal module hierarchy.
123 -- *** Copy/install functions
126 -- *** Interacting with the program database
127 , Program
(..), ConfiguredProgram
(..), ProgArg
128 , ProgramLocation
(..)
131 , configureUnconfiguredProgram
134 -- ** General @Cabal@ datatypes
135 , Verbosity
, Compiler
(..), Platform
(..), Suffix
(..)
137 -- *** Package information
138 , LocalBuildConfig
, LocalBuildInfo
, PackageBuildDescr
139 -- NB: we can't simply re-export all the fields of LocalBuildConfig etc,
140 -- due to the presence of duplicate record fields.
141 -- Ideally, we'd like to e.g. re-export LocalBuildConfig qualified,
142 -- but qualified re-exports aren't a thing currently.
144 , PackageDescription
(..)
146 -- *** Component information
147 , Component
(..), ComponentName
(..), componentName
148 , BuildInfo
(..), emptyBuildInfo
149 , TargetInfo
(..), ComponentLocalBuildInfo
(..)
152 , Library
(..), ForeignLib
(..), Executable
(..)
153 , TestSuite
(..), Benchmark
(..)
155 , emptyLibrary
, emptyForeignLib
, emptyExecutable
156 , emptyTestSuite
, emptyBenchmark
160 import Distribution
.PackageDescription
161 ( PackageDescription
(..)
162 , Library
(..), ForeignLib
(..)
163 , Executable
(..), TestSuite
(..), Benchmark
(..)
164 , emptyLibrary
, emptyForeignLib
165 , emptyExecutable
, emptyBenchmark
, emptyTestSuite
166 , BuildInfo
(..), emptyBuildInfo
167 , ComponentName
(..), LibraryName
(..)
169 import Distribution
.Simple
.BuildPaths
170 ( autogenComponentModulesDir
)
171 import Distribution
.Simple
.Compiler
173 import Distribution
.Simple
.Errors
174 ( CabalException
(SetupHooksException
) )
175 import Distribution
.Simple
.FileMonitor
.Types
176 import Distribution
.Simple
.Install
178 import Distribution
.Simple
.LocalBuildInfo
179 ( componentBuildDir
)
180 import Distribution
.Simple
.PreProcess
.Types
182 import Distribution
.Simple
.Program
.Db
183 ( ProgramDb
, addKnownPrograms
184 , configureUnconfiguredProgram
186 import Distribution
.Simple
.Program
.Find
188 import Distribution
.Simple
.Program
.Types
189 ( Program
(..), ConfiguredProgram
(..)
191 , ProgramLocation
(..)
193 import Distribution
.Simple
.Setup
201 import Distribution
.Simple
.SetupHooks
.Errors
202 import Distribution
.Simple
.SetupHooks
.Internal
203 import Distribution
.Simple
.SetupHooks
.Rule
as Rule
204 import Distribution
.Simple
.Utils
206 import Distribution
.System
208 import Distribution
.Types
.Component
209 ( Component
(..), componentName
)
210 import Distribution
.Types
.ComponentLocalBuildInfo
211 ( ComponentLocalBuildInfo
(..) )
212 import Distribution
.Types
.LocalBuildInfo
213 ( LocalBuildInfo
(..) )
214 import Distribution
.Types
.LocalBuildConfig
215 ( LocalBuildConfig
, PackageBuildDescr
)
216 import Distribution
.Types
.TargetInfo
218 import Distribution
.Utils
.ShortText
220 import Distribution
.Verbosity
225 import Control
.Monad
.IO.Class
227 import Control
.Monad
.Trans
.Class
229 import qualified Control
.Monad
.Trans
.Reader
as Reader
230 import qualified Control
.Monad
.Trans
.State
as State
231 #if MIN_VERSION_transformers
(0,5,6)
232 import qualified Control
.Monad
.Trans
.Writer
.CPS
as Writer
234 import qualified Control
.Monad
.Trans
.Writer
.Strict
as Writer
238 import Data
.Map
.Strict
as Map
239 ( insertLookupWithKey
)
241 --------------------------------------------------------------------------------
242 -- Haddocks for the SetupHooks API
245 A Cabal package with @Hooks@ @build-type@ must define the Haskell module
246 @SetupHooks@ which defines a value @setupHooks :: 'SetupHooks'@.
248 These *setup hooks* allow package authors to customise the configuration and
249 building of a package by providing certain hooks that get folded into the
250 general package configuration and building logic within @Cabal@.
252 This mechanism replaces the @Custom@ @build-type@, providing better
253 integration with the rest of the Haskell ecosystem.
257 > -- In your .cabal file
262 > base >= 4.18 && < 5,
263 > Cabal-hooks >= 0.1 && < 0.2
265 > The declared Cabal version should also be at least 3.14.
267 > -- In SetupHooks.hs, next to your .cabal file
268 > module SetupHooks where
269 > import Distribution.Simple.SetupHooks ( SetupHooks, noSetupHooks )
271 > setupHooks :: SetupHooks
274 > { configureHooks = myConfigureHooks
275 > , buildHooks = myBuildHooks }
277 Note that 'SetupHooks' can be monoidally combined, e.g.:
279 > module SetupHooks where
280 > import Distribution.Simple.SetupHooks
281 > import qualified SomeOtherLibrary ( setupHooks )
283 > setupHooks :: SetupHooks
284 > setupHooks = SomeOtherLibrary.setupHooks <> mySetupHooks
286 > mySetupHooks :: SetupHooks
291 Configure hooks can be used to augment the Cabal configure logic with
292 package-specific logic. The main principle is that the configure hooks can
293 feed into updating the 'PackageDescription' of a @cabal@ package. From then on,
294 this package configuration is set in stone, and later hooks (e.g. hooks into
295 the build phase) can no longer modify this configuration; instead they will
296 receive this configuration in their inputs, and must honour it.
298 Configuration happens at two levels:
300 * global configuration covers the entire package,
301 * local configuration covers a single component.
303 Once the global package configuration is done, all hooks work on a
304 per-component level. The configuration hooks thus follow a simple philosophy:
306 * All modifications to global package options must use `preConfPackageHook`.
307 * All modifications to component configuration options must use `preConfComponentHook`.
309 For example, to generate modules inside a given component, you should:
311 * In the per-component configure hook, declare the modules you are going to
312 generate by adding them to the `autogenModules` field for that component
313 (unless you know them ahead of time, in which case they can be listed
314 textually in the @.cabal@ file of the project).
315 * In the build hooks, describe the actions that will generate these modules.
319 Pre-build hooks are specified as a collection of pre-build 'Rules'.
320 Each t'Rule' consists of:
322 - a specification of its static dependencies and outputs,
323 - the commands that execute the rule.
325 Rules are constructed using either one of the 'staticRule' or 'dynamicRule'
326 smart constructors. Directly constructing a t'Rule' using the constructors of
327 that data type is not advised, as this relies on internal implementation details
328 which are subject to change in between versions of the `Cabal-hooks` library.
332 - To declare the dependency on the output of a rule, one must refer to the
333 rule directly, and not to the path to the output executing that rule will
335 To do so, registering a t'Rule' with the API returns a unique identifier
336 for that rule, in the form of a t'RuleId'.
337 - File dependencies and outputs are not specified directly by
338 'FilePath', but rather use the 'Location' type (which is more convenient
339 when working with preprocessors).
340 - Rules refer to the actions that execute them using static pointers, in order
341 to enable serialisation/deserialisation of rules.
342 - Rules can additionally monitor files or directories, which determines
343 when to re-compute the entire set of rules.
347 Rules can declare various kinds of dependencies:
349 - 'staticDependencies': files or other rules that a rule statically depends on,
350 - extra dynamic dependencies, using the 'DynamicRuleCommands' constructor,
351 - 'MonitorFilePath': additional files and directories to monitor.
353 Rules are considered __out-of-date__ precisely when any of the following
356 [O1] there has been a (relevant) change in the files and directories
357 monitored by the rules,
358 [O2] the environment passed to the computation of rules has changed.
360 If the rules are out-of-date, the build system is expected to re-run the
361 computation that computes all rules.
363 After this re-computation of the set of all rules, we match up new rules
364 with old rules, by 'RuleId'. A rule is then considered __stale__ if any of
365 following conditions apply:
367 [N] the rule is new, or
368 [S] the rule matches with an old rule, and either:
370 [S1] a file dependency of the rule has been modified/created/deleted, or
371 a (transitive) rule dependency of the rule is itself stale, or
372 [S2] the rule is different from the old rule, e.g. the argument stored in
373 the rule command has changed, or the pointer to the action to run the
374 rule has changed. (This is determined using the @Eq Rule@ instance.)
376 A stale rule becomes no longer stale once we run its associated action. The
377 build system is responsible for re-running the actions associated with
378 each stale rule, in dependency order. This means the build system is expected
379 to behave as follows:
381 1. Any time the rules are out-of-date, query the rules to obtain
383 2. Re-run stale rules.
387 Defining pre-build rules can be done in the following style:
389 > {-# LANGUAGE BlockArguments, StaticPointers #-}
390 > myPreBuildRules
:: PreBuildComponentRules
391 > myPreBuildRules
= rules
(static
()) $ \ preBuildEnvironment
-> do
392 > let cmd1
= mkCommand
(static Dict
) $ static
\ arg
-> do { .. }
393 > cmd2
= mkCommand
(static Dict
) $ static
\ arg
-> do { .. }
394 > myData
<- liftIO someIOAction
395 > addRuleMonitors
[ monitorDirectory
"someSearchDir" ]
396 > registerRule_
"rule_1_1" $ staticRule
(cmd1 arg1
) deps1 outs1
397 > registerRule_
"rule_1_2" $ staticRule
(cmd1 arg2
) deps2 outs2
398 > registerRule_
"rule_1_3" $ staticRule
(cmd1 arg3
) deps3 outs3
399 > registerRule_
"rule_2_4" $ staticRule
(cmd2 arg4
) deps4 outs4
401 Here we use the
'rules
', 'staticRule
' and 'mkCommand
' smart constructors
,
402 rather than directly using the v
'Rules
', v
'Rule
' and v
'Command
' constructors
,
403 which insulates us from internal changes to the t
'Rules
', t
'Rule
' and t
'Command
'
404 datatypes
, respectively
.
406 We use
'addRuleMonitors
' to declare a monitored directory that the collection
407 of rules
as a whole depends on
. In this
case, we declare that they depend on the
408 contents
of the
"searchDir" directory
. This means that the rules will be
409 computed anew whenever the contents
of this directory change
.
412 {- Note [Not hiding SetupHooks constructors]
413 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
414 We would like to hide as many datatype constructors from the API as possible
415 and provide smart constructors instead, so that hook authors don't end up
416 depending on internal implementation details that are subject to change.
418 However, doing so significantly degrades the Haddock documentation. So we
419 instead opt for exposing the constructor, but suggesting users use the
420 corresponding smart constructor instead.
423 --------------------------------------------------------------------------------
426 -- | Register a rule. Returns an identifier for that rule.
428 :: ShortText
-- ^ user-given rule name;
429 -- these should be unique on a per-package level
430 -> Rule
-- ^ the rule to register
432 registerRule nm
!newRule
= RulesT
$ do
433 RulesEnv
{ rulesEnvNameSpace
= ns
434 , rulesEnvVerbosity
= verbosity
} <- Reader
.ask
435 oldRules
<- lift
$ State
.get
436 let rId
= RuleId
{ ruleNameSpace
= ns
, ruleName
= nm
}
437 (mbDup
, newRules
) = Map
.insertLookupWithKey
(\ _ new _old
-> new
) rId newRule oldRules
438 for_ mbDup
$ \ oldRule
->
439 liftIO
$ dieWithException verbosity
440 $ SetupHooksException
442 $ DuplicateRuleId rId oldRule newRule
443 lift
$ State
.put newRules
446 -- | Register a rule, discarding the produced 'RuleId'.
448 -- Using this function means that you don't expect any other rules to ever
449 -- depend on any outputs of this rule. Use 'registerRule' to retain the
452 :: ShortText
-- ^ user-given rule name;
453 -- these should be unique on a per-package level
454 -> Rule
-- ^ the rule to register
456 registerRule_ i r
= void
$ registerRule i r
458 -- | Declare additional monitored objects for the collection of all rules.
460 -- When these monitored objects change, the rules are re-computed.
461 addRuleMonitors
:: Monad m
=> [MonitorFilePath
] -> RulesT m
()
462 addRuleMonitors
= RulesT
. lift
. lift
. Writer
.tell
463 {-# INLINEABLE addRuleMonitors #-}
465 -- TODO: add API functions that search and declare the appropriate monitoring