Skip to content

Commit 9857afe

Browse files
committed
Introduce SetupHooks
This commit introduces a new build-type, Hooks. A package using this build type should provide a SetupHooks.hs module which exports a value `setupHooks :: SetupHooks`. This is intended to replace the Custom setup type. This allows Cabal to have finer-grained information about the build, instead of having an opaque Setup executable to invoke.
1 parent f19498f commit 9857afe

File tree

40 files changed

+2649
-549
lines changed

40 files changed

+2649
-549
lines changed

Cabal-described/src/Distribution/Described.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ instance Described BenchmarkType where
355355
describe _ = "exitcode-stdio-1.0"
356356

357357
instance Described BuildType where
358-
describe _ = REUnion ["Simple","Configure","Custom","Make","Default"]
358+
describe _ = REUnion ["Simple","Configure","Custom","Hooks","Make","Default"]
359359

360360
instance Described CompilerFlavor where
361361
describe _ = REUnion

Cabal-hooks/Cabal-hooks.cabal

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
cabal-version: 2.2
2+
name: Cabal-hooks
3+
version: 0.1
4+
copyright: 2023, Cabal Development Team
5+
license: BSD-3-Clause
6+
license-file: LICENSE
7+
author: Cabal Development Team <[email protected]>
8+
maintainer: [email protected]
9+
homepage: http://www.haskell.org/cabal/
10+
bug-reports: https://github.com/haskell/cabal/issues
11+
synopsis: API for the Hooks build-type
12+
description:
13+
User-facing API for the Hooks build-type.
14+
category: Distribution
15+
build-type: Simple
16+
17+
extra-source-files:
18+
readme.md changelog.md
19+
20+
source-repository head
21+
type: git
22+
location: https://github.com/haskell/cabal/
23+
subdir: Cabal-hooks
24+
25+
library
26+
default-language: Haskell2010
27+
hs-source-dirs: src
28+
29+
build-depends:
30+
Cabal-syntax >= 3.11 && < 3.13,
31+
Cabal >= 3.11 && < 3.13,
32+
base >= 4.9 && < 5,
33+
containers >= 0.5.0.0 && < 0.8,
34+
filepath >= 1.3.0.1 && < 1.5,
35+
transformers >= 0.5.6.0 && < 0.7
36+
37+
ghc-options: -Wall -fno-ignore-asserts -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates
38+
39+
exposed-modules:
40+
Distribution.Simple.SetupHooks
41+
42+
other-extensions:
43+
BangPatterns
44+
CPP
45+
DefaultSignatures
46+
DeriveDataTypeable
47+
DeriveFoldable
48+
DeriveFunctor
49+
DeriveGeneric
50+
DeriveTraversable
51+
ExistentialQuantification
52+
FlexibleContexts
53+
FlexibleInstances
54+
GeneralizedNewtypeDeriving
55+
ImplicitParams
56+
KindSignatures
57+
LambdaCase
58+
NondecreasingIndentation
59+
OverloadedStrings
60+
PatternSynonyms
61+
RankNTypes
62+
RecordWildCards
63+
ScopedTypeVariables
64+
StandaloneDeriving
65+
Trustworthy
66+
TypeFamilies
67+
TypeOperators
68+
TypeSynonymInstances
69+
UndecidableInstances

Cabal-hooks/LICENSE

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Copyright (c) 2003-2023, Cabal Development Team.
2+
See the AUTHORS file for the full list of copyright holders.
3+
4+
See */LICENSE for the copyright holders of the subcomponents.
5+
6+
All rights reserved.
7+
8+
Redistribution and use in source and binary forms, with or without
9+
modification, are permitted provided that the following conditions are
10+
met:
11+
12+
* Redistributions of source code must retain the above copyright
13+
notice, this list of conditions and the following disclaimer.
14+
15+
* Redistributions in binary form must reproduce the above
16+
copyright notice, this list of conditions and the following
17+
disclaimer in the documentation and/or other materials provided
18+
with the distribution.
19+
20+
* Neither the name of Isaac Jones nor the names of other
21+
contributors may be used to endorse or promote products derived
22+
from this software without specific prior written permission.
23+
24+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Cabal-hooks/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Changelog for `Cabal-hooks`
2+
3+
## 0.1 – December 2023
4+
5+
* Initial release of the `Hooks` API.
6+

