You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# stack.yaml versus package.yaml versus a Cabal file
4
4
5
-
Due to their apparent overlap, the purpose of the following three files can be
6
-
unclear:
5
+
What is the difference between a `stack.yaml` file, a `package.yaml` file and a
6
+
Cabal file (named `<package_name>.cabal`)? This page aims to make that clear.
7
7
8
-
*`stack.yaml`
9
-
* A Cabal package file, e.g. `my-package.cabal`
10
-
*`package.yaml`
8
+
In short:
11
9
12
-
The last two are easy to explain: `package.yaml` is a file format supported by
13
-
[Hpack](https://github.com/sol/hpack#readme). It adds some niceties on top of
14
-
Cabal. For example, Hpack has YAML syntax support and will automatically
15
-
generate of `exposed-modules` lists. However, it's just a frontend to Cabal
16
-
package files. So for this document, we're instead going to focus on the first
17
-
two and try to answer:
10
+
*`stack.yaml` contains project-level configuration for Stack, and may contain
11
+
project-specific options and non-project-specific options.
18
12
19
-
_What's the difference between a `stack.yaml` file and a cabal package file?_
13
+
*`package.yaml` contains a description of a package in the
14
+
[Hpack](https://github.com/sol/hpack) format. Hpack, including Stack's
15
+
built-in version, uses the file to create a Cabal file.
16
+
17
+
* a Cabal file also contains a description of a package, but in the format used
18
+
by Cabal.
19
+
20
+
## package.yaml versus a Cabal file
21
+
22
+
Why two different formats to describe packages? Hpack is considered to have some
23
+
advantages over the underlying Cabal format, which are explained its project
24
+
repository. They include that the Hpack format supports YAML syntax and the
25
+
automatic generation of the lists of `exposed-modules` used in the Cabal format.
26
+
27
+
The remainder of this page will focus on the difference between a `stack.yaml`
28
+
file and a package description file.
20
29
21
30
## Package versus project
22
31
23
-
Cabal is a build system, which is used by Stack. Cabal defines the concept of a
24
-
_package_. A package has:
32
+
Stack is a build tool and it uses Cabal, a build system. Cabal defines the
33
+
concept of a _package_. A package has:
25
34
26
35
* A name and version
27
-
*0 or 1 libraries
28
-
*0 or more executables
36
+
*optionally, one library
37
+
*optionally, one or more executables
29
38
* A Cabal file (or, as mentioned above, an [Hpack](https://github.com/sol/hpack)
30
39
`package.yaml` file that generates a Cabal file)
31
40
* And a bunch more
32
41
33
-
The second to last bullet bears repeating: there's a 1-to-1 correspondence between
34
-
packages and cabal files.
42
+
There is a one-to-one correspondence between a package and a Cabal file.
35
43
36
-
Stack is a build tool that works on top of the Cabal build system, and defines
37
-
a new concept called a _project_. A project has:
44
+
Stack defines a new concept called a _project_. A project has:
38
45
39
46
* A _resolver_, which tells it about a snapshot (more on this later)
40
47
* Extra dependencies on top of the snapshot
41
-
*0 or more local Cabal packages
48
+
*Optionally, one or more local Cabal packages
42
49
* Flag and GHC options configurations
43
50
* And a bunch more Stack configuration
44
51
45
-
A source of confusion is that, often, you'll have a project that defines
46
-
exactly one package you're working on, and in that situation it's unclear why,
47
-
for example, you need to specify an extra depedency in both your `stack.yaml`
48
-
_and_ cabal file. To explain, let's take a quick detour to talk about snapshots
52
+
Often you will have a project that defines only one local Cabal package that you
53
+
are working on. If you need to specify an extra dependency, a source of
54
+
confusion can be why you need to specify it both in the `stack.yaml` file _and_
55
+
in the Cabal file. To explain, let's take a quick detour to talk about snapshots
49
56
and how Stack resolves dependencies.
50
57
51
58
## Resolvers and snapshots
52
59
53
-
Stack follows a rule that says, for any projects, there is precisely 1 version
54
-
of each package available. Obviously there are _many_ versions of many
55
-
different packages available in the world. But when resolving a `stack.yaml`
56
-
file, Stack requires that you have chosen a specific version for each package
57
-
available.
60
+
Stack follows a rule that says, for any projects, there is precisely one version
61
+
of each package available. Obviously, for many packages there are _many_
62
+
versions available in the world. But when resolving a `stack.yaml` file, Stack
63
+
requires that you have chosen a specific version for each package available.
58
64
59
65
The most common means by which this set of packages is defined is via a
60
-
Stackage Snapshot. For example, if you go to the page
61
-
<https://www.stackage.org/lts-10.2>, you will see a list of 2,666 packages at
62
-
specific version numbers. When you then specify `resolver: lts-10.2`, you're
66
+
snapshot provided by Stackage. For example, if you go to the page
67
+
<https://www.stackage.org/lts-19.17>, you will see a list of 2,910 packages at
68
+
specific version numbers. When you then specify `resolver: lts-19.17`, you're
63
69
telling Stack to use those package versions in resolving dependencies down to
64
-
concrete version numbers.
70
+
specific versions of packages.
65
71
66
-
Sometimes a snapshot doesn't have all of the packages you want. Or you want a
67
-
different version. Or you want to work on a local modification of a package. In
68
-
all of those cases, you can add more configuration data to your`stack.yaml` to
69
-
override the values it received from your `resolver` setting. At the end of the
70
-
day, each of your projects will end up with some way of resolving a package
71
-
name into a concrete version number.
72
+
Sometimes a snapshot doesn't have all of the packages that you want. Or you want
73
+
a different version of a package. Or you want to work on a local modification of
74
+
a package. In all of those cases, you can add more configuration data to your
75
+
`stack.yaml` file to override the values it received from your `resolver`
76
+
setting. At the end of the day, each of your projects will end up with some way
77
+
of resolving a package name into a specific version of that package.
72
78
73
-
## Why specify deps twice?
79
+
## Why specify dependencies twice?
74
80
75
-
When you add something like this to your `stack.yaml` file:
81
+
The package `acme-missiles` is not included in any Stackage snapshots. When you
82
+
add something like this to your `stack.yaml` file:
76
83
77
-
```yaml
84
+
~~~yaml
78
85
extra-deps:
79
86
- acme-missiles-0.3
80
-
```
87
+
~~~
81
88
82
-
What you're saying to Stack is: if at any point you find that you need to build
83
-
the `acme-missiles` package, please use version `0.3`. You are _not_ saying
89
+
what you're saying to Stack is: "if at any point you find that you need to build
90
+
the `acme-missiles` package, please use version `0.3`". You are _not_ saying
84
91
"please build `acme-missiles` now."You are also not saying "my package depends
85
92
on `acme-missiles`." You are simply making it available should the need arise.
86
93
87
-
When you add `build-depends: acme-missiles` to your cabal file or
88
-
`dependencies: [acme-missiles]` to your `package.yaml` file, you're saying
89
-
"this package requires that `acme-missiles` be available."Since
94
+
When you add to your `package.yaml` file:
95
+
96
+
~~~yaml
97
+
dependencies:
98
+
- acme-missiles
99
+
~~~
100
+
101
+
or, alternatively, you add directly to your Cabal file:
102
+
103
+
~~~yaml
104
+
build-depends: acme-missiles
105
+
~~~
106
+
107
+
you're saying "this package requires that `acme-missiles` be available." Since
90
108
`acme-missiles`doesn't appear in your snapshot, without also modifying your
91
109
`stack.yaml`to mention it via `extra-deps`, Stack will complain about the
92
110
dependency being unavailable.
@@ -98,43 +116,45 @@ somehow, you'd lose reproducibility. How would Stack know which version to use?
98
116
It may elect to use the newest version, but if a new version is available in
99
117
the future, will it automatically switch to that?
100
118
101
-
Stack's baseline philosophy is that build plans are always reproducible\*. The
119
+
Stack's core philosophy is that build plans are always reproducible. The
102
120
purpose of the `stack.yaml` file is to define an immutable set of packages. No
103
121
matter when in time you use it, and no matter how many new release happen in
104
122
the interim, the build plan generated should be the same.
105
123
106
-
\* There's at least one hole in this theory today, which is Hackage revisions.
107
-
When you specify `extra-deps: [acme-missiles-0.3]`, it doesn't specify which
108
-
revision of the cabal file to use, and Stack will just choose the latest. Stack
109
-
version 1.6 added the ability to specify exact revisions of cabal files, but
110
-
this isn't enforced as a requirement as it's so different from the way most
111
-
people work with packages.
124
+
(There is, however, at least one hole in this theory today, which is Hackage
125
+
revisions. When you specify `extra-deps: [acme-missiles-0.3]`, it doesn't
126
+
specify which revision of the Cabal file to use, and Stack will just choose the
127
+
latest. Stack has the ability to specify exact revisions of Cabal files, but
128
+
this isn't enforced as a requirement, because it is so different from the way
129
+
most people work with packages.)
112
130
113
131
And now, how about the other side: why doesn't Stack automatically add
114
-
`acme-missiles`to `build-depends` in your cabal file if you add it as an
115
-
extra-dep? There are a surprising number reasons actually:
132
+
`acme-missiles`to `build-depends` in your Cabal file if you add it as an
133
+
extra-dep? There are a surprising number reasons for this:
116
134
117
-
* The cabal spec doesn't support anything like that
135
+
* The Cabal specification doesn't support anything like that
118
136
* There can be multiple packages in a project, and how do we know which package
119
137
actually needs the dependency?
120
138
* There can be multiple components (libraries, executable, etc) in a package,
121
139
and how do we know which of those actually needs the dependency?
122
-
* The dependency may only be conditionally needed, based on flags, OS, or
123
-
architecture. As an extreme example, we wouldn't want a Linux-only package to
124
-
be force-built on Windows.
140
+
* The dependency may only be conditionally needed, based on flags, operating
141
+
system, or architecture. As an extreme example, we wouldn't want a Linux-only
142
+
package to be built by force on Windows.
125
143
126
144
While for simple use cases it seems like automatically adding dependencies from
127
-
the cabal file to the `stack.yaml` file or vice-versa would be a good thing, it
145
+
the Cabal file to the `stack.yaml` file or vice-versa would be a good thing, it
128
146
breaks down immediately for any semi-difficult case. Therefore, Stack requires
129
147
you to add it to both places.
130
148
131
-
And a final note, in case it wasn't clear. The example I gave above used
149
+
And a final note, in case it wasn't clear. The example above used
132
150
`acme-missiles`, which is not in Stackage snapshots. If, however, you want to
133
151
depend on a package already present in the snapshot you've selected, there's no
134
152
need to add it explicitly to your `stack.yaml` file: it's already there
135
153
implicitly via the `resolver` setting. This is what you do the majority of the
136
154
time, such as when you add `vector` or `mtl` as a `build-depends` value.
137
155
138
-
## Should I checkin generated cabal files?
156
+
## Should I check-in automatically generated Cabal files?
139
157
140
-
Yes, you should. This recommendation was changed in [issue #5210](https://github.com/commercialhaskell/stack/issues/5210), please see the discussion there.
158
+
Yes, you should. This recommendation was changed in
0 commit comments