Improve assertions in `IntegrationTests2` `testConfigOptionComments`
[cabal.git] / Cabal-hooks / src / Distribution / Simple / SetupHooks.hs
blob2b49ffc5e5ff42df1be5ec697d758d2a32043eea
1 {-# LANGUAGE BangPatterns #-}
2 {-# LANGUAGE CPP #-}
3 {-# LANGUAGE DerivingStrategies #-}
4 {-# LANGUAGE DuplicateRecordFields #-}
5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6 {-# LANGUAGE RankNTypes #-}
7 {-# LANGUAGE RecursiveDo #-}
8 {-# LANGUAGE StaticPointers #-}
9 {-# LANGUAGE TupleSections #-}
10 {-# LANGUAGE OverloadedStrings #-}
12 {-|
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
21 cabal build process.
23 See 'SetupHooks' for more details.
25 module Distribution.Simple.SetupHooks
26 ( -- * Hooks
28 -- $setupHooks
29 SetupHooks(..)
30 , noSetupHooks
32 -- * Configure hooks
34 -- $configureHooks
35 , ConfigureHooks(..)
36 , noConfigureHooks
37 -- ** Per-package configure hooks
38 , PreConfPackageInputs(..)
39 , PreConfPackageOutputs(..) -- See Note [Not hiding SetupHooks constructors]
40 , noPreConfPackageOutputs
41 , PreConfPackageHook
42 , PostConfPackageInputs(..)
43 , PostConfPackageHook
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
52 , BuildInfoDiff
54 -- * Build hooks
56 , BuildHooks(..), noBuildHooks
57 , BuildingWhat(..), buildingWhatVerbosity, buildingWhatDistPref
59 -- ** Pre-build rules
61 -- $preBuildRules
62 , PreBuildComponentInputs(..)
63 , PreBuildComponentRules
65 -- ** Post-build hooks
66 , PostBuildComponentInputs(..)
67 , PostBuildComponentHook
69 -- ** Rules
70 , Rules
71 , rules
72 , noRules
73 , Rule
74 , Dependency (..)
75 , RuleOutput (..)
76 , RuleId
77 , staticRule, dynamicRule
78 -- *** Rule inputs/outputs
80 -- $rulesDemand
81 , Location(..)
82 , location
83 , autogenComponentModulesDir
84 , componentBuildDir
86 -- *** Actions
87 , RuleCommands(..)
88 , Command
89 , mkCommand
90 , Dict(..)
92 -- *** Rules API
94 -- $rulesAPI
95 , RulesM
96 , registerRule
97 , registerRule_
99 -- **** File/directory monitoring
100 , addRuleMonitors
101 , module Distribution.Simple.FileMonitor.Types
103 -- * Install hooks
104 , InstallHooks(..), noInstallHooks
105 , InstallComponentInputs(..), InstallComponentHook
107 -- * Re-exports
109 -- ** Hooks
110 -- *** Configure hooks
111 , ConfigFlags(..)
112 -- *** Build hooks
113 , BuildFlags(..), ReplFlags(..), HaddockFlags(..), HscolourFlags(..)
114 -- *** Install hooks
115 , CopyFlags(..)
117 -- ** @Hooks@ API
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
124 , installFileGlob
126 -- *** Interacting with the program database
127 , Program(..), ConfiguredProgram(..), ProgArg
128 , ProgramLocation(..)
129 , ProgramDb
130 , addKnownPrograms
131 , configureUnconfiguredProgram
132 , simpleProgram
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(..)
151 -- **** Components
152 , Library(..), ForeignLib(..), Executable(..)
153 , TestSuite(..), Benchmark(..)
154 , LibraryName(..)
155 , emptyLibrary, emptyForeignLib, emptyExecutable
156 , emptyTestSuite, emptyBenchmark
159 where
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
172 ( Compiler(..) )
173 import Distribution.Simple.Errors
174 ( CabalException(SetupHooksException) )
175 import Distribution.Simple.FileMonitor.Types
176 import Distribution.Simple.Install
177 ( installFileGlob )
178 import Distribution.Simple.LocalBuildInfo
179 ( componentBuildDir )
180 import Distribution.Simple.PreProcess.Types
181 ( Suffix(..) )
182 import Distribution.Simple.Program.Db
183 ( ProgramDb, addKnownPrograms
184 , configureUnconfiguredProgram
186 import Distribution.Simple.Program.Find
187 ( simpleProgram )
188 import Distribution.Simple.Program.Types
189 ( Program(..), ConfiguredProgram(..)
190 , ProgArg
191 , ProgramLocation(..)
193 import Distribution.Simple.Setup
194 ( BuildFlags(..)
195 , ConfigFlags(..)
196 , CopyFlags(..)
197 , HaddockFlags(..)
198 , HscolourFlags(..)
199 , ReplFlags(..)
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
205 ( dieWithException )
206 import Distribution.System
207 ( Platform(..) )
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
217 ( TargetInfo(..) )
218 import Distribution.Utils.ShortText
219 ( ShortText )
220 import Distribution.Verbosity
221 ( Verbosity )
223 import Control.Monad
224 ( void )
225 import Control.Monad.IO.Class
226 ( MonadIO(liftIO) )
227 import Control.Monad.Trans.Class
228 ( lift )
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
233 #else
234 import qualified Control.Monad.Trans.Writer.Strict as Writer
235 #endif
236 import Data.Foldable
237 ( for_ )
238 import Data.Map.Strict as Map
239 ( insertLookupWithKey )
241 --------------------------------------------------------------------------------
242 -- Haddocks for the SetupHooks API
244 {- $setupHooks
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.
255 Usage example:
257 > -- In your .cabal file
258 > build-type: Hooks
260 > custom-setup
261 > setup-depends:
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
272 > setupHooks =
273 > noSetupHooks
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
287 > mySetupHooks = ...
290 {- $configureHooks
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.
318 {- $preBuildRules
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.
330 Note that:
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
334 eventually produce.
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.
346 {- $rulesDemand
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
354 conditions apply:
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
382 up-to-date rules.
383 2. Re-run stale rules.
386 {- $rulesAPI
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 --------------------------------------------------------------------------------
424 -- API functions
426 -- | Register a rule. Returns an identifier for that rule.
427 registerRule
428 :: ShortText -- ^ user-given rule name;
429 -- these should be unique on a per-package level
430 -> Rule -- ^ the rule to register
431 -> RulesM RuleId
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
441 $ RulesException
442 $ DuplicateRuleId rId oldRule newRule
443 lift $ State.put newRules
444 return rId
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
450 -- 'RuleId' instead.
451 registerRule_
452 :: ShortText -- ^ user-given rule name;
453 -- these should be unique on a per-package level
454 -> Rule -- ^ the rule to register
455 -> RulesT IO ()
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
466 -- at the same time.