summaryrefslogtreecommitdiff
path: root/hadrian/doc/expressions.md
blob: 1831bcce966a48c50cdf93baec8a0d314cd0184d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# Expressions

`Expr c b a` is a computation that produces a value of type `Action a` and can
read parameters of the current build `Target c b`, but what does that mean
exactly? Here's its definition from `hadrian/src/Hadrian/Expression.hs`:

```haskell
newtype Expr c b a = Expr (ReaderT (Target c b) Action a)
    deriving (Applicative, Functor, Monad)
```

So `Expr c b a` is a `newtype` wrapper around a `ReaderT (Target c b) Action a`.
In practice within Hadrian `c` is always `Context` and `b` is always `Builder`.
The extra parameterisation is there so that hopefully one day the general
functionality of Hadrian (eg. compiling a Haskell library) will be available
to Shake users via a library.

A type synonym from `hadrian/src/Expression/Type.hs` is often used to avoid
writing `Context` and `Builder` everywhere:

```haskell
type Expr a = H.Expr Context Builder a
```

Where `H.Expr` is the `Expr c b a` defined above. The following references to
`Expr` will generally refer to this type synonym unless there is extra
parameterisation.

Let's break down the type a bit, working from the outside in, left to right.

## ReaderT

Put simply, `ReaderT (Target c b) Action a` adds a read-only environment
`Target c b` (in the case of Hadrian: `Target Context Builder`) to values of
type `Action a`. It's the equivalent of threading through a `Target c b`
parameter to all our functions, but we only have to worry about it when we need
it, using `ask :: Monad m => ReaderT r m r` (where `r` is `Target c b` and `m`
is `Action` in this case) or other functions based on it. `ReaderT` and `ask`
are defined in [`Control.Monad.Trans.Reader`](https://hackage.haskell.org/package/transformers-0.5.6.2/docs/Control-Monad-Trans-Reader.html).

So, instead of:

```haskell
foo :: Target Context Builder -> Action ()
foo target = do
  liftIO $ putStrLn "Some message"
  bar target

bar :: Target Context Builder -> Action ()
bar target' = do
  liftIO $ putStrLn "Some other message"
  baz target'

baz :: Target Context Builder -> Action ()
baz target'' = do
  liftIO $ putStrLn "Yet another message"
  liftIO $ print target
```

We can write:

```haskell
foo :: ReaderT (Target Context Builder) Action ()
foo = do
  liftIO $ putStrLn "Some message"
  bar

bar :: ReaderT (Target Context Builder) Action ()
bar = do
  liftIO $ putStrLn "Some other message"
  baz

baz :: ReaderT (Target Context Builder) Action ()
baz = do
  liftIO $ putStrLn "Yet another message"
  target <- ask
  liftIO $ print target
```

And to make those into Hadrian Expressions all we have to do is change the type
and add the constructor:

```haskell
foo :: Expr ()
foo = Expr $ do
  liftIO $ putStrLn "Some message"
  bar

bar :: Expr ()
bar = Expr $ do
  liftIO $ putStrLn "Some other message"
  baz

baz :: Expr ()
baz = Expr $ do
  liftIO $ putStrLn "Yet another message"
  target <- ask
  liftIO $ print target
```

## Target

From `hadrian/src/Hadrian/Target.hs`:

> Each invocation of a builder is fully described by a `Target`, which
> comprises a build context (type variable `c`), a builder (type variable `b`),
> a list of input files and a list of output files. For example:
>
> ```haskell
> preludeTarget = Target (GHC.Context) (GHC.Builder)
>     { context = Context Stage1 base profiling
>     , builder = Ghc Stage1
>     , inputs = ["libraries/base/Prelude.hs"]
>     , outputs = ["build/stage1/libraries/base/Prelude.p_o"] }
> ```

The data type is as follows and is fairly self-explanatory:

```haskell
data Target c b = Target
    { context :: c          -- ^ Current build context
    , builder :: b          -- ^ Builder to be invoked
    , inputs  :: [FilePath] -- ^ Input files for the builder
    , outputs :: [FilePath] -- ^ Files to be produced
    } deriving (Eq, Generic, Show)
```

So we have some `inputs` to our target, some `outputs` that it will produce, a
context for the build (in Hadrian: `Context`), and the builder (in Hadrian:
`Builder`).

### Context

From `hadrian/src/Context/Type.hs`:

```haskell
data Context = Context
    { stage   :: Stage   -- ^ Currently build Stage
    , package :: Package -- ^ Currently build Package
    , way     :: Way     -- ^ Currently build Way (usually 'vanilla')
    } deriving (Eq, Generic, Show)
```

So Context is a data type that stores a Stage, Package, and a Way, i.e. the
context for some particular `Target`.

#### Stage

From `hadrian/src/Stage.hs`:

```haskell
data Stage = Stage0 | Stage1 | Stage2 | Stage3
    deriving (Show, Eq, Ord, Enum, Generic, Bounded)
```

#### Package

From `hadrian/src/Hadrian/Package.hs`:

```haskell
data Package = Package {
    -- | The package type. 'Library' and 'Program' packages are supported.
    pkgType :: PackageType,
    -- | The package name. We assume that all packages have different names,
    -- hence two packages with the same name are considered equal.
    pkgName :: PackageName,
    -- | The path to the package source code relative to the root of the build
    -- system. For example, @libraries/Cabal/Cabal@ and @ghc@ are paths to the
    -- @Cabal@ and @ghc-bin@ packages in GHC.
    pkgPath :: FilePath
    } deriving (Eq, Generic, Ord, Show)
```

`PackageType` is simply defined as:

```haskell
data PackageType = Library | Program deriving (Eq, Generic, Ord, Show)
```

This doesn't quite reflect how Cabal packages are actually structured, as
discussed in https://github.com/snowleopard/hadrian/issues/12, but Hadrian can
still function treating packages as either libraries or programs.

Both `PackageName` and `FilePath` are just type synonyms of `String`.

#### Way

From `hadrian/src/Way/Type.hs`:

```haskell
newtype Way = Way IntSet
```

Where `Way` is a set of enumerated `WayUnit`s wrapped in a `newtype`.

`WayUnit` is defined as:

```haskell
data WayUnit = Threaded
             | Debug
             | Profiling
             | Logging
             | Dynamic
             deriving (Bounded, Enum, Eq, Ord)
```

There are also some helper functions in this module to abstract away this
complexity. For example:

```haskell
import qualified Data.IntSet as Set

wayFromUnits :: [WayUnit] -> Way
wayFromUnits = Way . Set.fromList . map fromEnum
```

`wayFromUnits` converts the `[WayUnit]` into `[Int]` using `map fromEnum`,
creates an `IntSet` from them using `Set.fromList`, and then wraps the `IntSet`
with the `Way` constructor. So we can use `wayFromUnits` to create a `Way` that
builds Hadrian with both multi-threading and profiling by simply writing
`wayFromUnits [Threaded, Profiling]`.

We can also check if a `Way` contains a particular `WayUnit` by using
`wayUnit :: WayUnit -> Way -> Bool`. This is useful if we need to do something
when we're building with a particular `WayUnit`, but not otherwise.

For example, using `getWay :: Expr Context b Way` from `hadrian/src/Context.hs`:

```haskell
foo :: Expr ()
foo = do
  w <- getWay
  if wayUnit Profiling w
    then liftIO $ putStrLn "We're building this target with profiling"
    else liftIO $ putStrLn "We're not building this target with profiling"
```

### Builder

From `hadrian/src/Builder.hs`:

> A `Builder` is a (usually external) command invoked in a separate process
> via `cmd`. Here are some examples:
> * `Alex` is a lexical analyser generator that builds `Lexer.hs` from `Lexer.x`.
> * `Ghc` `Stage0` is the bootstrapping Haskell compiler used in `Stage0`.
> * `Ghc` `StageN` (N > 0) is the GHC built in stage (N - 1) and used in `StageN`.
>
> The `Cabal` builder is unusual in that it does not correspond to an external
> program but instead relies on the Cabal library for package configuration.

The data type itself is simply a long set of constructors that may or may not
be parameterised:

```haskell
data Builder = Alex
             | Ar ArMode Stage
             | Autoreconf FilePath
             | DeriveConstants
             | Cabal ConfigurationInfo Stage
             ...
             | Ghc GhcMode Stage
             ... etc.
             deriving (Eq, Generic, Show)
```

## Action

`Action` comes from Shake, the library underlying Hadrian. It can perform `IO`
using `liftIO` and keeps track of the dependencies for a rule. For more
information on `Action`, see the Shake docs:
https://hackage.haskell.org/package/shake-0.18.3/docs/Development-Shake.html

# Predicates

One useful kind of Hadrian expression is `Predicate`, which is just a type
synonym for `Expr Bool`. These expressions can read from the `Target` and
possibly perform `IO` or any other `Action` to return a `Bool`.

A particularly useful operator for using `Predicate`s is `?`. Its real type and
implementation can be found in `hadrian/src/Hadrian/Expression.hs`, but for the
sake of illustrating how it works in most cases, imagine it's defined like
this:

```haskell
(?) :: Monoid a => Predicate -> Expr a -> Expr a
predicate ? expr = do
  bool <- predicate
  if bool then expr else return mempty
```

If the `Predicate` returns `True`, we return the `Expr` we give it, otherwise
we return `mempty` (which is why we need the `Monoid` type constraint). In fact
thanks to some added type class complexity in the real definition, we can
give `?` a `Bool` instead of a `Predicate` and it works the same way.

To show how we might use `Predicate`s and `?` in practice, say we want to
compile all the Haskell modules in `compiler/` with `-O0` during stage 0. We can
do that by going to `UserSettings.hs` (see
[the user settings docs](user-settings.md)) and changing `userArgs` to:

```haskell
userArgs :: Args
userArgs = package compiler ? builder (Ghc CompileHs stage0) ? arg "-O0"
```

`Args` is just a type synonym for `Expr [String]` and `arg` just lifts a
`String` into an `Args`.

`package :: Package -> Predicate` from `hadrian/src/Expression.hs` takes a
`Package` and returns a `Predicate` that will return `True` if the current
`Target` is part of that package and `False` otherwise. In this case we give
it `compiler` which is defined in `hadrian/src/Packages.hs` along with many
other convenient `Package` definitions.

`builder` comes from `hadrian/src/Expression.hs`:

> This type class allows the user to construct both precise builder
> predicates, such as `builder (Ghc CompileHs Stage1)`, as well as predicates
> covering a set of similar builders. For example, `builder (Ghc CompileHs)`
> matches any stage, and `builder Ghc` matches any stage and any GHC mode.

```haskell
class BuilderPredicate a where
    -- | Is a particular builder being used?
    builder :: a -> Predicate
```

Other useful `Predicate` functions can be found in `hadrian/src/Expression.hs`
and `hadrian/src/Hadrian/Expression.hs`.