Cabal-hooks/readme.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# `Cabal-hooks`
2+
3+
This library provides an API for the `Cabal` `Hooks` build type.
4+
5+
## What is the `Hooks` build type?
6+
7+
The `Hooks` build type is a new `Cabal` build type that is scheduled to
8+
replace the `Custom` build type, providing better integration with
9+
the rest of the Haskell ecosystem.
10+
11+
The original specification for the `Hooks` build type can be found in
12+
the associated [Haskell Foundation Tech Proposal](https://github.com/haskellfoundation/tech-proposals/pull/60).
13+
14+
These *setup hooks* allow package authors to customise the configuration and
15+
building of a package by providing certain hooks that get folded into the
16+
general package configuration and building logic within `Cabal`.
17+
18+
## Defining a package with custom hooks
19+
20+
To use the `Hooks` build type, you will need to
21+
22+
* Update your `.cabal` file by:
23+
24+
- declaring `build-type: Hooks`,
25+
- declaring a `custom-setup` stanza, with a `setup-depends`
26+
field which includes a dependency on `Cabal-hooks`.
27+
28+
* Define a Haskell module `SetupHooks`, which must be placed
29+
at the root of your project and must define a value
30+
`setupHooks :: SetupHooks`.
31+
32+
That is, your `.cabal` file should contain the following
33+
34+
```cabal
35+
-- my-package.cabal
36+
name: my-package
37+
build-type: Hooks
38+
39+
custom-setup
40+
setup-depends:
41+
Cabal-hooks >= 0.1 && < 0.2
42+
```
43+
44+
and your `SetupHooks.hs` file should look like:
45+
46+
```haskell
47+
-- SetupHooks.hs
48+
module SetupHooks ( setupHooks ) where
49+
50+
-- Cabal-hooks
51+
import Distribution.Simple.SetupHooks
52+
53+
setupHooks :: SetupHooks
54+
setupHooks = ...
55+
-- use the API provided by 'Distribution.Simple.SetupHooks'
56+
-- to define the hooks relevant to your package
57+
```
58+
59+
## Using the API
60+
61+
The [Haddock documentation](https://hackage.haskell.org/package/Cabal-hooks)
62+
should help you get started using this library's API.

Cabal-hooks/src/Distribution/Simple/SetupHooks.hs

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ This module defines the interface for the @Hooks@ @build-type@.
1717
1818
To write a package that implements @build-type: Hooks@, you should define
1919
a module @SetupHooks.hs@ which exports a value @setupHooks :: 'SetupHooks'@.
20-
This is a record that declares actions to hook into the cabal build process.
20+
This is a record that declares actions that should be hooked into the
21+
cabal build process.
2122
2223
See 'SetupHooks' for more details.
2324
-}
@@ -97,9 +98,7 @@ module Distribution.Simple.SetupHooks
9798

9899
-- **** File/directory monitoring
99100
, addRuleMonitors
100-
, MonitorFilePath(..)
101-
, MonitorKindFile(..)
102-
, MonitorKindDir(..)
101+
, module Distribution.Simple.FileMonitor.Types
103102

104103
-- * Install hooks
105104
, InstallHooks(..), noInstallHooks
@@ -120,17 +119,27 @@ module Distribution.Simple.SetupHooks
120119
-- | These are functions provided as part of the @Hooks@ API.
121120
-- It is recommended to import them from this module as opposed to
122121
-- manually importing them from inside the Cabal module hierarchy.
123-
, installFileGlob, addKnownPrograms
122+
123+
-- *** Copy/install functions
124+
, installFileGlob
125+
126+
-- *** Interacting with the program database
127+
, Program(..), ConfiguredProgram(..), ProgArg
128+
, ProgramLocation(..)
129+
, ProgramDb
130+
, addKnownPrograms
131+
, configureUnconfiguredProgram
132+
, simpleProgram
124133

125134
-- ** General @Cabal@ datatypes
126135
, Verbosity, Compiler(..), Platform(..), Suffix(..)
127136

128137
-- *** Package information
129138
, LocalBuildConfig, LocalBuildInfo, PackageBuildDescr
130-
-- SetupHooks TODO: we can't simply re-export all the fields of
131-
-- LocalBuildConfig etc, due to the presence of duplicate record fields.
132-
-- Ideally we'd like to e.g. re-export LocalBuildConfig
133-
-- qualified, but qualified re-exports aren't a thing currently.
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.
134143

135144
, PackageDescription(..)
136145

@@ -146,9 +155,6 @@ module Distribution.Simple.SetupHooks
146155
, emptyLibrary, emptyForeignLib, emptyExecutable
147156
, emptyTestSuite, emptyBenchmark
148157

149-
-- ** Programs
150-
, Program, ConfiguredProgram, ProgramDb, ProgArg
151-
152158
)
153159
where
154160
import Distribution.PackageDescription
@@ -166,16 +172,24 @@ import Distribution.Simple.Compiler
166172
( Compiler(..) )
167173
import Distribution.Simple.Errors
168174
( CabalException(SetupHooksException) )
175+
import Distribution.Simple.FileMonitor.Types
169176
import Distribution.Simple.Install
170177
( installFileGlob )
171178
import Distribution.Simple.LocalBuildInfo
172179
( componentBuildDir )
173180
import Distribution.Simple.PreProcess.Types
174181
( Suffix(..) )
175182
import Distribution.Simple.Program.Db
176-
( ProgramDb, addKnownPrograms )
183+
( ProgramDb, addKnownPrograms
184+
, configureUnconfiguredProgram
185+
)
186+
import Distribution.Simple.Program.Find
187+
( simpleProgram )
177188
import Distribution.Simple.Program.Types
178-
( Program, ConfiguredProgram, ProgArg )
189+
( Program(..), ConfiguredProgram(..)
190+
, ProgArg
191+
, ProgramLocation(..)
192+
)
179193
import Distribution.Simple.Setup
180194
( BuildFlags(..)
181195
, ConfigFlags(..)
@@ -250,7 +264,9 @@ Usage example:
250264
> custom-setup
251265
> setup-depends:
252266
> base >= 4.18 && < 5,
253-
> Cabal-hooks >= 0.1 && < 0.3
267+
> Cabal-hooks >= 0.1 && < 0.2
268+
>
269+
> The declared Cabal version should also be at least 3.12.
254270
255271
> -- In SetupHooks.hs, next to your .cabal file
256272
> module SetupHooks where
@@ -304,34 +320,39 @@ For example, to generate modules inside a given component, you should:
304320
-}
305321

