├── .gitignore ├── .travis.yml ├── 00-introduction-to-nix └── run.sh ├── 01-normal-haskell-development ├── .gitignore └── run.sh ├── 02-nix-based-development ├── .gitignore └── run.sh ├── 03-finding-system-libraries ├── .gitignore ├── nixpkgs-config.nix └── run.sh ├── 04-ghc-with-packages ├── .gitignore └── run.sh ├── 05-registering-your-own-packages ├── .gitignore └── run.sh ├── LICENSE ├── README.md └── docker-run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | result 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # .travis.yml 2 | 3 | services: 4 | - docker 5 | 6 | addons: 7 | apt: 8 | packages: 9 | - docker-ce 10 | 11 | script: 12 | - ./docker-run.sh bash -e -c 'cd ~/workshop && for n in */run.sh; do echo $n; $n; done' 13 | -------------------------------------------------------------------------------- /00-introduction-to-nix/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -eu 4 | 5 | # Interpret the Nix language. 6 | nix-instantiate --eval -E '"hello"' # prints: "hello" 7 | nix-instantiate --eval -E '1 + 2' # prints: 3 8 | nix-instantiate --eval -E '[ "hello" (1 + 2) ]' # prints: [ "hello" ] 9 | nix-instantiate --eval --strict -E '[ "hello" (2 - 1) ]' # prints: [ "hello" 3 ] 10 | nix-instantiate --eval --strict -E ' 11 | with import {}; 12 | let 13 | fib = n: if n == 0 then 0 else 14 | if n == 1 then 1 else 15 | fib (n - 2) + fib (n - 1); 16 | in 17 | map fib (lib.range 0 10)' 18 | 19 | # Create a store derivation. 20 | nix-instantiate 2>/dev/null -E ' 21 | builtins.derivation { 22 | name = "test-0"; 23 | system = "x86_64-linux"; 24 | builder = "/bin/sh"; 25 | args = ["-c" "echo Hello, Nix."]; 26 | }' 27 | 28 | # Realize a store path. 29 | drv=$(nix-instantiate 2>/dev/null -E ' 30 | with import {}; 31 | builtins.derivation { 32 | name = "test-0"; 33 | system = "x86_64-linux"; 34 | builder = "/bin/sh"; 35 | args = ["-c" "${coreutils}/bin/ls -la /nix/store >$out"]; 36 | }') 37 | nix-store 2>/dev/null --realize $drv 38 | 39 | # Now for real. 40 | nix-build --no-out-link -E ' 41 | with import {}; 42 | builtins.derivation { 43 | name = "test-0"; 44 | system = "x86_64-linux"; 45 | PATH = lib.makeBinPath [coreutils hello]; 46 | builder = writeScript "my-build-script" 47 | "#! ${bash}/bin/bash 48 | echo PATH=$PATH | tee $out 49 | hello 50 | "; 51 | }' 52 | -------------------------------------------------------------------------------- /01-normal-haskell-development/.gitignore: -------------------------------------------------------------------------------- 1 | /mtl-compat-0.2.1.3 2 | -------------------------------------------------------------------------------- /01-normal-haskell-development/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -eu 4 | cd "$(dirname "$0")" 5 | 6 | # Get the source code of the desired Haskell package. 7 | cabal new-update 8 | rm -rf mtl-compat-0.2.1.3 9 | cabal get mtl-compat-0.2.1.3 10 | cd mtl-compat-0.2.1.3 11 | 12 | # Build it with cabal-install. 13 | cabal new-build 14 | 15 | # Build it with stack. 16 | stack config set system-ghc --global true 17 | stack config set install-ghc --global false 18 | # "stack init" currently doesn't work. It detects lts-12.16, but the 19 | # nixpkgs-unstable channel hasn't updated to ghc-8.4.4 yet. To avoid 20 | # this issue for the time being, we hard-code an older version. 21 | echo > stack.yaml "resolver: lts-12.14" 22 | echo >>stack.yaml "packages:" 23 | echo >>stack.yaml " - ." 24 | 25 | # Build it with a more recent version of GHC. 26 | nix-shell -p haskell.compiler.ghc861 --run "cabal new-build" 27 | -------------------------------------------------------------------------------- /02-nix-based-development/.gitignore: -------------------------------------------------------------------------------- 1 | /nix-derivation-1.0.1 2 | -------------------------------------------------------------------------------- /02-nix-based-development/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -eu 4 | cd "$(dirname "$0")" 5 | 6 | # Get the source code of the desired Haskell package. 7 | rm -rf nix-derivation-1.0.1 8 | cabal get nix-derivation-1.0.1 9 | cd nix-derivation-1.0.1 10 | 11 | # Enter a temporary development environment from Nixpkgs. 12 | # The --allow-newer is due to https://github.com/Gabriel439/Haskell-Nix-Derivation-Library/issues/1. 13 | nix-shell "" -A haskellPackages.nix-derivation.env --run "cabal v1-configure --allow-newer" 14 | cabal v1-build 15 | 16 | # Generate a development environment with cabal2nix. 17 | cabal2nix --shell . >shell.nix 18 | cabal v1-clean 19 | nix-shell --run "cabal v1-configure --enable-test --allow-newer" 20 | cabal v1-test 21 | 22 | # Interactive development. 23 | : cabal v1-repl lib:nix-derivation 24 | -------------------------------------------------------------------------------- /03-finding-system-libraries/.gitignore: -------------------------------------------------------------------------------- 1 | /hopenssl-2.2.2 2 | -------------------------------------------------------------------------------- /03-finding-system-libraries/nixpkgs-config.nix: -------------------------------------------------------------------------------- 1 | # ~/config/nixpkgs/config.nix 2 | 3 | { pkgs }: { 4 | 5 | packageOverrides = super: let self = super.pkgs; in { 6 | 7 | system-libraries-env = self.buildEnv { 8 | name = "system-libraries-0"; 9 | paths = [self.openssl_1_1]; 10 | extraOutputsToInstall = ["out" "dev"]; 11 | }; 12 | 13 | }; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /03-finding-system-libraries/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -eu 4 | cd "$(dirname "$0")" 5 | 6 | # Get the source code of the desired Haskell package. 7 | rm -rf hopenssl-2.2.2 8 | cabal get hopenssl-2.2.2 9 | cd hopenssl-2.2.2 10 | 11 | # Set up development environment. 12 | cabal2nix --shell . >shell.nix 13 | 14 | # Configure won't give a working build because of the missing OpenSSL library. 15 | nix-shell --run "cabal v1-configure" 16 | cabal v1-build || true 17 | 18 | # Check which outputs exist for OpenSSL. 19 | nix-instantiate --eval "" -A openssl.outputs 20 | inc=$(nix-build --no-out-link "" -A openssl.dev) 21 | lib=$(nix-build --no-out-link "" -A openssl.out) 22 | nix-shell --run "cabal v1-configure --extra-include-dirs=$inc/include --extra-lib-dirs=$lib/lib --enable-test" 23 | 24 | # Configure a global extra search path. 25 | echo >>~/.cabal/config "extra-include-dirs: $HOME/.nix-profile/include" 26 | echo >>~/.cabal/config "extra-lib-dirs: $HOME/.nix-profile/lib" 27 | install -D ../nixpkgs-config.nix ~/.config/nixpkgs/config.nix 28 | nix-env -f "" --install --attr system-libraries-env 29 | 30 | # Write a cabal.project.local file for cabal new-build. 31 | cat >cabal.project.local < stack.yaml "resolver: lts-12.14" 45 | echo >>stack.yaml "packages:" 46 | echo >>stack.yaml " - ." 47 | echo >>stack.yaml "" 48 | echo >>stack.yaml "extra-include-dirs: [ $inc/include ]" 49 | echo >>stack.yaml "extra-lib-dirs: [ $lib/lib ]" 50 | stack build 51 | 52 | rm -rf stack.yaml .stack-work 53 | echo >>~/.stack/config.yaml "" 54 | echo >>~/.stack/config.yaml "extra-include-dirs: [ $HOME/.nix-profile/include ]" 55 | echo >>~/.stack/config.yaml "extra-lib-dirs: [ $HOME/.nix-profile/lib ]" 56 | # "stack init" currently doesn't work. It detects lts-12.16, but the 57 | # nixpkgs-unstable channel hasn't updated to ghc-8.4.4 yet. To avoid 58 | # this issue for the time being, we hard-code an older version. 59 | echo > stack.yaml "resolver: lts-12.14" 60 | echo >>stack.yaml "packages:" 61 | echo >>stack.yaml " - ." 62 | stack build 63 | -------------------------------------------------------------------------------- /04-ghc-with-packages/.gitignore: -------------------------------------------------------------------------------- 1 | /mustache-2.3.0 2 | -------------------------------------------------------------------------------- /04-ghc-with-packages/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -eu 4 | cd "$(dirname "$0")" 5 | 6 | # Get the source code of the desired Haskell package. 7 | rm -rf mustache-2.3.0 8 | cabal get mustache-2.3.0 9 | cd mustache-2.3.0 10 | 11 | # Construct a GHC environment with all necessary packages. 12 | nix-shell --run "cabal v1-configure --enable-test" -p ' 13 | haskellPackages.ghcWithPackages (pset: with pset; [ 14 | aeson base-unicode-symbols cmdargs either hspec lens 15 | tar temporary text th-lift unordered-containers wreq 16 | yaml zlib 17 | ])' 18 | cabal v1-build 19 | 20 | # Now include hoogle databases and haddock information. 21 | nix-shell --run "cabal v1-configure --enable-test" -p ' 22 | haskellPackages.ghcWithHoogle (pset: with pset; [ 23 | aeson base-unicode-symbols cmdargs either hspec lens 24 | tar temporary text th-lift unordered-containers wreq 25 | yaml zlib 26 | ])' 27 | cabal v1-build 28 | 29 | # Make this compiler our default GHC. 30 | mkdir -p ~/.config/nixpkgs 31 | cat <~/.config/nixpkgs/config.nix 32 | { 33 | packageOverrides = super: let self = super.pkgs; in { 34 | 35 | haskell-env = self.haskellPackages.ghcWithHoogle (pset: with pset; [ 36 | aeson base-unicode-symbols cmdargs either hspec lens 37 | tar temporary text th-lift unordered-containers wreq 38 | yaml zlib 39 | ]); 40 | }; 41 | } 42 | EOF 43 | nix-env -f "" -iA haskell-env 44 | -------------------------------------------------------------------------------- /05-registering-your-own-packages/.gitignore: -------------------------------------------------------------------------------- 1 | /cabal 2 | -------------------------------------------------------------------------------- /05-registering-your-own-packages/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -eu 4 | cd "$(dirname "$0")" 5 | 6 | # Get the source code of the desired Haskell package. 7 | test -d cabal || { 8 | nix-env -f "" -iA git 9 | git clone --depth=1 git://github.com/haskell/cabal.git 10 | } 11 | 12 | cd cabal/Cabal && cabal2nix . >default.nix && cd - 13 | cd cabal/cabal-install && cabal2nix . >default.nix && cd - 14 | 15 | # Register the builds in the package set. 16 | cat <~/.config/nixpkgs/config.nix 17 | { pkgs }: 18 | 19 | { 20 | packageOverrides = super: let self = super.pkgs; in { 21 | 22 | haskellPackages = super.haskellPackages.override { 23 | 24 | overrides = self: super: { 25 | 26 | Cabal-git = self.callPackage $PWD/cabal/Cabal {}; 27 | 28 | cabal-install-git = (self.callPackage $PWD/cabal/cabal-install {}).overrideScope (self: super: { 29 | Cabal = self.Cabal-git; 30 | }); 31 | 32 | }; 33 | 34 | }; 35 | 36 | cabal-install-git = self.haskellPackages.cabal-install-git; 37 | 38 | }; 39 | 40 | } 41 | EOF 42 | 43 | # Install it. 44 | nix-env -f "" -iA cabal-install-git 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Peter Simons 2 | Copyright 2017 Bas van Dijk 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Advanced Haskell Development with Nix 2 | ===================================== 3 | 4 | A workshop at [Haskell eXchange 2017] hosted by: 5 | 6 | * [Peter Simons] 7 | * [Bas van Dijk] 8 | 9 | This repository contains the workshop materials. 10 | 11 | Abstract 12 | ======== 13 | 14 | This workshop introduces Haskell developers to the purely functional package 15 | manager [Nix] through a series of interactive practical examples. The first 16 | hour of the workshop focuses on practical use of Nix as a tool for *Haskell 17 | developers*: we'll examine how to create declarative reproducible development 18 | environments for advanced Haskell hacking, how to extend and customize the Nix 19 | package set with our own libraries, and how to integrate external system 20 | libraries into our build processes. 21 | 22 | The second hour looks at Nix as a tool for *Haskell application deployment*: 23 | we'll use Nix to manage the development, continuous integration, and deployment 24 | of a non-trivial GHCJS web application with a Haskell back-end and a PostgreSQL 25 | database. (See the [nixtodo repo](https://github.com/basvandijk/nixtodo)). 26 | 27 | We assume no prior knowledge of Nix, but we do assume that participants have a 28 | basic understanding of basic Haskell development with `Cabal`, `cabal-install`, 29 | or `stack`. All examples used in this workshop live in the Git repository 30 | . We encourage participants to 31 | clone that repository and to make sure they have access to a working Nix 32 | installation *before* the event starts. We provide a [Docker 33 | image](https://hub.docker.com/r/psimons/hex2017/) for these purposes that users 34 | can enter by running the 35 | [`docker-run.sh`](https://github.com/basvandijk/nix-workshop/blob/master/docker-run.sh) 36 | script found in the `nix-workshop` repository. Participants who can't or don't 37 | want to use Docker should please install Nix by other means as explained on the 38 | page . 39 | 40 | ------------------------------------------------------------------------------- 41 | 42 | # Get Access to Nix 43 | 44 | - Install Nix: 45 | 46 | - Run `./docker-run.sh` in checkout of https://github.com/basvandijk/nix-workshop. 47 | 48 | - `curl https://nixos.org/nix/install | sh` 49 | 50 | - https://nixos.org/nix/download.html 51 | 52 | - Install NixOS: 53 | 54 | - https://nixos.org/nixos/download.html 55 | 56 | ------------------------------------------------------------------------------- 57 | 58 | # Get Access to Nixpkgs 59 | 60 | $ git clone git://github.com/NixOS/nixpkgs.git 61 | 62 | $ git clone git://github.com/NixOS/nixpkgs-channels.git 63 | 64 | $ nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs 65 | $ nix-channel --update 66 | 67 | $ nix-channel --add https://nixos.org/channels/nixos-17.09 nixpkgs 68 | $ nix-channel --update 69 | 70 | ------------------------------------------------------------------------------- 71 | 72 | # Popular Nix Commands 73 | 74 | $ nix-env --install --dry-run ghc cabal-install 75 | $ nix-env -qaP --description Hackage 76 | 77 | $ nix-channel --list 78 | 79 | $ nix-shell --pure -p ghc 80 | $ nix-shell "" -A ghc 81 | 82 | $ nix-build --no-out-link "" -A ghc 83 | 84 | $ nix-instantiate "" -A stdenv 85 | 86 | $ nix-collect-garbage --delete-older-than 30d 87 | 88 | 89 | 90 | [Peter Simons]: https://github.com/peti 91 | [Bas van Dijk]: https://github.com/basvandijk 92 | [Haskell eXchange 2017]: https://skillsmatter.com/conferences/8522-haskell-exchange-2017 93 | [Nix]: https://nixos.org/nix/ 94 | -------------------------------------------------------------------------------- /docker-run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Change into the directory that contains this script. 4 | cd "$(dirname "$0")" || exit 1 5 | 6 | # Make sure we have the latest version of the container. 7 | docker pull psimons/hex2017 8 | 9 | # Enter the virtual environment and map this repository 10 | # into the container at ~root/workshop. 11 | exec docker run \ 12 | --interactive \ 13 | --tty \ 14 | -v "$PWD:/root/workshop" \ 15 | psimons/hex2017 \ 16 | "$@" 17 | --------------------------------------------------------------------------------