Merge pull request #10654 from haskell/wip/T10652
[cabal.git] / Cabal-hooks / src / Distribution / Simple / SetupHooks.hs
blob5e194ec9161c8f33068b0a2bfff7ab4ec9ab3787
1 {-# LANGUAGE BangPatterns #-}
2 {-# LANGUAGE CPP #-}
3 {-# LANGUAGE DerivingStrategies #-}
4 {-# LANGUAGE DuplicateRecordFields #-}
5 {-# LANGUAGE RankNTypes #-}
6 {-# LANGUAGE StaticPointers #-}
8 {-|
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
17 cabal build process.
19 See 'SetupHooks' for more details.
21 module Distribution.Simple.SetupHooks
22 ( -- * Hooks
24 -- $setupHooks
25 SetupHooks(..)
26 , noSetupHooks
28 -- * Configure hooks
30 -- $configureHooks
31 , ConfigureHooks(..)
32 , noConfigureHooks
33 -- ** Per-package configure hooks
34 , PreConfPackageInputs(..)
35 , PreConfPackageOutputs(..) -- See Note [Not hiding SetupHooks constructors]
36 , noPreConfPackageOutputs
37 , PreConfPackageHook
38 , PostConfPackageInputs(..)
39 , PostConfPackageHook
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
48 , BuildInfoDiff
50 -- * Build hooks
52 , BuildHooks(..), noBuildHooks
53 , BuildingWhat(..), buildingWhatVerbosity, buildingWhatDistPref
55 -- ** Pre-build rules
57 -- $preBuildRules
58 , PreBuildComponentInputs(..)
59 , PreBuildComponentRules
61 -- ** Post-build hooks
62 , PostBuildComponentInputs(..)
63 , PostBuildComponentHook
65 -- ** Rules
66 , Rules
67 , rules
68 , noRules
69 , Rule
70 , Dependency (..)
71 , RuleOutput (..)
72 , RuleId
73 , staticRule, dynamicRule
74 -- *** Rule inputs/outputs
76 -- $rulesDemand
77 , Location(..)
78 , location
79 , autogenComponentModulesDir
80 , componentBuildDir
82 -- *** Actions
83 , RuleCommands(..)
84 , Command
85 , mkCommand
86 , Dict(..)
88 -- *** Rules API
90 -- $rulesAPI
91 , RulesM
92 , registerRule
93 , registerRule_
95 -- **** File/directory monitoring
96 , addRuleMonitors
97 , module Distribution.Simple.FileMonitor.Types
99 -- * Install hooks
100 , InstallHooks(..), noInstallHooks
101 , InstallComponentInputs(..), InstallComponentHook
103 -- * Re-exports
105 -- ** Hooks
106 -- *** Configure hooks
107 , ConfigFlags(..)
108 -- *** Build hooks
109 , BuildFlags(..), ReplFlags(..), HaddockFlags(..), HscolourFlags(..)
110 -- *** Install hooks
111 , CopyFlags(..)
113 -- ** @Hooks@ API
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
120 , installFileGlob
122 -- *** Interacting with the program database
123 , Program(..), ConfiguredProgram(..), ProgArg
124 , ProgramLocation(..)
125 , ProgramDb
126 , addKnownPrograms
127 , configureUnconfiguredProgram
128 , simpleProgram
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(..)
147 -- **** Components
148 , Library(..), ForeignLib(..), Executable(..)
149 , TestSuite(..), Benchmark(..)
150 , LibraryName(..)
151 , emptyLibrary, emptyForeignLib, emptyExecutable
152 , emptyTestSuite, emptyBenchmark
155 where
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
168 ( Compiler(..) )
169 import Distribution.Simple.Errors
170 ( CabalException(SetupHooksException) )
171 import Distribution.Simple.FileMonitor.Types
172 import Distribution.Simple.Install
173 ( installFileGlob )
174 import Distribution.Simple.LocalBuildInfo
175 ( componentBuildDir )
176 import Distribution.Simple.PreProcess.Types
177 ( Suffix(..) )
178 import Distribution.Simple.Program.Db
179 ( ProgramDb, addKnownPrograms
180 , configureUnconfiguredProgram
182 import Distribution.Simple.Program.Find
183 ( simpleProgram )
184 import Distribution.Simple.Program.Types
185 ( Program(..), ConfiguredProgram(..)
186 , ProgArg
187 , ProgramLocation(..)
189 import Distribution.Simple.Setup
190 ( BuildFlags(..)
191 , ConfigFlags(..)
192 , CopyFlags(..)
193 , HaddockFlags(..)
194 , HscolourFlags(..)
195 , ReplFlags(..)
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
201 ( dieWithException )
202 import Distribution.System
203 ( Platform(..) )
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
213 ( TargetInfo(..) )
214 import Distribution.Utils.ShortText
215 ( ShortText )
216 import Distribution.Verbosity
217 ( Verbosity )
219 import Control.Monad
220 ( void )
221 import Control.Monad.IO.Class
222 ( MonadIO(liftIO) )
223 import Control.Monad.Trans.Class
224 ( lift )
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
229 #else
230 import qualified Control.Monad.Trans.Writer.Strict as Writer
231 #endif
232 import Data.Foldable
233 ( for_ )
234 import Data.Map.Strict as Map
235 ( insertLookupWithKey )
237 --------------------------------------------------------------------------------
238 -- Haddocks for the SetupHooks API
240 {- $setupHooks
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.
251 Usage example:
253 > -- In your .cabal file
254 > build-type: Hooks
256 > custom-setup
257 > setup-depends:
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
268 > setupHooks =
269 > noSetupHooks
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
283 > mySetupHooks = ...
286 {- $configureHooks
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.
314 {- $preBuildRules
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.
326 Note that:
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
330 eventually produce.
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.
342 {- $rulesDemand
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
350 conditions apply:
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
378 up-to-date rules.
379 2. Re-run stale rules.
382 {- $rulesAPI
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 --------------------------------------------------------------------------------
420 -- API functions
422 -- | Register a rule. Returns an identifier for that rule.
423 registerRule
424 :: ShortText -- ^ user-given rule name;
425 -- these should be unique on a per-package level
426 -> Rule -- ^ the rule to register
427 -> RulesM RuleId
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
437 $ RulesException
438 $ DuplicateRuleId rId oldRule newRule
439 lift $ State.put newRules
440 return rId
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
446 -- 'RuleId' instead.
447 registerRule_
448 :: ShortText -- ^ user-given rule name;
449 -- these should be unique on a per-package level
450 -> Rule -- ^ the rule to register
451 -> RulesT IO ()
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
462 -- at the same time.