306322
{- $preBuildRules
307-
Pre-build hooks are specified in the form of a collection of pre-build 'Rules'.
323+
Pre-build hooks are specified as a collection of pre-build 'Rules'.
324+
Each t'Rule' consists of:
308325
309-
Pre-build rules are specified as a collection of rules. Each t'Rule' declares
310-
its dependencies, its outputs, and refers to a command to run in order to
311-
execute the rule in the form of a t'RuleCommands'.
326+
- a specification of its static dependencies and outputs,
327+
- the commands that execute the rule.
328+
329+
Rules are constructed using either one of the 'staticRule' or 'dynamicRule'
330+
smart constructors. Directly constructing a t'Rule' using the constructors of
331+
that data type is not advised, as this relies on internal implementation details
332+
which are subject to change in between versions of the `Cabal-hooks` library.
312333
313334
Note that:
314335
315-
- file dependencies are not specified directly by 'FilePath' but rather use
316-
the 'Location' type,
317-
- rules can directly depend on other rules, which requires the ability to
318-
refer to a rule by 'RuleId',
319-
- rules refer to the actions that execute them using static pointers, in order
320-
to enable serialisation/deserialisation of rules,
321-
- rules can additionally monitor files or directories, which determines
336+
- To declare the dependency on the output of a rule, one must refer to the
337+
rule directly, and not to the path to the output executing that rule will
338+
eventually produce.
339+
To do so, registering a t'Rule' with the API returns a unique identifier
340+
for that rule, in the form of a t'RuleId'.
341+
- File dependencies and outputs are not specified directly by
342+
'FilePath', but rather use the 'Location' type (which is more convenient
343+
when working with preprocessors).
344+
- Rules refer to the actions that execute them using static pointers, in order
345+
to enable serialisation/deserialisation of rules.
346+
- Rules can additionally monitor files or directories, which determines
322347
when to re-compute the entire set of rules.
323-
324-
To construct a t'Rule', you should use one of the 'staticRule' or 'dynamicRule'
325-
smart constructors, to avoid relying on internal implementation details of
326-
the t'Rule' datatype.
327348
-}
328349

