├── README.md
├── project0
├── LICENSE
├── Main.hs
├── README.md
├── nixpkgs.json
├── project0.cabal
├── project0.nix
├── release0.nix
├── release1.nix
└── release2.nix
├── project1
├── LICENSE
├── Main.hs
├── QuickCheck.nix
├── README.md
├── optparse-applicative-2.nix
├── optparse-applicative.nix
├── project1.cabal
├── project1.nix
├── release0.nix
├── release1.nix
├── release2.nix
├── release2a.nix
├── release3.nix
├── release4.nix
├── release5.nix
├── release6.nix
├── turtle-2.nix
└── turtle.nix
├── project2
├── LICENSE
├── Main.hs
├── README.md
├── cbits
│ └── check.c
├── project2.cabal
├── project2.nix
└── release.nix
├── project3
├── LICENSE
├── Main.hs
├── Project3.hs
├── README.md
├── Test.hs
├── cbits
│ └── check.c
├── project3.cabal
├── project3.nix
├── release0.nix
├── release1.nix
└── release2.nix
└── project4
├── Main.hs
├── README.md
├── nix
├── foldl.nix
├── managed.nix
├── optparse-applicative.nix
├── project4.nix
└── turtle.nix
├── project4.cabal
├── release0.nix
├── release1.nix
├── release2.nix
└── release3.nix
/README.md:
--------------------------------------------------------------------------------
1 | # Nix and Haskell in production
2 |
3 | This guide documents how I use Nix for Haskell development. Feel free to open
4 | issues or pull requests if you would like to contribute or suggest improvements
5 |
6 | The purpose of this project is to support two Haskell workflows:
7 |
8 | * Workflow #1: Nix provisions the development environment
9 | * Nix provides all dependencies and the Haskell toolchain
10 | * You still build the root project using `cabal` ([overview](https://www.haskell.org/cabal/), [user's guide](https://cabal.readthedocs.io/))
11 | * This approach is ideal for development as it supports incremental builds
12 | * Workflow #2: Nix builds the root project for you
13 | * This approach is ideal for continuous integration (especially Hydra)
14 |
15 | The emphasis of this guide is to be as robust as possible and gracefully handle
16 | writing Haskell projects at scale. Some of the suggestions in this guide might
17 | be overkill for a small Haskell project but are essential when managing multiple
18 | private Haskell projects across a team of developers.
19 |
20 | This guide is based partly on
21 | [the Haskell section of the `nixpkgs` manual][nixpkgs-haskell]
22 | and partly on experience using Nix and Haskell in production at
23 | [Awake Security][awake].
24 |
25 | # Background
26 |
27 | Nix is not a `cabal` replacement and Nix actually complements `cabal` quite
28 | well. Nix is much more analogous to a `stack` replacement. `stack` does
29 | provide some support for Nix integration, but this document does not cover that.
30 | Instead, this document describes how to use Nix in conjunction with `cabal` for
31 | Haskell development
32 |
33 | The main benefits of using Nix over `stack` are:
34 |
35 | * Binary caches
36 |
37 | Nix lets you download precompiled Hackage packages whereas `stack` compiles
38 | them on your computer the first time you depend on them
39 |
40 | * Space efficiency
41 |
42 | `stack` creates a copy of each package for each resolver. This means that
43 | if you have two projects with different resolvers then they will not use
44 | the same copy of shared dependencies
45 |
46 | * Generality
47 |
48 | Nix is a language-independent build tool. This means you can use Nix to
49 | also build and customize non-Haskell dependencies (like `gtk`). This
50 | uniform language simplifies build tooling and infrastructure.
51 |
52 | * Larger ecosystem
53 |
54 | Nix provides a large ecosystem of tools that integrate with anything that
55 | Nix can build, such as Hydra (continuous integration), NixOS (an operating
56 | system), and NixOps (a deploy tool)
57 |
58 | * Flexibility
59 |
60 | Nix is a powerful tool in the hands of advanced users. You can make very
61 | deep and sweeping changes to your toolchain, such as recompiling everything
62 | with security hardening
63 |
64 | The main disadvantage of using Nix over `stack` are:
65 |
66 | * Verbosity
67 |
68 | Nix derivations for Haskell projects are significantly more complex than
69 | their corresponding `stack.yaml` files. The `release.nix` files in this
70 | repository are the Nix analog of a `stack.yaml` file and you can see for
71 | yourself the increase in complexity as the examples progress in difficulty.
72 |
73 | * Poor error messages
74 |
75 | Nix is an untyped language with no special Haskell integration, so error
76 | messages are unhelpful
77 |
78 | * Nix cannot incrementally compile Haskell libraries
79 |
80 | Note that you can still use Nix to provision a development environment and
81 | incrementally compile a Haskell package using cabal. However, if you use Nix
82 | to build the package then Nix will build the package from scratch for every
83 | minor change. In theory, this could be fixed to have Nix directly support
84 | incremental Haskell builds but this has not been done yet.
85 |
86 | * Worse user experience
87 |
88 | Nix does not provide many conveniences that `stack` does such as
89 | bootstrapping new projects or "file watch"
90 |
91 | Both Nix and `stack` use curated package sets instead of version bounds for
92 | dependency management. `stack` calls these package sets "resolvers" whereas
93 | Nix calls these package sets "channels". Nix provides stable channels with
94 | names like `NixOS-18.09` (analogous to `stack`'s LTS releases) and then an
95 | unstable channel named `nixpkgs-unstable` (analogous to `stack`'s nightly
96 | releases)
97 |
98 | # Related guides
99 |
100 | * [Nix Haskell Monorepo Tutorial](https://github.com/fghibellini/nix-haskell-monorepo) -
101 | Guide on how to scale Nix development to a larger repository containing all of
102 | a company's internally-developed Haskell packages
103 |
104 | # Related tools
105 |
106 | Before continuing, I'd like to mention some other tools for mixing Haskell with
107 | Nix:
108 |
109 | * [`tinc`](https://github.com/sol/tinc/blob/nixpkgs/NIX.md) - this uses
110 | `cabal`'s solver to select which Haskell packages to use instead of the
111 | curated Haskell package set from `nixpkgs`
112 | * [`styx`](https://github.com/jyp/styx) - This tool provides a `stack`-like
113 | interface to managing Haskell dependencies using Nix
114 | * [`haskell-overridez`](https://github.com/adetokunbo/haskell-overridez) -
115 | Tool that automates dependency management as described in this guide
116 |
117 | # Setup
118 |
119 | Before you begin, you must install Nix if you haven't already:
120 |
121 | ```bash
122 | $ curl -L https://nixos.org/nix/install | sh
123 | ```
124 |
125 | You must also install `cabal2nix` and `nix-prefetch-git`:
126 |
127 | ```bash
128 | $ nix-env --install cabal2nix
129 | $ nix-env --install nix-prefetch-git
130 | ```
131 |
132 | You also need to install `cabal` if you haven't done so already. You can either
133 | use your installed `cabal` or you can use `nix` to install `cabal` for you:
134 |
135 | ```bash
136 | $ nix-env --install cabal-install
137 | ```
138 |
139 | Make sure that you have a fairly recent version of `cabal` installed since these
140 | examples will use GHC 8 which requires version 1.24 or later of `cabal`. You
141 | can check what version you have installed by running:
142 |
143 | ```bash
144 | $ cabal --version
145 | ```
146 |
147 | Finally, run `cabal update` if you haven't done so already
148 |
149 | # Organization
150 |
151 | This tutorial is split into several tutorial projects in the `project*/`
152 | subdirectories. Read the `README.md` file in each subdirectory in
153 | order to follow the tutorial:
154 |
155 | * [Project 0 - Nix basics][proj0]
156 | * [Project 1 - Dependency management][proj1]
157 | * [Project 2 - Non-Haskell dependencies][proj2]
158 | * [Project 3 - Customizing Haskell projects ][proj3]
159 | * [Project 4 - Advanced dependency management][proj4]
160 |
161 | [awake]: https://awakesecurity.com/
162 | [nixpkgs-haskell]: https://nixos.org/nixpkgs/manual/#haskell
163 | [proj0]: ./project0/README.md
164 | [proj1]: ./project1/README.md
165 | [proj2]: ./project2/README.md
166 | [proj3]: ./project3/README.md
167 | [proj4]: ./project4/README.md
168 |
--------------------------------------------------------------------------------
/project0/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Gabriella Gonzalez
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright notice,
7 | this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright notice,
9 | this list of conditions and the following disclaimer in the documentation
10 | and/or other materials provided with the distribution.
11 | * Neither the name of Gabriella Gonzalez nor the names of other contributors
12 | may be used to endorse or promote products derived from this software
13 | without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/project0/Main.hs:
--------------------------------------------------------------------------------
1 | module Main where
2 |
3 | main :: IO ()
4 | main = putStrLn "Hello, world!"
5 |
--------------------------------------------------------------------------------
/project0/README.md:
--------------------------------------------------------------------------------
1 | # Building with `nix`
2 |
3 |
4 |
5 | This directory contains the simplest possible Haskell project and the simplest
6 | possible Nix derivation to build that project. You can think of a derivation
7 | as a language-agnostic recipe for how to build something (such as a Haskell
8 | package).
9 |
10 | You can build this Haskell package by running:
11 |
12 | ```bash
13 | $ nix-build release0.nix
14 | these derivations will be built:
15 | /nix/store/3l7f5v3aibz9rnxxafm6afvihjc04aiq-project0-1.0.0.drv
16 | building path(s) ‘/nix/store/x28vx2rfnffl1clmxn5054bxwqyln2j0-project0-1.0.0’
17 | ...
18 | Configuring project0-1.0.0...
19 | Dependency base <5: using base-4.9.0.0
20 | ...
21 | Building project0-1.0.0...
22 | Preprocessing executable 'project0' for project0-1.0.0...
23 | [1 of 1] Compiling Main ( Main.hs, dist/build/project0/project0-tmp/Main.dyn_o )
24 | Linking dist/build/project0/project0 ...
25 | ...
26 | Installing executable(s) in
27 | /nix/store/x28vx2rfnffl1clmxn5054bxwqyln2j0-project0-1.0.0/bin
28 | ...
29 | /nix/store/x28vx2rfnffl1clmxn5054bxwqyln2j0-project0-1.0.0
30 | ```
31 |
32 | I've highlighted the parts that matter the most for our simple example. Don't
33 | expect the hashed paths in the above example output to necessarily match the
34 | ones you get (See below for more details about why they might differ).
35 |
36 | The `project0.cabal` file only specifies a single dependency of `base < 5` and
37 | Nix picks the latest [`base` package](http://hackage.haskell.org/package/base)
38 | below version 5 (in this case, [`base-4.9.0.0`](http://hackage.haskell.org/package/base))
39 | to satisfy that dependency. Nix then builds the project, stores the build
40 | output in `/nix/store/x28vx2rfnffl1clmxn5054bxwqyln2j0-project0-1.0.0` and
41 | creates a symlink in the current directory named `result` pointing to that
42 | directory in the `/nix/store`:
43 |
44 | ```bash
45 | $ readlink result
46 | /nix/store/x28vx2rfnffl1clmxn5054bxwqyln2j0-project0-1.0.0
47 | ```
48 |
49 | Right now the contents of that directory are just an executable file and an
50 | empty library directory:
51 |
52 | ```bash
53 | $ tree result
54 | result
55 | ├── bin
56 | │ └── project0
57 | └── lib
58 | └── links
59 |
60 | 3 directories, 1 file
61 | ```
62 |
63 | As the project grows more complex we'll see additional build outputs in this
64 | directory.
65 |
66 | We can run the executable stored in the `result/bin` subdirectory:
67 |
68 | ```bash
69 | $ result/bin/project0
70 | Hello, world!
71 | ```
72 |
73 | This output makes sense since our `Main.hs` file is just a simple
74 | "Hello, world!" program:
75 |
76 | ```haskell
77 | module Main where
78 |
79 | main :: IO ()
80 | main = putStrLn "Hello, world!"
81 | ```
82 |
83 | What happens if we try to build the project again?
84 |
85 | ```bash
86 | $ nix-build release0.nix
87 | these derivations will be built:
88 | /nix/store/vvw0v8ys7dadck747vj48vb0jgs7isqm-project0-1.0.0.drv
89 | building path(s) ‘/nix/store/ysrqcpdl51jaa4gqzx1xmb7m0h05rdwq-project0-1.0.0’
90 | setupCompilerEnvironmentPhase
91 | ...
92 | [1 of 1] Compiling Main ( Main.hs, dist/build/project0/project0-tmp/Main.dyn_o )
93 | Linking dist/build/project0/project0 ...
94 | ...
95 | /nix/store/ysrqcpdl51jaa4gqzx1xmb7m0h05rdwq-project0-1.0.0
96 | ```
97 |
98 | This might seem odd at first since you'd expect Nix to reuse the cached result
99 | from our first build. However, Nix does not reuse the first build because our
100 | project subtly changed: our `nix-build` deposited a `result` symlink which was
101 | not there before! Our Nix derivation depends on the current project directory,
102 | so if anything in the current directory changes then Nix performs a complete
103 | rebuild of our project.
104 |
105 | We can verify this by removing the symlink and then performing the build again:
106 |
107 | ```bash
108 | $ rm result
109 | $ nix-build release0.nix
110 | /nix/store/x28vx2rfnffl1clmxn5054bxwqyln2j0-project0-1.0.0
111 | ```
112 |
113 | This time we get a cache hit and reuse the first build since our directory is
114 | now bit-for-bit identical to when we first ran `nix-build`.
115 |
116 | These wasteful rebuilds are one reason that I don't recommend using
117 | `nix-build` to build the root Haskell project. Instead, the next section
118 | describes how to use `cabal` with Nix to avoid the issue of wasteful rebuilds.
119 |
120 | # Creating your own project
121 |
122 | If you ever need [to bootstrap your own project using `cabal init`](https://cabal.readthedocs.io/en/latest/developing-packages.html#quickstart),
123 | then run:
124 |
125 | ```bash
126 | $ nix-shell --packages ghc --run 'cabal init'
127 | ```
128 |
129 | `cabal init` requires `ghc` to be on the executable search path, but we do not
130 | plan to install GHC globally. Instead, we can use a `nix-shell` to transiently
131 | provide `ghc` just for the duration of a `cabal init` command. Later on we
132 | rely on the project's Nix configuration to provide the desired GHC for
133 | project development.
134 |
135 | # Building with `cabal`
136 |
137 | You can open up a development environment for this project inside of a "Nix
138 | shell" by running:
139 |
140 | ```bash
141 | $ nix-shell --attr env release0.nix
142 | ```
143 |
144 | Normally `nix-shell` wouldn't require the `--attr` flag since `nix-shell` is
145 | designed to automatically compute the necessary development environment from
146 | the original derivation. However, Haskell derivations are different and
147 | `nix-shell` doesn't work out of the box on them.
148 |
149 | Haskell derivations are records that include an `env` field which `nix-shell`
150 | can use to compute the correct development environment. In Nix a field is
151 | called an "attribute" so we pass the `--attr env` flag to specify that
152 | `nix-shell` should compute the development environment from the derivation
153 | record's `env` "attribute".
154 |
155 | Once we open up the development environment we can use `cabal` to build and run
156 | the `project0` executable:
157 |
158 | ```bash
159 | $ cabal configure
160 | Resolving dependencies...
161 | Configuring project0-1.0.0...
162 | $ cabal run project0
163 | Preprocessing executable 'project0' for project0-1.0.0...
164 | [1 of 1] Compiling Main ( Main.hs, dist/build/project0/project0-tmp/Main.o )
165 | Linking dist/build/project0/project0 ...
166 | Running project0...
167 | Hello, world!
168 | ```
169 |
170 | You might get the following warning if you are using `cabal-install` version
171 | `2.*`:
172 |
173 | ```
174 | Warning: The configure command is a part of the legacy v1 style of cabal
175 | usage.
176 |
177 | Please switch to using either the new project style and the new-configure
178 | command or the legacy v1-configure alias as new-style projects will become the
179 | default in the next version of cabal-install. Please file a bug if you cannot
180 | replicate a working v1- use case with the new-style commands.
181 |
182 | For more information, see: https://wiki.haskell.org/Cabal/NewBuild
183 | ```
184 |
185 | If you get that warning, use the corresponding `cabal v1-*` command (e.g.
186 | `cabal v1-build`) in the interim and see
187 | [this issue](https://github.com/haskell/cabal/issues/4646) for details about
188 | Cabal's "new-build" support for Nix.
189 |
190 | Unlike Nix, `cabal` will be smart and won't wastefully rebuild things that
191 | haven't changed. This means we can safely re-run `cabal` without rebuilding the
192 | entire project from scratch:
193 |
194 | ```bash
195 | $ cabal run project0
196 | Up to date
197 | Hello, world!
198 | ```
199 |
200 | You can exit from the Nix shell using the `exit` command or typing `Ctrl-D`.
201 |
202 | This Nix shell provides all necessary dependencies for your project and the
203 | Haskell toolchain *except for `cabal`*. For example, inside the Nix shell you
204 | will see that you are using a `ghc` provided by Nix, regardless of whether or
205 | not you have a global `ghc` installed:
206 |
207 | ```bash
208 | $ which ghc # The exact hash in the path might differ
209 | /nix/store/dg7ak1hvlj66vgn4fwvddnnr4pfncd04-ghc-8.0.1/bin/ghc
210 | ```
211 |
212 | The `cabal configure` step automatically picks up the `ghc` tool-chain and
213 | package database provisioned by Nix and uses them for all subsequent `cabal`
214 | commands.
215 |
216 | The `nixpkgs` manual notes that if you only have Haskell dependencies you
217 | can also just run the following command once:
218 |
219 | ```
220 | $ nix-shell --attr env release0.nix --run 'cabal configure'
221 | ```
222 |
223 | ... and then run all the other `cabal` commands without the Nix shell. However,
224 | if you have non-Haskell dependencies then this won't work. When in doubt, just
225 | get used to development inside of a Nix shell since that habit will translate
226 | well to non-Haskell projects managed by Nix.
227 |
228 | **NOTE:** I recommend using `cabal` to build the root project during Haskell
229 | package development, but subsequent examples will still use `nix-build` to
230 | keep the examples short.
231 |
232 | # Nix derivations
233 |
234 | The `release0.nix` file specifies how to build the project using Nix:
235 |
236 | ```nix
237 | let
238 | pkgs = import { };
239 |
240 | in
241 | pkgs.haskellPackages.callPackage ./project0.nix { }
242 | ```
243 |
244 | I don't recommend reusing the above derivation for your Haskell projects. There
245 | are several ways that we can improve upon this derivation that we'll address in
246 | later examples.
247 |
248 | This derivation begins by importing `nixpkgs`, which is a Nix channel. You can
249 | find all the officially supported Nix channels here:
250 |
251 | * [Nix channels][channels]
252 |
253 | The default Nix installation will subscribe you some channel (i.e. release) of
254 | Nixpkgs (a package repository for Nix).
255 |
256 | You can tell which release of Nixpkgs you are using by running this command:
257 |
258 | ```bash
259 | $ nix-instantiate --eval --expr 'builtins.readFile '
260 | "18.03\n"
261 | ```
262 |
263 | ... and you can also obtain the exact git revision of the Nixpkgs repository
264 | that the channel was cut from using this command:
265 |
266 | ```bash
267 | $ nix-instantiate --eval --expr 'builtins.readFile '
268 | "411cc559c052feb6e20a01fc6d5fa63cba09ce9a"
269 | ```
270 |
271 | You should probably use the default channel selected for you. If you are using
272 | a Linux operating system other than NixOS, you can safely change to a stable
273 | channel if you prefer by running:
274 |
275 | ```bash
276 | $ nix-channel --add https://nixos.org/channels/nixos-18.09-small nixpkgs
277 | $ nix-channel --update nixpkgs
278 | ```
279 |
280 | ... replacing `18.09` with whatever stable release version you wish to use.
281 |
282 | However, you should be very careful about using a stable release on OS X because
283 | the public binary cache only caches OS X build products for the unstable
284 | channel. If you try to use a stable channel on OS X you run a very high risk of
285 | compiling things from scratch (including `ghc`).
286 |
287 | # Pinning `nixpkgs`
288 |
289 | Even "stable" channels are still not frozen. Stable channels are like major
290 | releases and they periodically receive minor releases for security patches, bug
291 | fixes, and new packages that successfully build. However, you have to
292 | specifically opt in to channel updates by running
293 | `nix-channel --update nixpkgs`. If you do nothing then your channel will
294 | remain frozen at whatever minor release you downloaded when you first installed
295 | Nix. When you do upgrade your channel you can only upgrade to the latest minor
296 | release.
297 |
298 | Nix's channel mechanism works okay for personal or open source development,
299 | but does not work well in a corporate environment, since you can't easily
300 | ensure that every person or deployment is on the exact same minor release.
301 |
302 | In a corporate environment, you can pin `nixpkgs` to a specific `git` revision
303 | as illustrated in `release1.nix`:
304 |
305 | ```nix
306 | let
307 | bootstrap = import { };
308 |
309 | nixpkgs = builtins.fromJSON (builtins.readFile ./nixpkgs.json);
310 |
311 | src = bootstrap.fetchFromGitHub {
312 | owner = "NixOS";
313 | repo = "nixpkgs";
314 | inherit (nixpkgs) rev sha256;
315 | };
316 |
317 | pkgs = import src { };
318 |
319 | in
320 | pkgs.haskellPackages.callPackage ./project0.nix { }
321 | ```
322 |
323 | ... where `nixpkgs.json` was generated using the `nix-prefetch-git` tool:
324 |
325 | ```bash
326 | $ nix-prefetch-git https://github.com/NixOS/nixpkgs.git 2c288548b93b657365c27a0132a43ba0080870cc > nixpkgs.json
327 | $ cat nixpkgs.json
328 | {
329 | "url": "https://github.com/NixOS/nixpkgs.git",
330 | "rev": "2c288548b93b657365c27a0132a43ba0080870cc",
331 | "date": "2017-01-02T00:10:04+01:00",
332 | "sha256": "1a365am90a1zy99k4qwddj8s3bdlyfisrsq4a3r00kghjcz89zld"
333 | }
334 | ```
335 |
336 | Replace `2c288548b93b657365c27a0132a43ba0080870cc` with the `git` revision that
337 | you want to pin `nixpkgs` to. You can also omit the revision to pin to the
338 | current `master`.
339 |
340 | However, if you choose to go this route then you will need to set up an
341 | internal Hydra server to build and cache your project.
342 |
343 | Without an internal cache your developers will likely need to build these tools
344 | from scratch whenever your pinned `nixpkgs` drifts too far from the publicly
345 | cached channels. `ghc` in particular is very expensive to rebuild.
346 |
347 | This guide does not (yet) cover how to set up an internal Hydra server for this
348 | purpose, but may do so in a future draft. Until then, the remaining examples
349 | will not use a pinned `nixpkgs` for simplicity.
350 |
351 | [channels]: https://nixos.org/channels/
352 |
353 | # `cabal2nix`
354 |
355 | The second half of our `release0.nix` derivation contains the instructions to
356 | build our project:
357 |
358 | ```nix
359 | let
360 | pkgs = import { };
361 |
362 | in
363 | pkgs.haskellPackages.callPackage ./project0.nix { }
364 | ```
365 |
366 | This references another file in this same project called `project0.nix`. This
367 | file was generated using `cabal2nix` by running:
368 |
369 | ```bash
370 | $ cabal2nix . > project0.nix
371 | ```
372 |
373 | ... and the generated `project0.nix` file for this project is:
374 |
375 | ```nix
376 | { mkDerivation, base, lib }:
377 | mkDerivation {
378 | pname = "project0";
379 | version = "1.0.0";
380 | src = ./.;
381 | isLibrary = false;
382 | isExecutable = true;
383 | executableHaskellDepends = [ base ];
384 | license = lib.licenses.bsd3;
385 | }
386 | ```
387 |
388 | All that `cabal2nix` does is translate our `project0.cabal` file into a
389 | corresponding Nix expression. For comparison, here is the original
390 | `project0.cabal` file that `project0.nix` was generated from:
391 |
392 | ```cabal
393 | name: project0
394 | version: 1.0.0
395 | license: BSD3
396 | license-file: LICENSE
397 | cabal-version: >= 1.18
398 | build-type: Simple
399 |
400 | executable project0
401 | build-depends: base < 5
402 | main-is: Main.hs
403 | default-language: Haskell2010
404 | ```
405 |
406 | Any time you update a Haskell project's `cabal` file you need to regenerate the
407 | `project0.nix` file using `cabal2nix`.
408 |
409 | # Hydra compatibility
410 |
411 | `release2.nix` illustrates the next improvement we can make to our project's
412 | Nix derivation:
413 |
414 | ```nix
415 | let
416 | pkgs = import { };
417 |
418 | in
419 | { project0 = pkgs.haskellPackages.callPackage ./project0.nix { };
420 | }
421 | ```
422 |
423 | The only difference is that now our file returns a "set" (the Nix term for a
424 | dictionary) of derivations. This "set" only has one "attribute" (i.e. key)
425 | named `project0` whose value is the derivation to build our Haskell project.
426 |
427 | The main motivation for this change is that Hydra (Nix's continuous integration
428 | server) requires that project build files are sets of derivations with one
429 | attribute per build product. If you try to build a naked derivation with Hydra
430 | you will get weird errors.
431 |
432 | A second lesser reason for this change is that this makes it easy to add
433 | additional build products to your project. This comes in handy when you want to
434 | build and test dependencies or extra tools that you rely on.
435 |
436 | You can still build the project using `nix-build` either by specifying to build
437 | all derivations in the set:
438 |
439 | ```bash
440 | $ nix-build release2.nix
441 | ```
442 |
443 | ... or by specifying the attribute of the derivation you want to build using
444 | the same `--attr` flag we introduced before for `nix-shell`:
445 |
446 | ```bash
447 | $ nix-build --attr project0 release2.nix
448 | ```
449 |
450 | This `--attr` flag specifies that we only want to build the `project0` field
451 | of the record, and this flag comes in handy once the record has more than one
452 | field.
453 |
454 | You can also still open up a Nix shell, but you need to change the attribute you
455 | pass on the command line from `env` to `project0.env`:
456 |
457 | ```bash
458 | $ nix-shell --attr project0.env release2.nix
459 | ```
460 |
461 | Like before, `nix-shell` and `nix-build` take slightly different attributes:
462 | we specify the `project0` attribute when using `nix-build` and the
463 | `project0.env` attribute when using `nix-shell`.
464 |
465 | You can also avoid having to type this every time you initialize the project by
466 | creating the following `shell.nix` file:
467 |
468 | ```nix
469 | (import ./release2.nix).project0.env
470 | ```
471 |
472 | ... replacing `release2.nix` with the name of your project's derivation file.
473 | Then you can just type:
474 |
475 | ```bash
476 | $ nix-shell
477 | ```
478 |
479 | ... and that will automatically use the contents of `shell.nix`
480 |
481 | Note that `cabal2nix` provides a `--shell` option to generate a `shell.nix`
482 | file suitable for the current project. However, this does not play nice with
483 | advanced dependency management (covered in the next section) so I do not
484 | recommend this approach in general.
485 |
486 | # Conclusion
487 |
488 | That concludes Nix workflow basics for Haskell development. The
489 | [next section](../project1/README.md) covers dependency management.
490 |
--------------------------------------------------------------------------------
/project0/nixpkgs.json:
--------------------------------------------------------------------------------
1 | {
2 | "url": "https://github.com/NixOS/nixpkgs.git",
3 | "rev": "2c288548b93b657365c27a0132a43ba0080870cc",
4 | "date": "2017-01-02T00:10:04+01:00",
5 | "sha256": "1a365am90a1zy99k4qwddj8s3bdlyfisrsq4a3r00kghjcz89zld"
6 | }
7 |
--------------------------------------------------------------------------------
/project0/project0.cabal:
--------------------------------------------------------------------------------
1 | name: project0
2 | version: 1.0.0
3 | license: BSD3
4 | license-file: LICENSE
5 | cabal-version: >= 1.18
6 | build-type: Simple
7 |
8 | executable project0
9 | build-depends: base < 5
10 | main-is: Main.hs
11 | default-language: Haskell2010
12 |
--------------------------------------------------------------------------------
/project0/project0.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, lib }:
2 | mkDerivation {
3 | pname = "project0";
4 | version = "1.0.0";
5 | src = ./.;
6 | isLibrary = false;
7 | isExecutable = true;
8 | executableHaskellDepends = [ base ];
9 | license = lib.licenses.bsd3;
10 | }
11 |
--------------------------------------------------------------------------------
/project0/release0.nix:
--------------------------------------------------------------------------------
1 | let
2 | pkgs = import { };
3 |
4 | in
5 | pkgs.haskellPackages.callPackage ./project0.nix { }
6 |
--------------------------------------------------------------------------------
/project0/release1.nix:
--------------------------------------------------------------------------------
1 | let
2 | bootstrap = import { };
3 |
4 | nixpkgs = builtins.fromJSON (builtins.readFile ./nixpkgs.json);
5 |
6 | src = bootstrap.fetchFromGitHub {
7 | owner = "NixOS";
8 | repo = "nixpkgs";
9 | inherit (nixpkgs) rev sha256;
10 | };
11 |
12 | pkgs = import src { };
13 |
14 | in
15 | pkgs.haskellPackages.callPackage ./project0.nix { }
16 |
--------------------------------------------------------------------------------
/project0/release2.nix:
--------------------------------------------------------------------------------
1 | let
2 | pkgs = import { };
3 |
4 | in
5 | { project0 = pkgs.haskellPackages.callPackage ./project0.nix { };
6 | }
7 |
--------------------------------------------------------------------------------
/project1/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Gabriella Gonzalez
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright notice,
7 | this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright notice,
9 | this list of conditions and the following disclaimer in the documentation
10 | and/or other materials provided with the distribution.
11 | * Neither the name of Gabriella Gonzalez nor the names of other contributors
12 | may be used to endorse or promote products derived from this software
13 | without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/project1/Main.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 |
3 | module Main where
4 |
5 | import Turtle
6 |
7 | main :: IO ()
8 | main = echo "Hello, world!"
9 |
--------------------------------------------------------------------------------
/project1/QuickCheck.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, containers, random, lib, template-haskell
2 | , test-framework, tf-random, transformers
3 | }:
4 | mkDerivation {
5 | pname = "QuickCheck";
6 | version = "2.8.2";
7 | sha256 = "1ai6k5v0bibaxq8xffcblc6rwmmk6gf8vjyd9p2h3y6vwbhlvilq";
8 | libraryHaskellDepends = [
9 | base containers random template-haskell tf-random transformers
10 | ];
11 | testHaskellDepends = [
12 | base containers template-haskell test-framework
13 | ];
14 | homepage = "https://github.com/nick8325/quickcheck";
15 | description = "Automatic testing of Haskell programs";
16 | license = lib.licenses.bsd3;
17 | }
18 |
--------------------------------------------------------------------------------
/project1/README.md:
--------------------------------------------------------------------------------
1 | # Dependencies
2 |
3 | This project now depends on the `turtle` package in order to illustrate some
4 | dependency management basics. The `Main.hs` executable is a contrived program
5 | that uses `turtle` gratuitously for the `echo` function:
6 |
7 | ```haskell
8 | {-# LANGUAGE OverloadedStrings #-}
9 |
10 | module Main where
11 |
12 | import Turtle
13 |
14 | main :: IO ()
15 | main = echo "Hello, world!"
16 | ```
17 |
18 | The `release0.nix` file is the same as for the previous project except that we
19 | now name our package `project1` instead of `project0`, and we've changed the
20 | name of the corresponding nix file to `project1.nix` rather than `project0.nix`:
21 |
22 | ```nix
23 | let
24 | pkgs = import { };
25 |
26 | in
27 | { project1 = pkgs.haskellPackages.callPackage ./project1.nix { };
28 | }
29 | ```
30 |
31 | ... the contents of our `project1.nix` file have changed as well. This is because our
32 | `project1.cabal` file now has a new `turtle` dependency:
33 |
34 | ```cabal
35 | name: project1
36 | version: 1.0.0
37 | license: BSD3
38 | license-file: LICENSE
39 | cabal-version: >= 1.18
40 | build-type: Simple
41 |
42 | executable project1
43 | build-depends: base < 5, turtle
44 | main-is: Main.hs
45 | default-language: Haskell2010
46 | ```
47 |
48 | We'll see the corresponding change in the `project1.nix` generated by
49 | `cabal2nix`:
50 |
51 | ```nix
52 | { mkDerivation, base, lib, turtle }:
53 | mkDerivation {
54 | pname = "project1";
55 | version = "1.0.0";
56 | src = ./.;
57 | isLibrary = false;
58 | isExecutable = true;
59 | executableHaskellDepends = [ base turtle ];
60 | license = lib.licenses.bsd3;
61 | }
62 | ```
63 |
64 | Notice how neither file specifies what version of `turtle` to depend on. This
65 | is because Nix resembles `stack` and provides a curated package set of
66 | Haskell packages that build together. If you don't specify a version then Nix
67 | will pick a version for you.
68 |
69 | You can find the latest curated package set here:
70 |
71 | * [Curated Hackage package set][hackage-packages]
72 |
73 | ... which corresponds roughly to the `nixpkgs-unstable` release. If you would
74 | like to see what package versions Nix selects for a stable release such as
75 | `nixos-18.09`, then change the branch name in the URL from `master` to
76 | `release-18.09`, like this:
77 |
78 | * [Curated Hackage package set for NixOS-18.09][hackage-packages-18.09]
79 |
80 | These curated package sets correspond roughly to Stackage resolvers. Stable
81 | releases like `nixos-18.09` correspond to Stackage LTS resolvers, and
82 | the `nixpkgs-unstable` release corresponds roughly to a Stackage nightly
83 | resolver. The main difference is that Nix's package set curation extends beyond
84 | Haskell packages: `nixpkgs` also curates non-Haskell dependencies, too.
85 |
86 | We can also see which version our project selects if we build our project
87 | using `nix-build`:
88 |
89 | ```bash
90 | $ nix-build --attr project1 release0.nix
91 | these derivations will be built:
92 | /nix/store/8g54hjpim7l9s41c9wpcn1h2q8m254m5-project1-1.0.0.drv
93 | building path(s) ‘/nix/store/pi47yvw46xv346brajyrblwqhjmglhaj-project1-1.0.0’
94 | ...
95 | Configuring project1-1.0.0...
96 | Dependency base <5: using base-4.9.0.0
97 | Dependency turtle -any: using turtle-1.2.8
98 | ...
99 | /nix/store/pi47yvw46xv346brajyrblwqhjmglhaj-project1-1.0.0
100 | ```
101 |
102 | The log output from `nix-build` notes that `turtle-1.2.8` was chosen for this
103 | build. Your results might vary depending on which version of the `nixpkgs`
104 | channel that you have installed.
105 |
106 | Note that `nixpkgs` only curates the Haskell package set to build against the
107 | default version of the GHC compiler. You can display the default compiler version
108 | by running the following command:
109 |
110 | ```shell
111 | $ nix-instantiate --eval --expr '(import { }).ghc.version'
112 | ```
113 |
114 | If you try to change the compiler (as described below) you may need to modify the
115 | Haskell package set to get them to build correctly.
116 |
117 | # Changing versions
118 |
119 | Suppose that we want to build against `turtle-1.3.0` for whatever reason. This
120 | requires a much larger change to our project derivation which we can see in
121 | `release1.nix`:
122 |
123 | ```nix
124 | # Note: This should fail to build
125 | let
126 | config = {
127 | packageOverrides = pkgs: rec {
128 | haskellPackages = pkgs.haskellPackages.override {
129 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
130 | project1 =
131 | haskellPackagesNew.callPackage ./project1.nix { };
132 |
133 | turtle =
134 | haskellPackagesNew.callPackage ./turtle.nix { };
135 | };
136 | };
137 | };
138 | };
139 |
140 | pkgs = import { inherit config; };
141 |
142 | in
143 | { project1 = pkgs.haskellPackages.project1;
144 | }
145 | ```
146 |
147 | By default, `import { }` will implicitly use an empty or user-defined
148 | configuration in `~/.nixpkgs/config.nix` or `$NIXPKGS_CONFIG`. The above
149 | configuration overrides that behavior with an explicit `config` defined within
150 | the same file.
151 |
152 | This new derivation now references a `turtle.nix` file generated from
153 | `cabal2nix` by running:
154 |
155 | ```bash
156 | $ cabal2nix cabal://turtle-1.3.2 > turtle.nix
157 | ```
158 |
159 | If you try to run that and the command fails with this error message:
160 |
161 | ```bash
162 | cabal2nix: ~/.cabal/packages/hackage.haskell.org/00-index.tar: openBinaryFile: does not exist (No such file or directory)
163 | ```
164 |
165 | ... then run `cabal update` and then the error should disappear.
166 |
167 | The generated `turtle.nix` file looks like this:
168 |
169 | ```nix
170 | { mkDerivation, ansi-wl-pprint, async, base, bytestring, clock
171 | , directory, doctest, foldl, hostname, managed, optional-args
172 | , optparse-applicative, process, lib, stm, system-fileio
173 | , system-filepath, temporary, text, time, transformers, unix
174 | , unix-compat
175 | }:
176 | mkDerivation {
177 | pname = "turtle";
178 | version = "1.3.2";
179 | sha256 = "0pbvkqqhiaddyhlqcrk48w7li81dijw92wwhchwqh1my1363n5pq";
180 | libraryHaskellDepends = [
181 | ansi-wl-pprint async base bytestring clock directory foldl hostname
182 | managed optional-args optparse-applicative process stm
183 | system-fileio system-filepath temporary text time transformers unix
184 | unix-compat
185 | ];
186 | testHaskellDepends = [ base doctest ];
187 | description = "Shell programming, Haskell-style";
188 | license = lib.licenses.bsd3;
189 | }
190 | ```
191 |
192 | `nixpkgs` uses a `callPackage` utility function to "tie the knot" when updating
193 | dependencies. When we change`turtle` this way every package that depends on
194 | `turtle` (including our `project1` package) will pick up this new version of
195 | `turtle`.
196 |
197 | The `nixpkgs` manual suggests an alternative approach of specifying package
198 | overrides in a shared `~/.nixpkgs/config.nix` configuration file. However, I do
199 | not recommend this approach: project build instructions should be checked into
200 | version control alongside the project so that they stay in sync with the
201 | project.
202 |
203 | At the time of this writing, if you try to build `release1.nix` then you will
204 | get the following build error:
205 |
206 | ```bash
207 | $ nix-build --attr project1 release1.nix
208 | these derivations will be built:
209 | /nix/store/r780xwf197a2gxn3008raq5k6xxid8mh-turtle-1.3.0.drv
210 | /nix/store/y6g5ya8lis6250fcj0mrw1kybjdara9i-project1-1.0.0.drv
211 | building path(s) ‘/nix/store/qmyqayhvy4rxhfkxpdvc1ayc1vyp8nmw-turtle-1.3.0’
212 | ...
213 | Configuring turtle-1.3.0...
214 | Setup: Encountered missing dependencies:
215 | optparse-applicative ==0.13.*
216 | builder for ‘/nix/store/r780xwf197a2gxn3008raq5k6xxid8mh-turtle-1.3.0.drv’ failed with exit code 1
217 | cannot build derivation ‘/nix/store/y6g5ya8lis6250fcj0mrw1kybjdara9i-project1-1.0.0.drv’: 1 dependencies couldn't be built
218 | error: build of ‘/nix/store/y6g5ya8lis6250fcj0mrw1kybjdara9i-project1-1.0.0.drv’ failed
219 | ```
220 |
221 | This error indicates that we can't upgrade `turtle-1.3.0` alone because
222 | `turtle-1.3.0` depends on `optparse-applicative-0.13.*` and the default version
223 | of `optparse-applicative` that Nix selects is not in this range. At the
224 | time of this writing, Nix picks `optparse-applicative-0.12.1.0` as the default
225 | version.
226 |
227 | We can override the `optparse-applicative` version using the exact same trick
228 | and `release2.nix` contains this additional override:
229 |
230 | ```nix
231 | # Note: This should also fail to build
232 | let
233 | config = {
234 | packageOverrides = pkgs: rec {
235 | haskellPackages = pkgs.haskellPackages.override {
236 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
237 | optparse-applicative =
238 | haskellPackagesNew.callPackage ./optparse-applicative.nix { };
239 |
240 | project1 =
241 | haskellPackagesNew.callPackage ./project1.nix { };
242 |
243 | turtle =
244 | haskellPackagesNew.callPackage ./turtle.nix { };
245 | };
246 | };
247 | };
248 | };
249 |
250 | pkgs = import { inherit config; };
251 |
252 | in
253 | { project1 = pkgs.haskellPackages.project1;
254 | }
255 | ```
256 |
257 | ... where `./optparse-applicative.nix` is also generated by `cabal2nix`:
258 |
259 | ```bash
260 | $ cabal2nix cabal://optparse-applicative-0.13.0.0 > optparse-applicative.nix
261 | ```
262 |
263 | Now that we've overriden `optparse-applicative`, we'll run into a different
264 | build failure if we are using a sufficiently new revision of `nixpkgs`:
265 |
266 | ```bash
267 | $ nix-build --attr project1 release2.nix
268 | ...
269 | Configuring optparse-applicative-0.13.0.0...
270 | Setup: Encountered missing dependencies:
271 | QuickCheck ==2.8.*
272 | ...
273 | ```
274 |
275 | Newer versions of `nixpkgs` select a newer version of `QuickCheck` than what
276 | `turtle-1.3.2` requires, so if we want to build against that specific version
277 | of `turtle` then we need to also pin `QuickCheck` in the same way in
278 | `release2a.nix`:
279 |
280 | ```nix
281 | # Note: This should also fail to build
282 | let
283 | config = {
284 | packageOverrides = pkgs: rec {
285 | haskellPackages = pkgs.haskellPackages.override {
286 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
287 | optparse-applicative =
288 | haskellPackagesNew.callPackage ./optparse-applicative.nix { };
289 |
290 | QuickCheck =
291 | haskellPackagesNew.callPackage ./QuickCheck.nix { };
292 |
293 | project1 =
294 | haskellPackagesNew.callPackage ./project1.nix { };
295 |
296 | turtle =
297 | haskellPackagesNew.callPackage ./turtle.nix { };
298 | };
299 | };
300 | };
301 | };
302 |
303 | pkgs = import { inherit config; };
304 |
305 | in
306 | { project1 = pkgs.haskellPackages.project1;
307 | }
308 | ```
309 |
310 | ... where `QuickCheck.nix` is generated by running:
311 |
312 | ```bash
313 | $ cabal2nix cabal://QuickCheck-2.8.2 > QuickCheck.nix
314 | ```
315 |
316 | However, that will still fail to build due to a test suite failure in
317 | `optparse-applicative-0.13.0.0`:
318 |
319 | ```bash
320 | $ nix-build --attr project1 release2a.nix
321 | these derivations will be built:
322 | /nix/store/55b9hxwvknznfdqcksdfp8fqxifgw00p-optparse-applicative-0.13.0.0.drv
323 | /nix/store/9m816lg47c6wcgmjqpfxsyml4hgc739d-turtle-1.3.0.drv
324 | /nix/store/las95xi2lzn1lfc75k6acv05yvcw1cc2-project1-1.0.0.drv
325 | building path(s) ‘/nix/store/bgam9vpvqx5j4x8fkasqwws54cqb0pbs-optparse-applicative-0.13.0.0’
326 | ...
327 | Running 1 test suites...
328 | Test suite optparse-applicative-tests: RUNNING...
329 | ...
330 | === prop_drops_back_contexts from tests/test.hs:153 ===
331 | *** Failed! Exception: 'tests/dropback.err.txt: openFile: does not exist (No such file or directory)' (after 1 test):
332 |
333 | === prop_context_carry from tests/test.hs:162 ===
334 | *** Failed! Exception: 'tests/carry.err.txt: openFile: does not exist (No such file or directory)' (after 1 test):
335 |
336 | === prop_help_on_empty from tests/test.hs:171 ===
337 | *** Failed! Exception: 'tests/helponempty.err.txt: openFile: does not exist (No such file or directory)' (after 1 test):
338 |
339 | === prop_help_on_empty_sub from tests/test.hs:180 ===
340 | *** Failed! Exception: 'tests/helponemptysub.err.txt: openFile: does not exist (No such file or directory)' (after 1 test):
341 | ...
342 | Test suite optparse-applicative-tests: FAIL
343 | Test suite logged to:
344 | dist/test/optparse-applicative-0.13.0.0-optparse-applicative-tests.log
345 | 0 of 1 test suites (0 of 1 test cases) passed.
346 | builder for ‘/nix/store/55b9hxwvknznfdqcksdfp8fqxifgw00p-optparse-applicative-0.13.0.0.drv’ failed with exit code 1
347 | cannot build derivation ‘/nix/store/9m816lg47c6wcgmjqpfxsyml4hgc739d-turtle-1.3.0.drv’: 1 dependencies couldn't be built
348 | cannot build derivation ‘/nix/store/las95xi2lzn1lfc75k6acv05yvcw1cc2-project1-1.0.0.drv’: 1 dependencies couldn't be built
349 | error: build of ‘/nix/store/las95xi2lzn1lfc75k6acv05yvcw1cc2-project1-1.0.0.drv’ failed
350 | ```
351 |
352 | However, we can instruct `cabal2nix` to disable the test suite for our
353 | `optparse-applicative` applicative dependency by running:
354 |
355 | ```bash
356 | $ cabal2nix --no-check cabal://optparse-applicative-0.13.0.0 > optparse-applicative-2.nix
357 | ```
358 |
359 | `release3.nix` uses this test-free `optparse-applicative-2.nix` file:
360 |
361 | ```nix
362 | let
363 | config = {
364 | packageOverrides = pkgs: rec {
365 | haskellPackages = pkgs.haskellPackages.override {
366 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
367 | optparse-applicative =
368 | haskellPackagesNew.callPackage ./optparse-applicative-2.nix { };
369 |
370 | project1 =
371 | haskellPackagesNew.callPackage ./project1.nix { };
372 |
373 | turtle =
374 | haskellPackagesNew.callPackage ./turtle.nix { };
375 | };
376 | };
377 | };
378 | };
379 |
380 | pkgs = import { inherit config; };
381 |
382 | in
383 | { project1 = pkgs.haskellPackages.project1;
384 | }
385 | ```
386 |
387 | ... and now the build succeeds:
388 |
389 | ```bash
390 | $ nix-build --attr project1 release3.nix
391 | these derivations will be built:
392 | /nix/store/f17zxqqk582my4qfig78yvi6nv0vb588-turtle-1.3.0.tar.gz.drv
393 | /nix/store/vn2m1agg92fbmwj1h9168jywy5wmarah-turtle-1.3.0.drv
394 | /nix/store/c3vidydlyaa25kq2zs35kx80vv8sz1mk-project1-1.0.0.drv
395 | these paths will be fetched (1.60 MiB download, 16.92 MiB unpacked):
396 | /nix/store/9kyi7kr3gzpxns4qalda26ww6jfzbpc7-syb-0.6
397 | /nix/store/9v640kglbndlrfpr6wpsq626gb2amx40-libssh2-1.7.0-dev
398 | /nix/store/cm1561r3fs320571n7cicggmjlgy3rdi-unix-compat-0.4.2.0
399 | /nix/store/hfy7dnf85gp1j8dqlhc2v0fdpsmc02vc-nghttp2-1.10.0
400 | /nix/store/kmqlvjmkrg66f5pnbx5jknfiyqf09jgx-curl-7.51.0-dev
401 | /nix/store/l06y84abvix2qmsxs5dbxllfsv9aznfk-hscolour-1.24.1
402 | /nix/store/pq0dcikqwvxxpiknmwki77nyalba3n4a-doctest-0.11.0
403 | /nix/store/v0wwvy3ygb52flq49z1yk445w2126rhs-base-compat-0.9.1
404 | /nix/store/vxqdcfj9kf0k1qvladvxl6wm21np8czh-optparse-applicative-0.13.0.0
405 | /nix/store/w5v8fjg704a5r38sk5dnf3zz7r8vapac-libev-4.22
406 | /nix/store/wyvv44zpn9j2fn30m2k2kry2cycqqlqv-ghc-paths-0.1.0.9
407 | /nix/store/xs8caklddfbmnvkgwcn38m0d7ivxdlq9-mirrors-list
408 | /nix/store/zdkjkdq4fcc6cwnxddm5mxa64n4pavb8-nghttp2-1.10.0-dev
409 | ...
410 | building path(s) ‘/nix/store/frdmdrpr7rgv24i8p5qs5ifvcxz2jm6d-turtle-1.3.0’
411 | ...
412 | Configuring turtle-1.3.0...
413 | Dependency ansi-wl-pprint ==0.6.*: using ansi-wl-pprint-0.6.7.3
414 | Dependency async >=2.0.0.0 && <2.2: using async-2.1.0
415 | Dependency base >=4.6 && <5: using base-4.9.0.0
416 | Dependency bytestring >=0.9.1.8 && <0.11: using bytestring-0.10.8.1
417 | Dependency clock >=0.4.1.2 && <0.8: using clock-0.7.2
418 | Dependency directory >=1.0.7 && <1.3: using directory-1.2.6.2
419 | Dependency doctest >=0.7 && <0.12: using doctest-0.11.0
420 | Dependency foldl >=1.1 && <1.3: using foldl-1.2.1
421 | Dependency hostname <1.1: using hostname-1.0
422 | Dependency managed >=1.0.3 && <1.1: using managed-1.0.5
423 | Dependency optional-args >=1.0 && <2.0: using optional-args-1.0.1
424 | Dependency optparse-applicative ==0.13.*: using optparse-applicative-0.13.0.0
425 | Dependency process >=1.0.1.1 && <1.5: using process-1.4.2.0
426 | Dependency stm <2.5: using stm-2.4.4.1
427 | Dependency system-fileio >=0.2.1 && <0.4: using system-fileio-0.3.16.3
428 | Dependency system-filepath >=0.3.1 && <0.5: using system-filepath-0.4.13.4
429 | Dependency temporary <1.3: using temporary-1.2.0.4
430 | Dependency text <1.3: using text-1.2.2.1
431 | Dependency time <1.7: using time-1.6.0.1
432 | Dependency transformers >=0.2.0.0 && <0.6: using transformers-0.5.2.0
433 | Dependency turtle -any: using turtle-1.3.0
434 | Dependency unix >=2.5.1.0 && <2.8: using unix-2.7.2.0
435 | Dependency unix-compat ==0.4.*: using unix-compat-0.4.2.0
436 | ...
437 | Building turtle-1.3.0...
438 | Preprocessing library turtle-1.3.0...
439 | [ 1 of 10] Compiling Turtle.Internal ( src/Turtle/Internal.hs, dist/build/Turtle/Internal.o )
440 | [ 2 of 10] Compiling Turtle.Line ( src/Turtle/Line.hs, dist/build/Turtle/Line.o )
441 | [ 3 of 10] Compiling Turtle.Shell ( src/Turtle/Shell.hs, dist/build/Turtle/Shell.o )
442 | [ 4 of 10] Compiling Turtle.Options ( src/Turtle/Options.hs, dist/build/Turtle/Options.o )
443 | [ 5 of 10] Compiling Turtle.Pattern ( src/Turtle/Pattern.hs, dist/build/Turtle/Pattern.o )
444 | [ 6 of 10] Compiling Turtle.Format ( src/Turtle/Format.hs, dist/build/Turtle/Format.o )
445 | [ 7 of 10] Compiling Turtle.Prelude ( src/Turtle/Prelude.hs, dist/build/Turtle/Prelude.o )
446 | [ 8 of 10] Compiling Turtle.Bytes ( src/Turtle/Bytes.hs, dist/build/Turtle/Bytes.o )
447 | [ 9 of 10] Compiling Turtle ( src/Turtle.hs, dist/build/Turtle.o )
448 | [10 of 10] Compiling Turtle.Tutorial ( src/Turtle/Tutorial.hs, dist/build/Turtle/Tutorial.o )
449 | Preprocessing test suite 'tests' for turtle-1.3.0...
450 | [1 of 1] Compiling Main ( test/Main.hs, dist/build/tests/tests-tmp/Main.dyn_o )
451 | Linking dist/build/tests/tests ...
452 | Preprocessing test suite 'regression-broken-pipe' for turtle-1.3.0...
453 | [1 of 1] Compiling Main ( test/RegressionBrokenPipe.hs, dist/build/regression-broken-pipe/regression-broken-pipe-tmp/Main.dyn_o )
454 | Linking dist/build/regression-broken-pipe/regression-broken-pipe ...
455 | running tests
456 | Running 2 test suites...
457 | Test suite tests: RUNNING...
458 | Test suite tests: PASS
459 | Test suite logged to: dist/test/turtle-1.3.0-tests.log
460 | Test suite regression-broken-pipe: RUNNING...
461 | Test suite regression-broken-pipe: PASS
462 | Test suite logged to: dist/test/turtle-1.3.0-regression-broken-pipe.log
463 | 2 of 2 test suites (2 of 2 test cases) passed.
464 | ...
465 | Configuring project1-1.0.0...
466 | Dependency base <5: using base-4.9.0.0
467 | Dependency turtle -any: using turtle-1.3.0
468 | ...
469 | /nix/store/dv0vxc8pwrl8sdrfhy4y2ajnb1nh6qqy-project1-1.0.0
470 | ```
471 |
472 | We can look at the difference between `optparse-applicative.nix` and
473 | `optparse-applicative-2.nix` to see what changed:
474 |
475 | ```haskell
476 | $ diff optparse-applicative.nix optparse-applicative-2.nix
477 | 11a12
478 | > doCheck = false;
479 | ```
480 |
481 | The latter file contains a `doCheck = false;` directive which instructs Nix to
482 | not build and run the test suite.
483 |
484 | # Github dependencies
485 |
486 | Suppose that we wish to retrieve our `turtle` dependency from Github instead of
487 | from Hackage. All we have to do is run:
488 |
489 | ```bash
490 | $ cabal2nix https://github.com/Gabriel439/Haskell-Turtle-Library.git --revision ba9c992933ae625cef40a88ea16ee857d1b93e13 > turtle-2.nix
491 | ```
492 |
493 | ... replacing `ba9c992933ae625cef40a88ea16ee857d1b93e13` with the revision we
494 | wish to use. You can omit the revision if you want `cabal2nix` to select the
495 | revision of the current `master` branch.
496 |
497 | Result looks like this `turtle-2.nix`:
498 |
499 | ```nix
500 | { mkDerivation, ansi-wl-pprint, async, base, bytestring, clock
501 | , directory, doctest, fetchgit, foldl, hostname, managed
502 | , optional-args, optparse-applicative, process, lib, stm
503 | , system-fileio, system-filepath, temporary, text, time
504 | , transformers, unix, unix-compat
505 | }:
506 | mkDerivation {
507 | pname = "turtle";
508 | version = "1.3.2";
509 | src = fetchgit {
510 | url = "https://github.com/Gabriel439/Haskell-Turtle-Library.git";
511 | sha256 = "0cbs3yi4glqhv3419hxihvpsgcj2h2sirbgym5d45hz4d32n9i67";
512 | rev = "21b50f09e04b4e149b3c5a5f12405ed608cda2ab";
513 | };
514 | libraryHaskellDepends = [
515 | ansi-wl-pprint async base bytestring clock directory foldl hostname
516 | managed optional-args optparse-applicative process stm
517 | system-fileio system-filepath temporary text time transformers unix
518 | unix-compat
519 | ];
520 | testHaskellDepends = [ base doctest ];
521 | description = "Shell programming, Haskell-style";
522 | license = lib.licenses.bsd3;
523 | }
524 | ```
525 |
526 | `release4.nix` shows an example of depending on `turtle` from Github. The only
527 | difference is that we now depend on the `turtle-2.nix` file:
528 |
529 | ```bash
530 | diff release3.nix release4.nix
531 | 13c13
532 | < haskellPackagesNew.callPackage ./turtle.nix { };
533 | ---
534 | > haskellPackagesNew.callPackage ./turtle-2.nix { };
535 | ```
536 |
537 | ... and the difference between `turtle.nix` and `turtle-2.nix` is that the
538 | dependency list changed and there is a new `src` field pointing to the Github
539 | repository:
540 |
541 | ```diff
542 | 2,5c2,5
543 | < , directory, doctest, foldl, hostname, managed, optional-args
544 | < , optparse-applicative, process, lib, stm, system-fileio
545 | < , system-filepath, temporary, text, time, transformers, unix
546 | < , unix-compat
547 | ---
548 | > , directory, doctest, fetchgit, foldl, hostname, managed
549 | > , optional-args, optparse-applicative, process, lib, stm
550 | > , system-fileio, system-filepath, temporary, text, time
551 | > , transformers, unix, unix-compat
552 | 10c10,14
553 | < sha256 = "0pbvkqqhiaddyhlqcrk48w7li81dijw92wwhchwqh1my1363n5pq";
554 | ---
555 | > src = fetchgit {
556 | > url = "https://github.com/Gabriel439/Haskell-Turtle-Library.git";
557 | > sha256 = "0cbs3yi4glqhv3419hxihvpsgcj2h2sirbgym5d45hz4d32n9i67";
558 | > rev = "21b50f09e04b4e149b3c5a5f12405ed608cda2ab";
559 | > };
560 | ```
561 |
562 | # Source dependencies
563 |
564 | You can also depend on local source checkouts of a given dependency. For
565 | example, if you checkout the `turtle` repository in some other directory then
566 | all you have to do is run:
567 |
568 | ```haskell
569 | $ cabal2nix /path/to/turtle > turtle.nix
570 | ```
571 |
572 | ... and then reference `./turtle.nix` in your `release.nix` file. Now your
573 | build will automatically pull in any changes you make to your source checkout of
574 | `turtle`.
575 |
576 | # Changing the compiler
577 |
578 | You can also override the GHC version used to compile your project, which
579 | `release5.nix` illustrates:
580 |
581 | ```nix
582 | { compiler ? "ghc802" }:
583 |
584 | let
585 | config = {
586 | packageOverrides = pkgs: rec {
587 | haskell = pkgs.haskell // {
588 | packages = pkgs.haskell.packages // {
589 | "${compiler}" = pkgs.haskell.packages."${compiler}".override {
590 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
591 | optparse-applicative =
592 | haskellPackagesNew.callPackage ./optparse-applicative-2.nix { };
593 |
594 | project1 =
595 | haskellPackagesNew.callPackage ./project1.nix { };
596 |
597 | turtle =
598 | haskellPackagesNew.callPackage ./turtle-2.nix { };
599 | };
600 | };
601 | };
602 | };
603 | };
604 | };
605 |
606 | pkgs = import { inherit config; };
607 |
608 | in
609 | { project1 = pkgs.haskell.packages.${compiler}.project1;
610 | }
611 | ```
612 |
613 | By default, the above project is built using GHC 8.0.2:
614 |
615 | ```bash
616 | $ nix-build --attr project1 release5.nix
617 | ...
618 | Using ghc version 8.0.2 found on system at:
619 | /nix/store/7nl6dii88xd761nnz3xyh11qcnrqvqri-ghc-8.0.2/bin/ghc
620 | Using ghc-pkg version 8.0.2 found on system at:
621 | /nix/store/7nl6dii88xd761nnz3xyh11qcnrqvqri-ghc-8.0.2/bin/ghc-pkg
622 | ...
623 | /nix/store/0mw2v9h6f3fz1p9bwy48qxsr5637550s-project1-1.0.0
624 | ```
625 |
626 | ... but you can now override the compiler on the command line like this:
627 |
628 | ```bash
629 | $ nix-build --argstr compiler ghc7103 --attr project1 release5.nix
630 | ...
631 | building path(s) ‘/nix/store/2c5w9j0dam8m2pn35jvbq2namf8y723f-optparse-applicative-0.13.0.0’
632 | setupCompilerEnvironmentPhase
633 | Build with /nix/store/ik664w3cxq2jzr5kby0gwmcm0k96xgmg-ghc-7.10.3.
634 | unpacking sources
635 | unpacking source archive /nix/store/b55m0akvijaxps1lzr424m8y4y4q6awv-optparse-applicative-0.13.0.0.tar.gz
636 | source root is optparse-applicative-0.13.0.0
637 | setting SOURCE_DATE_EPOCH to timestamp 1471231137 of file optparse-applicative-0.13.0.0/tests/test.hs
638 | patching sources
639 | compileBuildDriverPhase
640 | setupCompileFlags: -package-db=/private/var/folders/c9/zf_25xbx7bx8yhsxm4q4vw6m0000gn/T/nix-build-optparse-applicative-0.13.0.0.drv-0/package.conf.d -j8 -threaded
641 | [1 of 1] Compiling Main ( Setup.hs, /private/var/folders/c9/zf_25xbx7bx8yhsxm4q4vw6m0000gn/T/nix-build-optparse-applicative-0.13.0.0.drv-0/Main.o )
642 | Linking Setup ...
643 | configuring
644 | configureFlags: --verbose --prefix=/nix/store/2c5w9j0dam8m2pn35jvbq2namf8y723f-optparse-applicative-0.13.0.0 --libdir=$prefix/lib/$compiler --libsubdir=$pkgid --with-gcc=clang --package-db=/private/var/folders/c9/zf_25xbx7bx8yhsxm4q4vw6m0000gn/T/nix-build-optparse-applicative-0.13.0.0.drv-0/package.conf.d --ghc-option=-optl=-Wl,-headerpad_max_install_names --disable-split-objs --disable-library-profiling --disable-executable-profiling --enable-shared --disable-coverage --enable-library-vanilla --enable-executable-dynamic --disable-tests
645 | Configuring optparse-applicative-0.13.0.0...
646 | Setup: At least the following dependencies are missing:
647 | semigroups >=0.10 && <0.19
648 | builder for ‘/nix/store/kz5ngjwspkky7677vfcjfkliglsxw8nq-optparse-applicative-0.13.0.0.drv’ failed with exit code 1
649 | cannot build derivation ‘/nix/store/4ll08plvc8wmx31mvvsxs83fg6wp849j-turtle-1.3.2.drv’: 1 dependencies couldn't be built
650 | cannot build derivation ‘/nix/store/q40kf7b4d5xwlrnyrflib3qzdc8vfqly-project1-1.0.0.drv’: 1 dependencies couldn't be built
651 | error: build of ‘/nix/store/q40kf7b4d5xwlrnyrflib3qzdc8vfqly-project1-1.0.0.drv’ failed
652 | ```
653 |
654 | Note that the build may fail when you downgrade the compiler. For example, this
655 | build fails because of a missing `semigroups` dependency. This is because of the
656 | following clause in the `optparse-applicative.cabal` file:
657 |
658 | ```
659 | if !impl(ghc >= 8)
660 | build-depends: semigroups >= 0.10 && < 0.19
661 | ```
662 |
663 | ... which `cabal2nix` ignores because it assumes that the current GHC version is
664 | GHC 8. However, we can tweak our `optparse-applicative` dependency to manually
665 | add the `semigroups` dependency in `release6.nix`:
666 |
667 | ```haskell
668 | { compiler ? "ghc802" }:
669 |
670 | let
671 | config = {
672 | packageOverrides = pkgs: rec {
673 | haskell = pkgs.haskell // {
674 | packages = pkgs.haskell.packages // {
675 | "${compiler}" = pkgs.haskell.packages."${compiler}".override {
676 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
677 | optparse-applicative =
678 | pkgs.haskell.lib.addBuildDepend
679 | (haskellPackagesNew.callPackage ./optparse-applicative-2.nix { })
680 | haskellPackagesNew.semigroups;
681 |
682 | project1 =
683 | haskellPackagesNew.callPackage ./project1.nix { };
684 |
685 | turtle =
686 | haskellPackagesNew.callPackage ./turtle-2.nix { };
687 | };
688 | };
689 | };
690 | };
691 | };
692 | };
693 |
694 | pkgs = import { inherit config; };
695 |
696 | in
697 | { project1 = pkgs.haskell.packages.${compiler}.project1;
698 | }
699 | ```
700 |
701 | ... and now the build succeeds:
702 |
703 | ```bash
704 | $ nix-build --argstr compiler ghc7103 --attr project1 release6.nix
705 | ...
706 | Using ghc version 7.10.3 found on system at:
707 | /nix/store/ik664w3cxq2jzr5kby0gwmcm0k96xgmg-ghc-7.10.3/bin/ghc
708 | Using ghc-pkg version 7.10.3 found on system at:
709 | /nix/store/ik664w3cxq2jzr5kby0gwmcm0k96xgmg-ghc-7.10.3/bin/ghc-pkg
710 | ...
711 | /nix/store/9fmspa6dz93vg84d2c5bl9y1hszxmk7v-project1-1.0.0
712 | ```
713 |
714 | The [fourth section](../project3/README.md) of this tutorial contains more details
715 | on how to tweak Haskell builds.
716 |
717 | # Conclusion
718 |
719 | This concludes basic dependency management in Nix. The
720 | [next section](../project2/README.md) covers using Nix to manage non-Haskell
721 | dependencies.
722 |
723 | [hackage-packages]: https://raw.githubusercontent.com/NixOS/nixpkgs/master/pkgs/development/haskell-modules/hackage-packages.nix
724 | [hackage-packages-18.09]: https://raw.githubusercontent.com/NixOS/nixpkgs/release-18.09/pkgs/development/haskell-modules/hackage-packages.nix
725 |
--------------------------------------------------------------------------------
/project1/optparse-applicative-2.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, ansi-wl-pprint, base, process, QuickCheck, lib
2 | , transformers, transformers-compat
3 | }:
4 | mkDerivation {
5 | pname = "optparse-applicative";
6 | version = "0.13.0.0";
7 | sha256 = "1b0c5fdq8bd070g24vrjrwlq979r8dk8mys6aji9hy1l9pcv3inf";
8 | libraryHaskellDepends = [
9 | ansi-wl-pprint base process transformers transformers-compat
10 | ];
11 | testHaskellDepends = [ base QuickCheck ];
12 | doCheck = false;
13 | homepage = "https://github.com/pcapriotti/optparse-applicative";
14 | description = "Utilities and combinators for parsing command line options";
15 | license = lib.licenses.bsd3;
16 | }
17 |
--------------------------------------------------------------------------------
/project1/optparse-applicative.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, ansi-wl-pprint, base, process, QuickCheck, lib
2 | , transformers, transformers-compat
3 | }:
4 | mkDerivation {
5 | pname = "optparse-applicative";
6 | version = "0.13.0.0";
7 | sha256 = "1b0c5fdq8bd070g24vrjrwlq979r8dk8mys6aji9hy1l9pcv3inf";
8 | libraryHaskellDepends = [
9 | ansi-wl-pprint base process transformers transformers-compat
10 | ];
11 | testHaskellDepends = [ base QuickCheck ];
12 | homepage = "https://github.com/pcapriotti/optparse-applicative";
13 | description = "Utilities and combinators for parsing command line options";
14 | license = lib.licenses.bsd3;
15 | }
16 |
--------------------------------------------------------------------------------
/project1/project1.cabal:
--------------------------------------------------------------------------------
1 | name: project1
2 | version: 1.0.0
3 | license: BSD3
4 | license-file: LICENSE
5 | cabal-version: >= 1.18
6 | build-type: Simple
7 |
8 | executable project1
9 | build-depends: base < 5, turtle
10 | main-is: Main.hs
11 | default-language: Haskell2010
12 |
--------------------------------------------------------------------------------
/project1/project1.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, lib, turtle }:
2 | mkDerivation {
3 | pname = "project1";
4 | version = "1.0.0";
5 | src = ./.;
6 | isLibrary = false;
7 | isExecutable = true;
8 | executableHaskellDepends = [ base turtle ];
9 | license = lib.licenses.bsd3;
10 | }
11 |
--------------------------------------------------------------------------------
/project1/release0.nix:
--------------------------------------------------------------------------------
1 | let
2 | pkgs = import { };
3 |
4 | in
5 | { project1 = pkgs.haskellPackages.callPackage ./project1.nix { };
6 | }
7 |
--------------------------------------------------------------------------------
/project1/release1.nix:
--------------------------------------------------------------------------------
1 | # Note: This should fail to build
2 | let
3 | config = {
4 | packageOverrides = pkgs: rec {
5 | haskellPackages = pkgs.haskellPackages.override {
6 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
7 | project1 =
8 | haskellPackagesNew.callPackage ./project1.nix { };
9 |
10 | turtle =
11 | haskellPackagesNew.callPackage ./turtle.nix { };
12 | };
13 | };
14 | };
15 | };
16 |
17 | pkgs = import { inherit config; };
18 |
19 | in
20 | { project1 = pkgs.haskellPackages.project1;
21 | }
22 |
--------------------------------------------------------------------------------
/project1/release2.nix:
--------------------------------------------------------------------------------
1 | # Note: This should also fail to build
2 | let
3 | config = {
4 | packageOverrides = pkgs: rec {
5 | haskellPackages = pkgs.haskellPackages.override {
6 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
7 | optparse-applicative =
8 | haskellPackagesNew.callPackage ./optparse-applicative.nix { };
9 |
10 | project1 =
11 | haskellPackagesNew.callPackage ./project1.nix { };
12 |
13 | turtle =
14 | haskellPackagesNew.callPackage ./turtle.nix { };
15 | };
16 | };
17 | };
18 | };
19 |
20 | pkgs = import { inherit config; };
21 |
22 | in
23 | { project1 = pkgs.haskellPackages.project1;
24 | }
25 |
--------------------------------------------------------------------------------
/project1/release2a.nix:
--------------------------------------------------------------------------------
1 | # Note: This should also fail to build
2 | let
3 | config = {
4 | packageOverrides = pkgs: rec {
5 | haskellPackages = pkgs.haskellPackages.override {
6 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
7 | optparse-applicative =
8 | haskellPackagesNew.callPackage ./optparse-applicative.nix { };
9 |
10 | QuickCheck =
11 | haskellPackagesNew.callPackage ./QuickCheck.nix { };
12 |
13 | project1 =
14 | haskellPackagesNew.callPackage ./project1.nix { };
15 |
16 | turtle =
17 | haskellPackagesNew.callPackage ./turtle.nix { };
18 | };
19 | };
20 | };
21 | };
22 |
23 | pkgs = import { inherit config; };
24 |
25 | in
26 | { project1 = pkgs.haskellPackages.project1;
27 | }
28 |
--------------------------------------------------------------------------------
/project1/release3.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages = pkgs.haskellPackages.override {
5 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
6 | optparse-applicative =
7 | haskellPackagesNew.callPackage ./optparse-applicative-2.nix { };
8 |
9 | project1 =
10 | haskellPackagesNew.callPackage ./project1.nix { };
11 |
12 | turtle =
13 | haskellPackagesNew.callPackage ./turtle.nix { };
14 | };
15 | };
16 | };
17 | };
18 |
19 | pkgs = import { inherit config; };
20 |
21 | in
22 | { project1 = pkgs.haskellPackages.project1;
23 | }
24 |
--------------------------------------------------------------------------------
/project1/release4.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages = pkgs.haskellPackages.override {
5 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
6 | optparse-applicative =
7 | haskellPackagesNew.callPackage ./optparse-applicative-2.nix { };
8 |
9 | project1 =
10 | haskellPackagesNew.callPackage ./project1.nix { };
11 |
12 | turtle =
13 | haskellPackagesNew.callPackage ./turtle-2.nix { };
14 | };
15 | };
16 | };
17 | };
18 |
19 | pkgs = import { inherit config; };
20 |
21 | in
22 | { project1 = pkgs.haskellPackages.project1;
23 | }
24 |
--------------------------------------------------------------------------------
/project1/release5.nix:
--------------------------------------------------------------------------------
1 | { compiler ? "ghc802" }:
2 |
3 | let
4 | config = {
5 | packageOverrides = pkgs: rec {
6 | haskell = pkgs.haskell // {
7 | packages = pkgs.haskell.packages // {
8 | "${compiler}" = pkgs.haskell.packages."${compiler}".override {
9 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
10 | optparse-applicative =
11 | haskellPackagesNew.callPackage ./optparse-applicative-2.nix { };
12 |
13 | project1 =
14 | haskellPackagesNew.callPackage ./project1.nix { };
15 |
16 | turtle =
17 | haskellPackagesNew.callPackage ./turtle-2.nix { };
18 | };
19 | };
20 | };
21 | };
22 | };
23 | };
24 |
25 | pkgs = import { inherit config; };
26 |
27 | in
28 | { project1 = pkgs.haskell.packages.${compiler}.project1;
29 | }
30 |
--------------------------------------------------------------------------------
/project1/release6.nix:
--------------------------------------------------------------------------------
1 | { compiler ? "ghc802" }:
2 |
3 | let
4 | config = {
5 | packageOverrides = pkgs: rec {
6 | haskell = pkgs.haskell // {
7 | packages = pkgs.haskell.packages // {
8 | "${compiler}" = pkgs.haskell.packages."${compiler}".override {
9 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
10 | optparse-applicative =
11 | pkgs.haskell.lib.addBuildDepend
12 | (haskellPackagesNew.callPackage ./optparse-applicative-2.nix { })
13 | haskellPackagesNew.semigroups;
14 |
15 | project1 =
16 | haskellPackagesNew.callPackage ./project1.nix { };
17 |
18 | turtle =
19 | haskellPackagesNew.callPackage ./turtle-2.nix { };
20 | };
21 | };
22 | };
23 | };
24 | };
25 | };
26 |
27 | pkgs = import { inherit config; };
28 |
29 | in
30 | { project1 = pkgs.haskell.packages.${compiler}.project1;
31 | }
32 |
--------------------------------------------------------------------------------
/project1/turtle-2.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, ansi-wl-pprint, async, base, bytestring, clock
2 | , directory, doctest, fetchgit, foldl, hostname, managed
3 | , optional-args, optparse-applicative, process, lib, stm
4 | , system-fileio, system-filepath, temporary, text, time
5 | , transformers, unix, unix-compat
6 | }:
7 | mkDerivation {
8 | pname = "turtle";
9 | version = "1.3.2";
10 | src = fetchgit {
11 | url = "https://github.com/Gabriel439/Haskell-Turtle-Library.git";
12 | sha256 = "0cbs3yi4glqhv3419hxihvpsgcj2h2sirbgym5d45hz4d32n9i67";
13 | rev = "21b50f09e04b4e149b3c5a5f12405ed608cda2ab";
14 | };
15 | libraryHaskellDepends = [
16 | ansi-wl-pprint async base bytestring clock directory foldl hostname
17 | managed optional-args optparse-applicative process stm
18 | system-fileio system-filepath temporary text time transformers unix
19 | unix-compat
20 | ];
21 | testHaskellDepends = [ base doctest ];
22 | description = "Shell programming, Haskell-style";
23 | license = lib.licenses.bsd3;
24 | }
25 |
--------------------------------------------------------------------------------
/project1/turtle.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, ansi-wl-pprint, async, base, bytestring, clock
2 | , directory, doctest, foldl, hostname, managed, optional-args
3 | , optparse-applicative, process, lib, stm, system-fileio
4 | , system-filepath, temporary, text, time, transformers, unix
5 | , unix-compat
6 | }:
7 | mkDerivation {
8 | pname = "turtle";
9 | version = "1.3.2";
10 | sha256 = "0pbvkqqhiaddyhlqcrk48w7li81dijw92wwhchwqh1my1363n5pq";
11 | libraryHaskellDepends = [
12 | ansi-wl-pprint async base bytestring clock directory foldl hostname
13 | managed optional-args optparse-applicative process stm
14 | system-fileio system-filepath temporary text time transformers unix
15 | unix-compat
16 | ];
17 | testHaskellDepends = [ base doctest ];
18 | description = "Shell programming, Haskell-style";
19 | license = lib.licenses.bsd3;
20 | }
21 |
--------------------------------------------------------------------------------
/project2/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Gabriella Gonzalez
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright notice,
7 | this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright notice,
9 | this list of conditions and the following disclaimer in the documentation
10 | and/or other materials provided with the distribution.
11 | * Neither the name of Gabriella Gonzalez nor the names of other contributors
12 | may be used to endorse or promote products derived from this software
13 | without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/project2/Main.hs:
--------------------------------------------------------------------------------
1 | module Main where
2 |
3 | foreign import ccall "check" check :: IO ()
4 |
5 | main :: IO ()
6 | main = check
7 |
--------------------------------------------------------------------------------
/project2/README.md:
--------------------------------------------------------------------------------
1 | # Native dependencies
2 |
3 | Nix can also provision non-Haskell dependencies for your project. This project
4 | provides an example of a small program that invokes `libtar` to open and close
5 | a TAR file.
6 |
7 | The first difference is that we have to extend the `cabal` file to add an
8 | `extra-libraries` clause:
9 |
10 | ```cabal
11 | name: project2
12 | version: 1.0.0
13 | license: BSD3
14 | license-file: LICENSE
15 | cabal-version: >= 1.18
16 | build-type: Simple
17 | extra-source-files: cbits
18 |
19 | executable project2
20 | build-depends: base < 5
21 | default-language: Haskell2010
22 | c-sources: cbits/check.c
23 | extra-libraries: tar
24 | main-is: Main.hs
25 | ```
26 |
27 | We also use the `c-sources` line so that Cabal can build and link in a small
28 | test C program located at `cbits/check.c` for demonstration purposes. This
29 | program uses `libtar` to open and close a tar file named `example.tar`:
30 |
31 | ```c
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | void check() {
38 | int ret;
39 | TAR *t;
40 |
41 | ret = tar_open(&t, "example.tar", NULL, O_RDONLY, 0, TAR_VERBOSE);
42 | if (ret == -1) {
43 | fprintf(stderr, "tar_open failed\n");
44 | exit(1);
45 | }
46 | printf("Successfully opened example.tar\n");
47 |
48 | ret = tar_close(t);
49 | if (ret == -1) {
50 | fprintf(stderr, "tar_close failed\n");
51 | exit(1);
52 | }
53 | printf("Successfully closed example.tar\n");
54 | }
55 | ```
56 |
57 | ... and then we refer to the `check` function from the Haskell code like this:
58 |
59 | ```haskell
60 | module Main where
61 |
62 | foreign import ccall "check" check :: IO ()
63 |
64 | main :: IO ()
65 | main = check
66 | ```
67 |
68 | Like before, we transform the `project2.cabal` file into a Nix expression by
69 | running `cabal2nix`:
70 |
71 | ```bash
72 | $ cabal2nix . > project2.nix
73 | ```
74 |
75 | `cabal2nix` is smart and notices that our `project2.cabal` file has an
76 | `extra-libraries` section and adds `tar` as a dependency of our package in
77 | `project2.nix`:
78 |
79 | ```nix
80 | { mkDerivation, base, lib, tar }:
81 | mkDerivation {
82 | pname = "project2";
83 | version = "1.0.0";
84 | src = ./.;
85 | isLibrary = false;
86 | isExecutable = true;
87 | executableHaskellDepends = [ base ];
88 | executableSystemDepends = [ tar ];
89 | license = lib.licenses.bsd3;
90 | }
91 | ```
92 |
93 | The `tar` dependency goes in the `executableSystemDepends` section, which
94 | indicates that this is a dependency on a native library and not a Haskell
95 | dependency.
96 |
97 | However, this alone is not enough to thread `libtar` to our package. We need
98 | to update our `release.nix` to specify which Nix package the `tar` dependency
99 | comes from:
100 |
101 | ```nix
102 | let
103 | config = {
104 | packageOverrides = pkgs: rec {
105 | haskellPackages = pkgs.haskellPackages.override {
106 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
107 | project2 =
108 | haskellPackagesNew.callPackage ./project2.nix {
109 | tar = pkgs.libtar;
110 | };
111 | };
112 | };
113 | };
114 | };
115 |
116 | pkgs = import { inherit config; };
117 |
118 | in
119 | rec {
120 | project2 = pkgs.haskellPackages.project2;
121 |
122 | test = pkgs.runCommand "check" {} ''
123 | touch example
124 | tar cf example.tar example
125 | ${project2}/bin/project2 | tee $out
126 | '';
127 | }
128 | ```
129 |
130 | In fact, our `release.nix` file goes a step further and includes two attributes
131 | instead of one:
132 |
133 | * the `project2` attribute builds our project
134 | * the `test` attribute runs a simple test to verify that the program works
135 |
136 | If we only care about building the project, then we would just run:
137 |
138 | ```bash
139 | $ nix-build --attr project2 release.nix
140 | ```
141 |
142 | ... but if we want to build and test our project, we can run:
143 |
144 | ```bash
145 | $ nix-build --attr test release.nix
146 | these derivations will be built:
147 | /nix/store/6z0jc3s2ya8j226rgma73w03pdphqwx8-project2-1.0.0.drv
148 | /nix/store/k82nnkiszzfcnjxq29gcbmgnh5cbfc51-check.drv
149 | building path(s) ‘/nix/store/83bf1b1cp0qs6jgryb6yr0jfb759x4k5-project2-1.0.0’
150 | ...
151 | configureFlags: --verbose --prefix=/nix/store/83bf1b1cp0qs6jgryb6yr0jfb759x4k5-project2-1.0.0 --libdir=$prefix/lib/$compiler --libsubdir=$pkgid --with-gcc=gcc --package-db=/tmp/nix-build-project2-1.0.0.drv-0/package.conf.d --ghc-option=-optl=-Wl,-rpath=/nix/store/83bf1b1cp0qs6jgryb6yr0jfb759x4k5-project2-1.0.0/lib/ghc-8.0.1/project2-1.0.0 --ghc-option=-j1 --enable-split-objs --disable-library-profiling --disable-profiling --enable-shared --enable-library-vanilla --enable-executable-dynamic --enable-tests --extra-include-dirs=/nix/store/pb3jxhy4z54i24i9s0kyszdmxd2xajc5-libtar-1.2.20/include --extra-lib-dirs=/nix/store/pb3jxhy4z54i24i9s0kyszdmxd2xajc5-libtar-1.2.20/lib
152 | ...
153 | building path(s) ‘/nix/store/zc7ai4h44fydbm89a85b5bik5683klyk-check’
154 | Successfully opened example.tar
155 | Successfully closed example.tar
156 | /nix/store/zc7ai4h44fydbm89a85b5bik5683klyk-check
157 | ```
158 |
159 | Notice how the `configureFlags` passed to `project2` ensure that Nix's `libtar`
160 | is on `cabal`'s search path when building the library. Then the tests are run
161 | and the output is saved to `/nix/store/zc7ai4h44fydbm89a85b5bik5683klyk-check`,
162 | which we can also reference via the `result` symlink that `nix-build` creates:
163 |
164 | ```bash
165 | $ cat result
166 | Successfully opened example.tar
167 | Successfully closed example.tar
168 | ```
169 |
170 | # Conclusion
171 |
172 | This concludes managing native dependencies using Nix. The next section covers
173 | [customizing Haskell projects](../project3/README.md).
174 |
--------------------------------------------------------------------------------
/project2/cbits/check.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | void check() {
7 | int ret;
8 | TAR *t;
9 |
10 | ret = tar_open(&t, "example.tar", NULL, O_RDONLY, 0, TAR_VERBOSE);
11 | if (ret == -1) {
12 | fprintf(stderr, "tar_open failed\n");
13 | exit(1);
14 | }
15 | printf("Successfully opened example.tar\n");
16 |
17 | ret = tar_close(t);
18 | if (ret == -1) {
19 | fprintf(stderr, "tar_close failed\n");
20 | exit(1);
21 | }
22 | printf("Successfully closed example.tar\n");
23 | }
24 |
--------------------------------------------------------------------------------
/project2/project2.cabal:
--------------------------------------------------------------------------------
1 | name: project2
2 | version: 1.0.0
3 | license: BSD3
4 | license-file: LICENSE
5 | cabal-version: >= 1.18
6 | build-type: Simple
7 | extra-source-files: cbits
8 |
9 | executable project2
10 | build-depends: base < 5
11 | default-language: Haskell2010
12 | c-sources: cbits/check.c
13 | extra-libraries: tar
14 | main-is: Main.hs
15 |
--------------------------------------------------------------------------------
/project2/project2.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, lib, tar }:
2 | mkDerivation {
3 | pname = "project2";
4 | version = "1.0.0";
5 | src = ./.;
6 | isLibrary = false;
7 | isExecutable = true;
8 | executableHaskellDepends = [ base ];
9 | executableSystemDepends = [ tar ];
10 | license = lib.licenses.bsd3;
11 | }
12 |
--------------------------------------------------------------------------------
/project2/release.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages = pkgs.haskellPackages.override {
5 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
6 | project2 =
7 | haskellPackagesNew.callPackage ./project2.nix {
8 | tar = pkgs.libtar;
9 | };
10 | };
11 | };
12 | };
13 | };
14 |
15 | pkgs = import { inherit config; };
16 |
17 | in
18 | rec {
19 | project2 = pkgs.haskellPackages.project2;
20 |
21 | test = pkgs.runCommand "check" {} ''
22 | touch example
23 | tar cf example.tar example
24 | ${project2}/bin/project2 | tee $out
25 | '';
26 | }
27 |
--------------------------------------------------------------------------------
/project3/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Gabriella Gonzalez
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright notice,
7 | this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright notice,
9 | this list of conditions and the following disclaimer in the documentation
10 | and/or other materials provided with the distribution.
11 | * Neither the name of Gabriella Gonzalez nor the names of other contributors
12 | may be used to endorse or promote products derived from this software
13 | without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/project3/Main.hs:
--------------------------------------------------------------------------------
1 | module Main where
2 |
3 | import qualified Project3
4 |
5 | main :: IO ()
6 | main = Project3.check
7 |
--------------------------------------------------------------------------------
/project3/Project3.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ForeignFunctionInterface #-}
2 |
3 | module Project3 where
4 |
5 | foreign import ccall "check" check :: IO ()
6 |
--------------------------------------------------------------------------------
/project3/README.md:
--------------------------------------------------------------------------------
1 | # Exploring Haskell packages
2 |
3 |
4 |
5 | This section covers how to tweak Haskell packages that Nix builds (typically
6 | to fix failing builds).
7 |
8 | Begin by running the following command to build the `morte` package:
9 |
10 | ```bash
11 | $ nix-build '' --attr haskellPackages.morte
12 | ...
13 | /nix/store/z8rf74cylf9dqj63yb8p3j37sn8n49zf-morte-1.6.2
14 | ```
15 |
16 | `nix-build` creates a `result` symlink pointing to the built package. We can
17 | use `tree` to study the directory layout of the built package:
18 |
19 | ```bash
20 | $ tree result
21 | result
22 | ├── bin
23 | │ └── morte
24 | ├── lib
25 | │ └── ghc-8.0.1
26 | │ ├── morte-1.6.2
27 | │ │ ├── libHSmorte-1.6.2-9FIpoXNPAFhL3LxBszO4VO.a
28 | │ │ ├── libHSmorte-1.6.2-9FIpoXNPAFhL3LxBszO4VO-ghc8.0.1.so
29 | │ │ └── Morte
30 | │ │ ├── Context.dyn_hi
31 | │ │ ├── Context.hi
32 | │ │ ├── Core.dyn_hi
33 | │ │ ├── Core.hi
34 | │ │ ├── Import.dyn_hi
35 | │ │ ├── Import.hi
36 | │ │ ├── Lexer.dyn_hi
37 | │ │ ├── Lexer.hi
38 | │ │ ├── Parser.dyn_hi
39 | │ │ ├── Parser.hi
40 | │ │ ├── Tutorial.dyn_hi
41 | │ │ └── Tutorial.hi
42 | │ └── package.conf.d
43 | │ └── morte-1.6.2-9FIpoXNPAFhL3LxBszO4VO.conf
44 | ├── nix-support
45 | │ └── propagated-native-build-inputs
46 | └── share
47 | ├── doc
48 | │ └── x86_64-linux-ghc-8.0.1
49 | │ └── morte-1.6.2
50 | │ ├── html
51 | │ │ ├── doc-index.html
52 | │ │ ├── frames.html
53 | │ │ ├── haddock-util.js
54 | │ │ ├── hslogo-16.png
55 | │ │ ├── index-frames.html
56 | │ │ ├── index.html
57 | │ │ ├── mini_Morte-Context.html
58 | │ │ ├── mini_Morte-Core.html
59 | │ │ ├── mini_Morte-Import.html
60 | │ │ ├── mini_Morte-Lexer.html
61 | │ │ ├── mini_Morte-Parser.html
62 | │ │ ├── mini_Morte-Tutorial.html
63 | │ │ ├── minus.gif
64 | │ │ ├── Morte-Context.html
65 | │ │ ├── Morte-Core.html
66 | │ │ ├── morte.haddock
67 | │ │ ├── Morte-Import.html
68 | │ │ ├── Morte-Lexer.html
69 | │ │ ├── Morte-Parser.html
70 | │ │ ├── Morte-Tutorial.html
71 | │ │ ├── morte.txt
72 | │ │ ├── ocean.css
73 | │ │ ├── plus.gif
74 | │ │ ├── src
75 | │ │ │ ├── hscolour.css
76 | │ │ │ ├── Morte-Context.html
77 | │ │ │ ├── Morte-Core.html
78 | │ │ │ ├── Morte-Import.html
79 | │ │ │ ├── Morte-Lexer.html
80 | │ │ │ ├── Morte-Parser.html
81 | │ │ │ └── Morte-Tutorial.html
82 | │ │ └── synopsis.png
83 | │ └── LICENSE
84 | └── x86_64-linux-ghc-8.0.1
85 | └── morte-1.6.2
86 | ├── bench
87 | │ └── src
88 | │ ├── concat.mt
89 | │ ├── factorial.mt
90 | │ └── recursive.mt
91 | └── test
92 | └── src
93 | ├── example0.mt
94 | ├── example10.mt
95 | ├── example11.mt
96 | ├── example12.mt
97 | ├── example13.mt
98 | ├── example14.mt
99 | ├── example15.mt
100 | ├── example1.mt
101 | ├── example2.mt
102 | ├── example3.mt
103 | ├── example4.mt
104 | ├── example5.mt
105 | ├── example6.mt
106 | ├── example7.mt
107 | ├── example8.mt
108 | └── example9.mt
109 |
110 | 19 directories, 68 files
111 | ```
112 |
113 | This package has a significantly more complex layout:
114 |
115 | * `bin/`
116 |
117 | This directory stores any executable programs that the Haskell package
118 | produces. Nix does not require that derivations deposit executables in this
119 | directory, but Nix does specially integrate with packages that do use this
120 | directory for their executables. For example, if you use NixOS and you add
121 | a package to `environment.systemPackages` then the `bin/` directory for the
122 | package is added to the system `$PATH`. Similarly, if you add a package as
123 | a `buildInput` to a Nix derivation, then any executables underneath `bin/`
124 | are added to the `$PATH` of the derivation's build script.
125 |
126 | * `lib/ghc-8.0.1/morte-1.6.2/`
127 |
128 | This contains all object code and interface files built by the Haskell
129 | library. By default, Nix generates both a static library (i.e.
130 | `libHSmorte-1.6.2-9FIpoXNPAFhL3LxBszO4VO.a`) and a dynamic library
131 | (i.e. `libHSmorte-1.6.2-9FIpoXNPAFhL3LxBszO4VO-ghc8.0.1.so`) for any
132 | Haskell package that provides a `library` section in the `*.cabal` file
133 |
134 | By default, these libraries are compiled with GHC's `-fsplit-objs` flag to
135 | enable split object files, which reduces the library size but increases the
136 | compile time. This is another reason why I recommend building the root
137 | project using `cabal` during development because by default `cabal` won't
138 | compile with split objects and will therefore build faster. However,
139 | `-fsplit-objs` is a good default when Nix builds the project, so you don't
140 | need to change this setting.
141 |
142 | Nix also stores a "singleton" GHC package database holding just one package
143 | such as `package.conf.d/morte-1.6.2-9FIpoXNPAFhL3LxBszO4VO.conf`. This is
144 | one difference between Nix and `stack`: `stack` uses up to three package
145 | databases (i.e. the global, snapshot, and project databases) whereas Nix
146 | uses a package database per package. In practice this difference usually
147 | doesn't matter, but for more exotic build scenarios (such as compiling
148 | different sets of packages with different flags) this gives Nix greater
149 | flexibility than `stack`.
150 |
151 | * `nix-support/propagated-native-build-inputs`
152 |
153 | Anything which depends on the `morte` library must also depend on packages
154 | listed in `nix-support/propagated-native-build-inputs`. This is how Nix
155 | ensures that you are not missing any libraries when linking packages. For
156 | example, this is what the file looks like for `morte`:
157 |
158 | ```bash
159 | cat result/nix-support/propagated-native-build-inputs
160 | /nix/store/ifgkzb0df0d6378399hny3ry8x530wim-Earley-0.11.0.1 /nix/store/3q914v2ijfj26fv5w9hd07a6lcnr4d89-http-client-0.4.31.1 /nix/store/xp55z3bpf92yaswyd6s9ajzr44w5m1r7-http-client-tls-0.2.4.1 /nix/store/39a8f70x5j09jnixj6cwv9l0n6jdncwb-microlens-0.4.7.0 /nix/store/rqd01fgj53wyc627wvns94j68a5iza07-microlens-mtl-0.1.10.0 /nix/store/x0g5n6k09f6zga000w9y0z1hx4i9ryyx-pipes-4.1.9 /nix/store/gkg8jgn8f8if6j83jhz3v6i5nnqwg8vc-system-fileio-0.3.16.3 /nix/store/x8chw4wp9r4dapsjmbgsbs3blgnmsxy5-system-filepath-0.4.13.4 /nix/store/zx0yg2d5jxwpdig83qica8hq4mv5l0hx-text-1.2.2.1 /nix/store/l4w7l07k9hr1qrmj9772zk60scw5xa4n-text-format-0.3.1.1 /nix/store/0crlsp45jgs2bs50bv68klpdz1b1jpk9-optparse-applicative-0.12.1.0 /nix/store/zx0yg2d5jxwpdig83qica8hq4mv5l0hx-text-1.2.2.1
161 | ```
162 |
163 | All of those paths are packages that `morte` depends on, each of which
164 | provides object code which must be linked into any executable that depends
165 | on `morte`
166 |
167 | * `share/doc/x86_64-linux-ghc-8.0.1/morte-1.6.2/html`
168 |
169 | These are the generated haddocks for the project. You can open
170 | `share/doc/x86_64-linux-ghc-8.0.1/morte-1.6.2/html/index.html` in your
171 | browser to view `morte`'s documentation
172 |
173 | * `share/doc/x86_64-linux-ghc-8.0.1/morte-1.6.2/LICENSE`
174 |
175 | This is the project license
176 |
177 | * `x86_64-linux-ghc-8.0.1/morte-1.6.2`
178 |
179 | These are extra data files bundled with the project for running tests and
180 | benchmarks. `cabal` includes these files because `morte` has the following
181 | line in the `morte.cabal` file:
182 |
183 | ```cabal
184 | Data-Files: bench/src/*.mt test/src/*.mt
185 | ```
186 |
187 | # Tweaking Haskell projects
188 |
189 | You can customize what Nix includes in a built Haskell package using the
190 | `pkg.haskell.lib` suite of utilities. For example, `release0.nix` contains an
191 | example of disabling haddocks for the `morte` package:
192 |
193 | ```nix
194 | let
195 | config = {
196 | packageOverrides = pkgs: rec {
197 | haskellPackages = pkgs.haskellPackages.override {
198 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
199 | morte = pkgs.haskell.lib.dontHaddock haskellPackagesOld.morte;
200 | };
201 | };
202 | };
203 | };
204 |
205 | pkgs = import { inherit config; };
206 |
207 | in
208 | { morte = pkgs.haskellPackages.morte;
209 | }
210 | ```
211 |
212 | You can find the full list of `pkgs.haskell.lib` utilities in this file:
213 |
214 | * [Utilities for tweaking Haskell packages][haskell-lib]
215 |
216 | Some really convenient utilities include:
217 |
218 | * `pkgs.haskell.lib.dontCheck` - Disable tests (applies derivation option `doCheck = false;`)
219 | * `pkgs.haskell.lib.appendPatch` - Patch a Haskell package
220 | * `pkgs.haskell.lib.overrideCabal` - General tool for post-processing the output
221 | of `cabal2nix`
222 |
223 | For example, this project is almost identical to the previous project except
224 | that instead of using Nix to test the executable we now add a test suite to our
225 | Haskell package that tests our TAR code in `Test.hs`:
226 |
227 | ```haskell
228 | {-# LANGUAGE OverloadedStrings #-}
229 |
230 | module Main where
231 |
232 | import Control.Applicative (empty)
233 | import qualified Project3
234 | import qualified Turtle
235 |
236 | main :: IO ()
237 | main = do
238 | Turtle.touch "example"
239 | Turtle.procs "bsdtar" ["cf", "example.tar", "example"] empty
240 | Project3.check
241 | ```
242 |
243 | For whatever reason we use `bsdtar` instead of `tar` because we are gluttons for
244 | punishment.
245 |
246 | The `project3.nix` file generated by `cabal2nix` has no idea that our test suite
247 | depends on `bsdtar` so if we were to build and run the Haskell project without
248 | any modifications the test suite would fail due to a missing `bsdtar`
249 | executable.
250 |
251 | However, our `release1.nix` file shows how we can tweak our project to include
252 | `bsdtar` (provided by `libarchive`) for the test suite:
253 |
254 | ```nix
255 | let
256 | config = {
257 | packageOverrides = pkgs: rec {
258 | haskellPackages = pkgs.haskellPackages.override {
259 | overrides = haskellPackagesNew: haskellPackgesOld: rec {
260 | project3 =
261 | pkgs.haskell.lib.overrideCabal
262 | ( haskellPackagesNew.callPackage ./project3.nix {
263 | tar = pkgs.libtar;
264 | }
265 | )
266 | ( oldDerivation: {
267 | testToolDepends = [ pkgs.libarchive ];
268 | }
269 | );
270 | };
271 | };
272 | };
273 | };
274 |
275 | pkgs = import { inherit config; };
276 |
277 | in
278 | { project3 = pkgs.haskellPackages.project3;
279 | }
280 | ```
281 |
282 | This uses the `pkgs.haskell.lib.overrideCabal` which is the most general
283 | utility for tweaking derivations generated by `cabal2nix`. You can find the
284 | full set of fields that we can use to extend the `cabal2nix` code at the top of
285 | this file:
286 |
287 | * [Haskell generic builder][generic-builder]
288 |
289 | These are also the same fields that you will see in an expression generated by
290 | `cabal2nix`, such as `executableHaskellDepends` or `isLibrary`. `overrideCabal`
291 | lets you tweak any field you see in that list.
292 |
293 | If you build and run the `release1.nix` project you will see that the test
294 | suite runs and passes, indicating that our code works with TAR files generated
295 | by `bsdtar`:
296 |
297 | ```bash
298 | $ nix-build --attr project3 release1.nix these derivations will be built:
299 | /nix/store/i7i38jx93qwxzwg9xakbd8lrfv9kbpi4-project3-1.0.0.drv
300 | building path(s) ‘/nix/store/2z661k6rnwyiimympn1anmghdybi7izc-project3-1.0.0’
301 | ...
302 | Building project3-1.0.0...
303 | Preprocessing library project3-1.0.0...
304 | [1 of 1] Compiling Project3 ( Project3.hs, dist/build/Project3.o )
305 | Preprocessing executable 'project3' for project3-1.0.0...
306 | [1 of 2] Compiling Project3 ( Project3.hs, dist/build/project3/project3-tmp/Project3.dyn_o )
307 | [2 of 2] Compiling Main ( Main.hs, dist/build/project3/project3-tmp/Main.dyn_o )
308 | Linking dist/build/project3/project3 ...
309 | Preprocessing test suite 'test' for project3-1.0.0...
310 | [1 of 2] Compiling Project3 ( Project3.hs, dist/build/test/test-tmp/Project3.dyn_o )
311 | [2 of 2] Compiling Main ( Test.hs, dist/build/test/test-tmp/Main.dyn_o )
312 | Linking dist/build/test/test ...
313 | running tests
314 | Running 1 test suites...
315 | Test suite test: RUNNING...
316 | Test suite test: PASS
317 | Test suite logged to: dist/test/project3-1.0.0-test.log
318 | 1 of 1 test suites (1 of 1 test cases) passed.
319 | ...
320 | /nix/store/2z661k6rnwyiimympn1anmghdybi7izc-project3-1.0.0
321 | ```
322 |
323 | # Minimizing the closure
324 |
325 | In Nix, a package "closure" is the set of all transitive dependencies for that
326 | package. Haskell packages built using Nix have a very large closure because
327 | they all depend on `ghc`, which is a very large package.
328 |
329 | For example, `release2.nix` contains an example of building a `docker` container
330 | for the `project3` executable before and after closure minimization:
331 |
332 | ```nix
333 | let
334 | config = rec {
335 | packageOverrides = pkgs: rec {
336 | docker-container-large = pkgs.dockerTools.buildImage {
337 | name = "project3-container";
338 | config.Cmd = [ "${haskellPackages.project3}/bin/project3" ];
339 | };
340 |
341 | docker-container-small = pkgs.dockerTools.buildImage {
342 | name = "project3-container";
343 | config.Cmd = [ "${haskellPackages.project3-minimal}/bin/project3" ];
344 | };
345 |
346 | haskellPackages = pkgs.haskellPackages.override {
347 | overrides = haskellPackagesNew: haskellPackgesOld: rec {
348 | project3 =
349 | pkgs.haskell.lib.overrideCabal
350 | ( haskellPackagesNew.callPackage ./project3.nix {
351 | tar = pkgs.libtar;
352 | }
353 | )
354 | ( oldDerivation: {
355 | testToolDepends = [ pkgs.libarchive ];
356 | enableSharedExecutables = false;
357 | }
358 | );
359 |
360 | project3-minimal =
361 | pkgs.haskell.lib.overrideCabal
362 | ( pkgs.haskell.lib.justStaticExecutables
363 | ( haskellPackagesNew.callPackage ./project3.nix {
364 | tar = pkgs.libtar;
365 | }
366 | )
367 | )
368 | ( oldDerivation: {
369 | testToolDepends = [ pkgs.libarchive ];
370 | }
371 | );
372 | };
373 | };
374 | };
375 | };
376 |
377 | pkgs = import { inherit config; system = "x86_64-linux"; };
378 |
379 | in
380 | { project3 = pkgs.haskellPackages.project3;
381 |
382 | project3-minimal = pkgs.haskellPackages.project3-minimal;
383 |
384 | docker-container-small = pkgs.docker-container-small;
385 |
386 | docker-container-large = pkgs.docker-container-large;
387 | }
388 | ```
389 |
390 | If you build the `docker-container-large` attribute you will get a container
391 | that contains the entire closure of the `project03` project, including a
392 | complete `ghc` installation. This is because `project03` is dynamically linked
393 | and depends on several dynamic libraries that are packaged with `ghc`. The
394 | final container is over 250 MB compressed and 1.5 GB uncompressed.
395 |
396 | ```bash
397 | $ nix-build --attr docker-container-large release2.nix
398 | ...
399 | building path(s) ‘/nix/store/ml441hr1l5gw9piq8ysnjdaazjxapsci-project3-container.tar.gz’
400 | Adding layer
401 | Adding meta
402 | Cooking the image
403 | /nix/store/ml441hr1l5gw9piq8ysnjdaazjxapsci-project3-container.tar.gz
404 | $ du -hs $(readlink result)
405 | 251M /nix/store/ml441hr1l5gw9piq8ysnjdaazjxapsci-project3-container.tar.gz
406 | ```
407 |
408 | If you build the `docker-container-small` attribute you will get a much
409 | smaller container that is only 11 MB compressed and 27 MB uncompressed:
410 |
411 | ```bash
412 | $ nix-build --attr docker-container-small release2.nix
413 | ...
414 | building path(s) ‘/nix/store/3mkrcqjnqv5vwid4qcaf3p1i70y87096-project3-container.tar.gz’
415 | Adding layer
416 | Adding meta
417 | Cooking the image
418 | /nix/store/3mkrcqjnqv5vwid4qcaf3p1i70y87096-project3-container.tar.gz
419 | $ du -hs $(readlink result)
420 | 11M /nix/store/3mkrcqjnqv5vwid4qcaf3p1i70y87096-project3-container.tar.gz
421 | $ docker load -i result
422 | 73bcfb39a3b5: Loading layer [==================================================>] 27.8MB/27.8MB
423 | $ docker run -it project3-container
424 | tar_open failed
425 | ```
426 |
427 | This works thanks to the `justStaticExecutables` utility, which statically
428 | links the built executable and removes other unnecessary directories from the
429 | built package.
430 |
431 | The combination of these two things significantly slims down the dependency
432 | tree. We can verify this using the handy `nix-store --query --requisites`
433 | command which lets you view all transitive dependencies for a given Nix package.
434 |
435 | Before the closure minimization, we get this dependency tree:
436 |
437 | ```bash
438 | $ nix-build --attr project3 release2.nix these derivations will be built:
439 | ...
440 | /nix/store/1nc7kaw3lp574hhi2bfxdb490q5kp2m8-project3-1.0.0
441 |
442 | $ nix-store --query --requisites result
443 | /nix/store/jm1n87rp8vr90j9ahcrfzr57nc2r8vgf-glibc-2.24
444 | /nix/store/6sp63j6m6vyspqm2d7zw15ipiym70kzc-attr-2.4.47
445 | /nix/store/03a2gwqj26hidgqsczpvk0g65p5fbhrs-attr-2.4.47-bin
446 | /nix/store/28wl3f34vfjpw0y5809bgr6382wqdscf-bash-4.3-p48
447 | /nix/store/24ln24075d8i5nq1sg558y667pyijx65-ghc-8.0.1-doc
448 | /nix/store/1rhcwhq2f8kkq01wrrr4w210n31iyq10-lzo-2.09
449 | /nix/store/81n6pgrx7lq5f7cjajv82wa422ccvbv4-openssl-1.0.2j
450 | /nix/store/8jwqlpyxp5c2rl5g7yspdfbh58dsciwx-xz-5.2.2
451 | /nix/store/ci7bn61pfds1y1ijkf3c85il0jdp87ar-acl-2.2.52
452 | /nix/store/isl3g7jbrl9a1kdljiyzvjjsqnmn060r-bzip2-1.0.6.0.1
453 | /nix/store/wz7l2zqdsa78jxnzkigv5gy2c7hxnbxh-zlib-1.2.8
454 | /nix/store/mh7n2d60dy0bj5qhhgwhn55skbsqcnsz-libxml2-2.9.4
455 | /nix/store/7crpicyhj61dkqa402f6m3fmb8iy23bn-libarchive-3.2.2-lib
456 | /nix/store/p4ks7972lzhqq9rcyc837c0a9ms2qspr-acl-2.2.52-bin
457 | /nix/store/fsxgigm2yb6xp3axxh342cx04kxrijg1-acl-2.2.52-dev
458 | /nix/store/igwsyzqig15qzgacq35vqdxsj0v3k1ba-attr-2.4.47-dev
459 | /nix/store/q4f9n8vv030pddpg1y2v5p38g0rkmffy-libarchive-3.2.2
460 | /nix/store/41zm7hyzc6qs2fzw2j2fs6c1j9bw7nfm-libarchive-3.2.2-dev
461 | /nix/store/djd2r4cnbcx4vfbj1qalg85vjbcslwxv-gcc-5.4.0-lib
462 | /nix/store/6xiq93r3dnny4m7svvb6hvq9qwjrixk8-gmp-6.1.1
463 | /nix/store/8rn45r9ndfq5h7mx58r35p2szky5ja6n-coreutils-8.25
464 | /nix/store/5kkjn2h1m852q8xh5kz0kfgi5nrbc1fz-perl-5.22.2
465 | /nix/store/321k7mdjv4fwf1rfss1r7nayni18iqaw-glibc-2.24-bin
466 | /nix/store/ppjqx7j8w22j0pahnq4gnzhk4vmibncn-pcre-8.39
467 | /nix/store/h9aqgpgspgjhygj63wpncfzvz5ys851n-gnugrep-2.25
468 | /nix/store/1q6v2661bkkzjx48q4n1d3q2ahlsha9q-linux-headers-4.4.10
469 | /nix/store/idwrh4bm5s4lnnb03d1j2b2rqg9x42h6-glibc-2.24-dev
470 | /nix/store/nm9r5lymydnsrlxjn30ym2kix6mbyr19-binutils-2.27
471 | /nix/store/wb8x0kjry7xvh4jqlx391lr0bffqrzhz-gcc-5.4.0
472 | /nix/store/yn4s58frawcqxcid79npmy2aq8cxcjj1-gcc-5.4.0-man
473 | /nix/store/dp2nf60lqzy1kbhd78ndf5nm3fb3qicd-gcc-wrapper-5.4.0
474 | /nix/store/glhpbq9nhyrrzzbnbdg41vn9h717rrr7-gmp-6.1.1-dev
475 | /nix/store/gz5ph3m624zivi687zvy82izi2z39aik-ncurses-6.0
476 | /nix/store/ff3h3dl87xl0q99b963xpwxacsqaqb43-ncurses-6.0-man
477 | /nix/store/i3hxdbjgbagyslsqnfl7zkpnn6q32hxv-ncurses-6.0-dev
478 | /nix/store/dg7ak1hvlj66vgn4fwvddnnr4pfncd04-ghc-8.0.1
479 | /nix/store/pb3jxhy4z54i24i9s0kyszdmxd2xajc5-libtar-1.2.20
480 | /nix/store/1nc7kaw3lp574hhi2bfxdb490q5kp2m8-project3-1.0.0
481 | ```
482 |
483 | After closure minimization we get this dependency tree:
484 |
485 | ```bash
486 | $ nix-build --attr project3-minimal release2.nix
487 | ...
488 | /nix/store/i4fypk5m0rdwribpwacdd1nbzbfqcnpc-project3-minimal
489 | $ nix-store --query --requisites result
490 | /nix/store/jm1n87rp8vr90j9ahcrfzr57nc2r8vgf-glibc-2.24
491 | /nix/store/djd2r4cnbcx4vfbj1qalg85vjbcslwxv-gcc-5.4.0-lib
492 | /nix/store/6xiq93r3dnny4m7svvb6hvq9qwjrixk8-gmp-6.1.1
493 | /nix/store/pb3jxhy4z54i24i9s0kyszdmxd2xajc5-libtar-1.2.20
494 | /nix/store/i4fypk5m0rdwribpwacdd1nbzbfqcnpc-project3-minimal
495 | ```
496 |
497 | Much smaller! Note that the executable is still not fully statically linked.
498 | Only the Haskell dependencies have been statically linked. Most of the
499 | remaining space is due to using `glibc`:
500 |
501 | ```bash
502 | $ du -hs /nix/store/jm1n87rp8vr90j9ahcrfzr57nc2r8vgf-glibc-2.24/
503 | 23M /nix/store/jm1n87rp8vr90j9ahcrfzr57nc2r8vgf-glibc-2.24/
504 | ```
505 |
506 | The actual `project3` executable is tiny in comparison:
507 |
508 | ```bash
509 | $ du -hs /nix/store/i4fypk5m0rdwribpwacdd1nbzbfqcnpc-project3-minimal
510 | 788K /nix/store/i4fypk5m0rdwribpwacdd1nbzbfqcnpc-project3-minimal
511 | ```
512 |
513 | If we wanted to get the size down further we'd need to use something like
514 | `musl` instead of `glibc`.
515 |
516 | # Conclusion
517 |
518 | This concludes the section on exploring and customizing Haskell packages. The
519 | next section covers [simplified dependency management](../project4/README.md).
520 |
521 | [haskell-lib]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/haskell-modules/lib/default.nix
522 | [generic-builder]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/haskell-modules/generic-builder.nix
523 |
--------------------------------------------------------------------------------
/project3/Test.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 |
3 | module Main where
4 |
5 | import Control.Applicative (empty)
6 | import qualified Project3
7 | import qualified Turtle
8 |
9 | main :: IO ()
10 | main = do
11 | Turtle.touch "example"
12 | Turtle.procs "bsdtar" ["cf", "example.tar", "example"] empty
13 | Project3.check
14 |
--------------------------------------------------------------------------------
/project3/cbits/check.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | void check() {
7 | int ret;
8 | TAR *t;
9 |
10 | ret = tar_open(&t, "example.tar", NULL, O_RDONLY, 0, TAR_VERBOSE);
11 | if (ret == -1) {
12 | fprintf(stderr, "tar_open failed\n");
13 | exit(1);
14 | }
15 | printf("Successfully opened example.tar\n");
16 |
17 | ret = tar_close(t);
18 | if (ret == -1) {
19 | fprintf(stderr, "tar_close failed\n");
20 | exit(1);
21 | }
22 | printf("Successfully closed example.tar\n");
23 | }
24 |
--------------------------------------------------------------------------------
/project3/project3.cabal:
--------------------------------------------------------------------------------
1 | name: project3
2 | version: 1.0.0
3 | license: BSD3
4 | license-file: LICENSE
5 | cabal-version: >= 1.18
6 | build-type: Simple
7 | extra-source-files: cbits
8 |
9 | library
10 | build-depends: base < 5
11 | c-sources: cbits/check.c
12 | default-language: Haskell2010
13 | extra-libraries: tar
14 | exposed-modules: Project3
15 |
16 | executable project3
17 | build-depends: base < 5, project3
18 | default-language: Haskell2010
19 | main-is: Main.hs
20 |
21 | test-suite test
22 | build-depends: base < 5, project3, turtle
23 | main-is: Test.hs
24 | type: exitcode-stdio-1.0
25 |
--------------------------------------------------------------------------------
/project3/project3.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, lib, tar, turtle }:
2 | mkDerivation {
3 | pname = "project3";
4 | version = "1.0.0";
5 | src = ./.;
6 | isLibrary = true;
7 | isExecutable = true;
8 | libraryHaskellDepends = [ base ];
9 | librarySystemDepends = [ tar ];
10 | executableHaskellDepends = [ base ];
11 | testHaskellDepends = [ base turtle ];
12 | license = lib.licenses.bsd3;
13 | }
14 |
--------------------------------------------------------------------------------
/project3/release0.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages = pkgs.haskellPackages.override {
5 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
6 | morte = pkgs.haskell.lib.dontHaddock haskellPackagesOld.morte;
7 | };
8 | };
9 | };
10 | };
11 |
12 | pkgs = import { inherit config; };
13 |
14 | in
15 | { morte = pkgs.haskellPackages.morte;
16 | }
17 |
--------------------------------------------------------------------------------
/project3/release1.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages = pkgs.haskellPackages.override {
5 | overrides = haskellPackagesNew: haskellPackgesOld: rec {
6 | project3 =
7 | pkgs.haskell.lib.overrideCabal
8 | ( haskellPackagesNew.callPackage ./project3.nix {
9 | tar = pkgs.libtar;
10 | }
11 | )
12 | ( oldDerivation: {
13 | testToolDepends = [ pkgs.libarchive ];
14 | }
15 | );
16 | };
17 | };
18 | };
19 | };
20 |
21 | pkgs = import { inherit config; };
22 |
23 | in
24 | { project3 = pkgs.haskellPackages.project3;
25 | }
26 |
--------------------------------------------------------------------------------
/project3/release2.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = rec {
3 | packageOverrides = pkgs: rec {
4 | docker-container-large = pkgs.dockerTools.buildImage {
5 | name = "project3-container";
6 | config.Cmd = [ "${haskellPackages.project3}/bin/project3" ];
7 | };
8 |
9 | docker-container-small = pkgs.dockerTools.buildImage {
10 | name = "project3-container";
11 | config.Cmd = [ "${haskellPackages.project3-minimal}/bin/project3" ];
12 | };
13 |
14 | haskellPackages = pkgs.haskellPackages.override {
15 | overrides = haskellPackagesNew: haskellPackgesOld: rec {
16 | project3 =
17 | pkgs.haskell.lib.overrideCabal
18 | ( haskellPackagesNew.callPackage ./project3.nix {
19 | tar = pkgs.libtar;
20 | }
21 | )
22 | ( oldDerivation: {
23 | testToolDepends = [ pkgs.libarchive ];
24 | enableSharedExecutables = false;
25 | }
26 | );
27 |
28 | project3-minimal =
29 | pkgs.haskell.lib.overrideCabal
30 | ( pkgs.haskell.lib.justStaticExecutables
31 | ( haskellPackagesNew.callPackage ./project3.nix {
32 | tar = pkgs.libtar;
33 | }
34 | )
35 | )
36 | ( oldDerivation: {
37 | testToolDepends = [ pkgs.libarchive ];
38 | }
39 | );
40 | };
41 | };
42 | };
43 | };
44 |
45 | pkgs = import { inherit config; system = "x86_64-linux"; };
46 |
47 | in
48 | { project3 = pkgs.haskellPackages.project3;
49 |
50 | project3-minimal = pkgs.haskellPackages.project3-minimal;
51 |
52 | docker-container-small = pkgs.docker-container-small;
53 |
54 | docker-container-large = pkgs.docker-container-large;
55 | }
56 |
--------------------------------------------------------------------------------
/project4/Main.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 |
3 | module Main where
4 |
5 | import Turtle
6 |
7 | main :: IO ()
8 | main = echo "Hello, world!"
9 |
--------------------------------------------------------------------------------
/project4/README.md:
--------------------------------------------------------------------------------
1 | # Advanced dependency management
2 |
3 |
4 |
5 | This section covers how to automate dependency management so that people less
6 | familiar with Nix can easily contribute to your project
7 |
8 | Let's begin by considering a variation on the example from `project1`:
9 |
10 | ```nix
11 | let
12 | config = {
13 | packageOverrides = pkgs: rec {
14 | haskellPackages = pkgs.haskellPackages.override {
15 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
16 | foldl = haskellPackagesNew.callPackage ./nix/foldl.nix { };
17 |
18 | optparse-applicative =
19 | pkgs.haskell.lib.dontCheck
20 | (haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { });
21 |
22 | project4 =
23 | haskellPackagesNew.callPackage ./nix/project4.nix { };
24 |
25 | turtle =
26 | pkgs.haskell.lib.doJailbreak
27 | (haskellPackagesNew.callPackage ./nix/turtle.nix { });
28 | };
29 | };
30 | };
31 | };
32 |
33 | pkgs = import { inherit config; };
34 |
35 | in
36 | { project4 = pkgs.haskellPackages.project4;
37 | }
38 | ```
39 |
40 | Here we pin the versions of three dependencies: `optparse-applicative`, `foldl`,
41 | and `turtle`. Additionally, we disable tests for `optparse-applicative` (since
42 | they fail) and jailbreak `turtle` because our new `foldl` dependency is out of
43 | bounds for `turtle`.
44 |
45 | This time we've moved all of our package derivations underneath the `nix/`
46 | subdirectory so that we can take advantage of the handy `builtins.readDir`
47 | function to automate some dependency boilerplate.
48 |
49 | `release1.nix` shows an example of automating the addition of any Haskell
50 | package that we store underneath the `nix/` subdirectory:
51 |
52 | ```nix
53 | let
54 | config = {
55 | packageOverrides = pkgs: rec {
56 | haskellPackages = pkgs.haskellPackages.override {
57 | overrides = haskellPackagesNew: haskellPackagesOld:
58 | let
59 | toPackage = file: _: {
60 | name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
61 |
62 | value = haskellPackagesNew.callPackage (./. + "/nix/${file}") { };
63 | };
64 |
65 | packages = pkgs.lib.mapAttrs' toPackage (builtins.readDir ./nix);
66 |
67 | in
68 | packages // {
69 | optparse-applicative =
70 | pkgs.haskell.lib.dontCheck
71 | (haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { });
72 |
73 | turtle =
74 | pkgs.haskell.lib.doJailbreak
75 | (haskellPackagesNew.callPackage ./nix/turtle.nix { });
76 | };
77 | };
78 | };
79 | };
80 |
81 | pkgs = import { inherit config; };
82 |
83 | in
84 | { project4 = pkgs.haskellPackages.project4;
85 | }
86 | ```
87 |
88 | In the above code, the`packages` sub-expression expands out to:
89 |
90 | ```haskell
91 | { foldl =
92 | haskellPackagesNew.callPackage ./nix/foldl.nix { };
93 |
94 | optparse-applicative =
95 | haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { };
96 |
97 | project4 =
98 | haskellPackagesNew.callPackage ./nix/project4.nix { };
99 |
100 | turtle =
101 | haskellPackagesNew.callPackage ./nix/turtle.nix { };
102 | }
103 | ```
104 |
105 | ... which we then merge with the record of package-specific tweaks:
106 |
107 | ```nix
108 | { foldl =
109 | haskellPackagesNew.callPackage ./nix/foldl.nix { };
110 |
111 | optparse-applicative =
112 | haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { };
113 |
114 | project4 =
115 | haskellPackagesNew.callPackage ./nix/project4.nix { };
116 |
117 | turtle =
118 | haskellPackagesNew.callPackage ./nix/turtle.nix { };
119 | } // {
120 | optparse-applicative =
121 | pkgs.haskell.lib.dontCheck
122 | (haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { });
123 |
124 | turtle =
125 | pkgs.haskell.lib.doJailbreak
126 | (haskellPackagesNew.callPackage ./nix/turtle.nix { });
127 | };
128 | ```
129 |
130 | The latter record takes precedence when merging the two, so the final result is:
131 |
132 | ```nix
133 | { foldl =
134 | haskellPackagesNew.callPackage ./nix/foldl.nix { };
135 |
136 | project4 =
137 | haskellPackagesNew.callPackage ./nix/project4.nix { };
138 |
139 | optparse-applicative =
140 | pkgs.haskell.lib.dontCheck
141 | (haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { });
142 |
143 | turtle =
144 | pkgs.haskell.lib.doJailbreak
145 | (haskellPackagesNew.callPackage ./nix/turtle.nix { });
146 | };
147 | ```
148 |
149 | ... which matches the set of overrides in our original `release0.nix`
150 |
151 | ```nix
152 | let
153 | config = {
154 | packageOverrides = pkgs: rec {
155 | haskellPackages = pkgs.haskellPackages.override {
156 | overrides = haskellPackagesNew: haskellPackagesOld:
157 | let
158 | toPackage = file: _: {
159 | name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
160 |
161 | value = haskellPackagesNew.callPackage "./nix/${file}" { };
162 | };
163 |
164 | packages = pkgs.lib.mapAttrs toPackage (builtins.readDir ./nix);
165 |
166 | in
167 | builtins.listToAttrs packages // {
168 | optparse-applicative =
169 | pkgs.haskell.lib.dontCheck
170 | (haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { });
171 |
172 | turtle =
173 | pkgs.haskell.lib.doJailbreak
174 | (haskellPackagesNew.callPackage ./nix/turtle.nix { });
175 | };
176 | };
177 | };
178 | };
179 |
180 | pkgs = import { inherit config; };
181 |
182 | in
183 | { project4 = pkgs.haskellPackages.project4;
184 | }
185 | ```
186 |
187 | # Composing overrides
188 |
189 | In the above example we had to throw away some of our auto-generated overrides
190 | because we need to customize them. For example, we need to jailbreak `turtle`,
191 | so we have to repeat the import of the `./nix/turtle.nix` derivation.
192 |
193 | However, we can get the best of both worlds by decomposing our Haskell overrides
194 | into two separate sets of overrides:
195 |
196 | * The first set of overrides we auto-generate from the `nix/` subdirectory
197 | * In the second set of overrides we manually tweak Haskell packages
198 |
199 | In order to do this, we take advantage of a useful utility called
200 | `composeExtensions`. As the name suggests, this lets us combine two extensions
201 | into a single extension to our Haskell package overrides.
202 |
203 | `release2.nix` demonstrates how this works:
204 |
205 | ```nix
206 | let
207 | config = {
208 | packageOverrides = pkgs: rec {
209 | haskellPackages =
210 | let
211 | generatedOverrides = haskellPackagesNew: haskellPackagesOld:
212 | let
213 | toPackage = file: _: {
214 | name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
215 |
216 | value = haskellPackagesNew.callPackage (./. + "/nix/${file}") { };
217 | };
218 |
219 | in
220 | pkgs.lib.mapAttrs' toPackage (builtins.readDir ./nix);
221 |
222 | manualOverrides = haskellPackagesNew: haskellPackagesOld: {
223 | optparse-applicative =
224 | pkgs.haskell.lib.dontCheck
225 | haskellPackagesOld.optparse-applicative;
226 |
227 | turtle =
228 | pkgs.haskell.lib.doJailbreak
229 | haskellPackagesOld.turtle;
230 | };
231 | in
232 | pkgs.haskellPackages.override {
233 | overrides =
234 | pkgs.lib.composeExtensions generatedOverrides manualOverrides;
235 | };
236 | };
237 | };
238 |
239 | pkgs = import { inherit config; };
240 |
241 | in
242 | { project4 = pkgs.haskellPackages.project4;
243 | }
244 | ```
245 |
246 | Composing two extensions applies them in order: first we override the Haskell
247 | derivations with the ones found in the `nix/` subdirectory and then we tweak
248 | some of them by jailbreaking or disabling tests.
249 |
250 | When we modify a derivation like `turtle` we now define the modification in
251 | terms of `haskellPackagesOld`:
252 |
253 | ```haskell
254 | turtle = pkgs.haskell.lib.doJailbreak haskellPackagesOld.turtle;
255 | ```
256 |
257 | This is because `haskellPackagesOld.turtle` now refers to our updated `turtle`
258 | derivation from the preceding generated overrides.
259 |
260 | We can take advantage of this trick to organize our overrides into sections:
261 | one set of overrides for disabling tests, another set of overrides for
262 | jailbreaking packages, and another set of overrides for disabling haddocks.
263 |
264 | The following `release3.nix` shows how to take advantage of Nix's automation
265 | features to streamline such a configuration:
266 |
267 | ```nix
268 | let
269 | # Disable tests for these packages
270 | dontCheckPackages = [
271 | "optparse-applicative"
272 | ];
273 |
274 | # Jailbreak these packages
275 | doJailbreakPackages = [
276 | "turtle"
277 | ];
278 |
279 | # Disable haddocks for these packages
280 | dontHaddockPackages = [
281 | ];
282 |
283 | config = {
284 | packageOverrides = pkgs: rec {
285 | haskellPackages =
286 | let
287 | generatedOverrides = haskellPackagesNew: haskellPackagesOld:
288 | let
289 | toPackage = file: _: {
290 | name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
291 |
292 | value = haskellPackagesNew.callPackage (./. + "/nix/${file}") { };
293 | };
294 |
295 | in
296 | pkgs.lib.mapAttrs' toPackage (builtins.readDir ./nix);
297 |
298 | makeOverrides =
299 | function: names: haskellPackagesNew: haskellPackagesOld:
300 | let
301 | toPackage = name: {
302 | inherit name;
303 |
304 | value = function haskellPackagesOld.${name};
305 | };
306 |
307 | in
308 | builtins.listToAttrs (map toPackage names);
309 |
310 | composeExtensionsList =
311 | pkgs.lib.fold pkgs.lib.composeExtensions (_: _: {});
312 |
313 | # More exotic overrides go here
314 | manualOverrides = haskellPackagesNew: haskellPackagesOld: {
315 | };
316 | in
317 | pkgs.haskellPackages.override {
318 | overrides = composeExtensionsList [
319 | generatedOverrides
320 | (makeOverrides pkgs.haskell.lib.dontCheck dontCheckPackages )
321 | (makeOverrides pkgs.haskell.lib.doJailbreak doJailbreakPackages)
322 | (makeOverrides pkgs.haskell.lib.dontHaddock dontHaddockPackages)
323 | manualOverrides
324 | ];
325 | };
326 | };
327 | };
328 |
329 | pkgs = import { inherit config; };
330 |
331 | in
332 | { project4 = pkgs.haskellPackages.project4;
333 | }
334 | ```
335 |
336 | This sort of automation simplifies the contribution for people less familiar
337 | with Nix. They can add new package versions by running:
338 |
339 | ```bash
340 | $ cabal2nix ... > nix/${PACKAGE_NAME}.nix
341 | ```
342 |
343 | ... and they can control jailbreaking, tests, haddocks from simple lists at the
344 | top of the configuration file. Advanced users can still customize in more
345 | detail inside the `manualOverrides` section.
346 |
347 | The above example uses a handy `composeExtensionsList` function, which allows
348 | you to easily add or subtract sets of extensions. For example, you can turn
349 | haddock generation back on for all packages by just commenting out the line for
350 | haddock package overrides:
351 |
352 | ```nix
353 | overrides = composeExtensionsList [
354 | generatedOverrides
355 | (makeOverrides pkgs.haskell.lib.dontCheck dontCheckPackages )
356 | (makeOverrides pkgs.haskell.lib.doJailbreak doJailbreakPackages)
357 | # (makeOverrides pkgs.haskell.lib.dontHaddock dontHaddockPackages)
358 | manualOverrides
359 | ];
360 | ```
361 |
362 | Similarly, you might require some overrides to get your Haskell IDE to work.
363 | You can add such overrides to the list easily, like this:
364 |
365 | ```nix
366 | overrides = composeExtensionsList [
367 | generatedOverrides
368 | (makeOverrides pkgs.haskell.lib.dontCheck dontCheckPackages )
369 | (makeOverrides pkgs.haskell.lib.doJailbreak doJailbreakPackages)
370 | (makeOverrides pkgs.haskell.lib.dontHaddock dontHaddockPackages)
371 | (./import ~/myHaskellOverrides.nix)
372 | manualOverrides
373 | ];
374 | ```
375 |
376 | Nix is a programming language, so you can easily create these domain-specific
377 | languages for others to configure your Haskell build.
378 |
379 | # Conclusion
380 |
381 | This concludes the section on automating dependency management. This is the
382 | last section of the tutorial.
383 |
--------------------------------------------------------------------------------
/project4/nix/foldl.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, bytestring, comonad, containers
2 | , contravariant, criterion, hashable, mwc-random, primitive
3 | , profunctors, lib, text, transformers, unordered-containers
4 | , vector, vector-builder
5 | }:
6 | mkDerivation {
7 | pname = "foldl";
8 | version = "1.3.0";
9 | sha256 = "1rinr1a18pjwlrk21d9sfg0f954cwdc3bk9jl276ypcf8ydy3yin";
10 | libraryHaskellDepends = [
11 | base bytestring comonad containers contravariant hashable
12 | mwc-random primitive profunctors text transformers
13 | unordered-containers vector vector-builder
14 | ];
15 | benchmarkHaskellDepends = [ base criterion ];
16 | description = "Composable, streaming, and efficient left folds";
17 | license = lib.licenses.bsd3;
18 | }
19 |
--------------------------------------------------------------------------------
/project4/nix/managed.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, lib, transformers }:
2 | mkDerivation {
3 | pname = "managed";
4 | version = "1.0.4";
5 | sha256 = "1nf38g8r9s8b37f6yw9w21b28pd6nvlxfl3gzfbn89m0zswrwv0v";
6 | libraryHaskellDepends = [ base transformers ];
7 | description = "A monad for managed values";
8 | license = lib.licenses.bsd3;
9 | }
10 |
--------------------------------------------------------------------------------
/project4/nix/optparse-applicative.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, ansi-wl-pprint, base, process, QuickCheck, lib
2 | , transformers, transformers-compat
3 | }:
4 | mkDerivation {
5 | pname = "optparse-applicative";
6 | version = "0.13.0.0";
7 | sha256 = "1b0c5fdq8bd070g24vrjrwlq979r8dk8mys6aji9hy1l9pcv3inf";
8 | libraryHaskellDepends = [
9 | ansi-wl-pprint base process transformers transformers-compat
10 | ];
11 | testHaskellDepends = [ base QuickCheck ];
12 | homepage = "https://github.com/pcapriotti/optparse-applicative";
13 | description = "Utilities and combinators for parsing command line options";
14 | license = lib.licenses.bsd3;
15 | }
16 |
--------------------------------------------------------------------------------
/project4/nix/project4.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, base, foldl, lib, text, turtle }:
2 | mkDerivation {
3 | pname = "project4";
4 | version = "1.0.0";
5 | src = ../.;
6 | isLibrary = false;
7 | isExecutable = true;
8 | executableHaskellDepends = [ base foldl text turtle ];
9 | license = lib.licenses.bsd3;
10 | }
11 |
--------------------------------------------------------------------------------
/project4/nix/turtle.nix:
--------------------------------------------------------------------------------
1 | { mkDerivation, ansi-wl-pprint, async, base, bytestring, clock
2 | , directory, doctest, foldl, hostname, managed, optional-args
3 | , optparse-applicative, process, lib, stm, system-fileio
4 | , system-filepath, temporary, text, time, transformers, unix
5 | , unix-compat
6 | }:
7 | mkDerivation {
8 | pname = "turtle";
9 | version = "1.3.2";
10 | sha256 = "0pbvkqqhiaddyhlqcrk48w7li81dijw92wwhchwqh1my1363n5pq";
11 | libraryHaskellDepends = [
12 | ansi-wl-pprint async base bytestring clock directory foldl hostname
13 | managed optional-args optparse-applicative process stm
14 | system-fileio system-filepath temporary text time transformers unix
15 | unix-compat
16 | ];
17 | testHaskellDepends = [ base doctest ];
18 | description = "Shell programming, Haskell-style";
19 | license = lib.licenses.bsd3;
20 | }
21 |
--------------------------------------------------------------------------------
/project4/project4.cabal:
--------------------------------------------------------------------------------
1 | name: project4
2 | version: 1.0.0
3 | license: BSD3
4 | license-file: LICENSE
5 | cabal-version: >= 1.18
6 | build-type: Simple
7 |
8 | executable project4
9 | build-depends: base < 5, foldl, text, turtle
10 | main-is: Main.hs
11 | default-language: Haskell2010
12 |
--------------------------------------------------------------------------------
/project4/release0.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages = pkgs.haskellPackages.override {
5 | overrides = haskellPackagesNew: haskellPackagesOld: rec {
6 | foldl = haskellPackagesNew.callPackage ./nix/foldl.nix { };
7 |
8 | optparse-applicative =
9 | pkgs.haskell.lib.dontCheck
10 | (haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { });
11 |
12 | project4 =
13 | haskellPackagesNew.callPackage ./nix/project4.nix { };
14 |
15 | turtle =
16 | pkgs.haskell.lib.doJailbreak
17 | (haskellPackagesNew.callPackage ./nix/turtle.nix { });
18 | };
19 | };
20 | };
21 | };
22 |
23 | pkgs = import { inherit config; };
24 |
25 | in
26 | { project4 = pkgs.haskellPackages.project4;
27 | }
28 |
--------------------------------------------------------------------------------
/project4/release1.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages = pkgs.haskellPackages.override {
5 | overrides = haskellPackagesNew: haskellPackagesOld:
6 | let
7 | toPackage = file: _: {
8 | name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
9 |
10 | value = haskellPackagesNew.callPackage (./. + "/nix/${file}") { };
11 | };
12 |
13 | packages = pkgs.lib.mapAttrs' toPackage (builtins.readDir ./nix);
14 |
15 | in
16 | packages // {
17 | optparse-applicative =
18 | pkgs.haskell.lib.dontCheck
19 | (haskellPackagesNew.callPackage ./nix/optparse-applicative.nix { });
20 |
21 | turtle =
22 | pkgs.haskell.lib.doJailbreak
23 | (haskellPackagesNew.callPackage ./nix/turtle.nix { });
24 | };
25 | };
26 | };
27 | };
28 |
29 | pkgs = import { inherit config; };
30 |
31 | in
32 | { project4 = pkgs.haskellPackages.project4;
33 | }
34 |
--------------------------------------------------------------------------------
/project4/release2.nix:
--------------------------------------------------------------------------------
1 | let
2 | config = {
3 | packageOverrides = pkgs: rec {
4 | haskellPackages =
5 | let
6 | generatedOverrides = haskellPackagesNew: haskellPackagesOld:
7 | let
8 | toPackage = file: _: {
9 | name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
10 |
11 | value = haskellPackagesNew.callPackage (./. + "/nix/${file}") { };
12 | };
13 |
14 | in
15 | pkgs.lib.mapAttrs' toPackage (builtins.readDir ./nix);
16 |
17 | manualOverrides = haskellPackagesNew: haskellPackagesOld: {
18 | optparse-applicative =
19 | pkgs.haskell.lib.dontCheck
20 | haskellPackagesOld.optparse-applicative;
21 |
22 | turtle =
23 | pkgs.haskell.lib.doJailbreak
24 | haskellPackagesOld.turtle;
25 | };
26 | in
27 | pkgs.haskellPackages.override {
28 | overrides =
29 | pkgs.lib.composeExtensions generatedOverrides manualOverrides;
30 | };
31 | };
32 | };
33 |
34 | pkgs = import { inherit config; };
35 |
36 | in
37 | { project4 = pkgs.haskellPackages.project4;
38 | }
39 |
--------------------------------------------------------------------------------
/project4/release3.nix:
--------------------------------------------------------------------------------
1 | let
2 | # Disable tests for these packages
3 | dontCheckPackages = [
4 | "optparse-applicative"
5 | ];
6 |
7 | # Jailbreak these packages
8 | doJailbreakPackages = [
9 | "turtle"
10 | ];
11 |
12 | # Disable haddocks for these packages
13 | dontHaddockPackages = [
14 | ];
15 |
16 | config = {
17 | packageOverrides = pkgs: rec {
18 | haskellPackages =
19 | let
20 | generatedOverrides = haskellPackagesNew: haskellPackagesOld:
21 | let
22 | toPackage = file: _: {
23 | name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
24 |
25 | value = haskellPackagesNew.callPackage (./. + "/nix/${file}") { };
26 | };
27 |
28 | in
29 | pkgs.lib.mapAttrs' toPackage (builtins.readDir ./nix);
30 |
31 | makeOverrides =
32 | function: names: haskellPackagesNew: haskellPackagesOld:
33 | let
34 | toPackage = name: {
35 | inherit name;
36 |
37 | value = function haskellPackagesOld.${name};
38 | };
39 |
40 | in
41 | builtins.listToAttrs (map toPackage names);
42 |
43 | composeExtensionsList =
44 | pkgs.lib.fold pkgs.lib.composeExtensions (_: _: {});
45 |
46 | # More exotic overrides go here
47 | manualOverrides = haskellPackagesNew: haskellPackagesOld: {
48 | };
49 | in
50 | pkgs.haskellPackages.override {
51 | overrides = composeExtensionsList [
52 | generatedOverrides
53 | (makeOverrides pkgs.haskell.lib.dontCheck dontCheckPackages )
54 | (makeOverrides pkgs.haskell.lib.doJailbreak doJailbreakPackages)
55 | (makeOverrides pkgs.haskell.lib.dontHaddock dontHaddockPackages)
56 | manualOverrides
57 | ];
58 | };
59 | };
60 | };
61 |
62 | pkgs = import { inherit config; };
63 |
64 | in
65 | { project4 = pkgs.haskellPackages.project4;
66 | }
67 |
--------------------------------------------------------------------------------