329350
{- $rulesDemand
330351
Rules can declare various kinds of dependencies:
331352
332353
- 'staticDependencies': files or other rules that a rule statically depends on,
333354
- extra dynamic dependencies, using the 'DynamicRuleCommands' constructor,
334-
- 'MonitoredFileOrDir': additional files or directories to monitor.
355+
- 'MonitorFilePath': additional files and directories to monitor.
335356
336357
Rules are considered __out-of-date__ precisely when any of the following
337358
conditions apply:
@@ -377,11 +398,11 @@ Defining pre-build rules can be done in the following style:
377398
> let cmd1 = mkCommand (static Dict) $ static \ arg -> do { .. }
378399
> cmd2 = mkCommand (static Dict) $ static \ arg -> do { .. }
379400
> myData <- liftIO someIOAction
380-
> addRuleMonitors [ MonitorDir "someSearchDir" DirContents ]
381-
> registerRule_ $ staticRule (cmd1 arg1) deps1 outs1
382-
> registerRule_ $ staticRule (cmd1 arg2) deps2 outs2
383-
> registerRule_ $ staticRule (cmd1 arg3) deps3 outs3
384-
> registerRule_ $ staticRule (cmd2 arg4) deps4 outs4
401+
> addRuleMonitors [ monitorDirectory "someSearchDir" ]
402+
> registerRule_ "rule_1_1" $ staticRule (cmd1 arg1) deps1 outs1
403+
> registerRule_ "rule_1_2" $ staticRule (cmd1 arg2) deps2 outs2
404+
> registerRule_ "rule_1_3" $ staticRule (cmd1 arg3) deps3 outs3
405+
> registerRule_ "rule_2_4" $ staticRule (cmd2 arg4) deps4 outs4
385406
386407
Here we use the 'rules', 'staticRule' and 'mkCommand' smart constructors,
387408
rather than directly using the v'Rules', v'Rule' and v'Command' constructors,
@@ -456,5 +477,5 @@ findFileInDirs file dirs =
456477
| path <- nub dirs
457478
]
458479

459-
-- SetupHooks TODO: add API functions that do searching and declare
460-
-- the appropriate monitoring at the same time.
480+
-- TODO: add API functions that search and declare the appropriate monitoring
481+
-- at the same time.

Cabal-syntax/src/Distribution/PackageDescription/Parsec.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,10 @@ checkForUndefinedCustomSetup gpd = do
761761
parseFailure zeroPos $
762762
"Since cabal-version: 1.24 specifying custom-setup section is mandatory"
763763

764+
when (buildType pd == Hooks && isNothing (setupBuildInfo pd)) $
765+
parseFailure zeroPos $
766+
"Packages with build-type: Hooks require a custom-setup stanza"
767+
764768
-------------------------------------------------------------------------------
765769
-- Post processing of internal dependencies
766770
-------------------------------------------------------------------------------
@@ -988,7 +992,7 @@ parseHookedBuildInfo' lexWarnings fs = do
988992
-- RFC5234 ABNF):
989993
--
990994
-- @
991-
-- newstyle-spec-version-decl = "cabal-version" *WS ":" *WS newstyle-pec-version *WS
995+
-- newstyle-spec-version-decl = "cabal-version" *WS ":" *WS newstyle-spec-version *WS
992996
--
993997
-- spec-version = NUM "." NUM [ "." NUM ]
994998
--

0 commit comments

Comments
 (0)