diff --git a/.gitignore b/.gitignore index 3bc1d11e1..c5f1b4b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ site *~ *#* .#* +*.swp +result* +dist-newstyle diff --git a/default.nix b/default.nix new file mode 100644 index 000000000..72613014c --- /dev/null +++ b/default.nix @@ -0,0 +1,56 @@ +let + name = + "postgrest"; + + # PostgREST source files, filtered based on the rules in the .gitignore files + # and file extensions. We want to include as litte as possible, as the files + # added here will increase the space used in the Nix store and trigger the + # build of new Nix derivations when changed. + src = + pkgs.lib.sourceFilesBySuffices + (pkgs.gitignoreSource ./.) + [ ".cabal" ".hs" ".lhs" "LICENSE" ]; + + nixpkgsVersion = + import nix/nixpkgs-version.nix; + + pinnedPkgs = + builtins.fetchTarball { + url = "https://github.com/nixos/nixpkgs/archive/${nixpkgsVersion.rev}.tar.gz"; + sha256 = nixpkgsVersion.tarballHash; + }; + + overlays = + [ + (import nix/overlays/gitignore.nix) + ]; + + pkgs = + import pinnedPkgs { inherit overlays; }; + + drv = + pkgs.haskellPackages.callCabal2nix name src {}; +in +rec { + inherit pkgs pinnedPkgs; + + # Derivation for the PostgREST Haskell package, including the executable, + # libraries and documentation. We disable running the test suite on Nix + # builds, as they require a database to be set up. + postgrestWithLib = + pkgs.haskell.lib.dontCheck drv; + + # Derivation for just the PostgREST binary, where we strip all dynamic + # libraries and documentation, leaving only the executable. + postgrest = + pkgs.haskell.lib.justStaticExecutables postgrestWithLib; + + # Environment in which PostgREST can be built with cabal, useful e.g. for + # defining a shell for nix-shell. + env = + drv.env; + + # Utility for updating the pinned version of Nixpkgs. + nixpkgsUpgrade = + pkgs.callPackage nix/nixpkgs-upgrade.nix {}; +} diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 000000000..84f84df9a --- /dev/null +++ b/nix/README.md @@ -0,0 +1,77 @@ +# Nix development and build environment + +With Nix it's possible to quickly and reliably recreate the full environments +for developing, testing and building PostgREST. + +## Getting started with Nix + +You'll need to [get Nix](https://nixos.org/download.html). The installer will +create your Nix store in the `/nix/` directory, where all build artifacts and +their dependencies will be stored. It will also link the Nix executables like +`nix-env`, `nix-build` and `nix-shell` into your PATH. Nix will manage all +other PostgREST dependencies from here on out. To clean up older build +artifacts from the `/nix/store`, you can run `nix-collect-garbage`. + +## Building PostgREST + +To build PostgREST from your local checkout of the repository, run: + +```bash +nix-build --attr postgrest + +``` + +This will create a `result` directory that contains the PostgREST binary at +`result/bin/postgrest`. The `--attr` parameter (or short: `-A`) tells Nix to +build the `postgrest` attribute from the Nix expression it finds in our +`default.nix` (see below for details). Nix will take care of getting the right +GHC version and all the build dependencies. + +## Developing + +A development environment for PostgREST is available with `nix-shell`. The +following command will put you into a new shell that has GHC and Cabal on the +PATH: + +```bash +nix-shell + +``` + +Within `nix-shell`, you can run Cabal commands as usual. + +## Tour + +The following is not required for working on PostgREST with Nix, but it will +give you some more background and details on how it works. + +### `default.nix` + +[`default.nix`](../default.nix) is our 'respository expression' that pulls all +the pieces that we define with Nix together. It returns a set (like a dict in +other programming languages), where each attribute is a derivation that Nix +knows how to build, like the `postgrest` attribute from earlier. + +Internally, our `default.nix` uses the `pkgs.callPackage` function to import +the modules that we defined in the `nix` directory. It automatically passes the +arguments those modules require if they are available in `pkgs` (this means +that `pkgs` is defined in terms of itself, better not to think too much about +that). + +We also use `default.nix` to load our pinned version of the `nixpkgs` +repository. This set of packages will always be the same, independently from +where or when you use it. The pinned version can be upgraded with the small +`nixpkgs-upgrade` utility. Running `nixpkgs-upgrade > nix/nixpkgs-version.nix` +in `nix-shell` will upgrade the pinned version to the latest `nixpkgs-unstable` +version. + +### `shell.nix` + +[`shell.nix`](../shell.nix) defines an environment in which PostgREST can be +built and developed. It extends the build enviroment from our `postgrest` +attribute with useful utilities that will be put on the PATH in `nix-shell`. + +### `nix/overlays` + +Our overlays to the Nix package set are defined here. They allow us to tweak our +`pkgs` in `default.nix` by adding new packages or overriding existing ones. diff --git a/nix/nixpkgs-upgrade.nix b/nix/nixpkgs-upgrade.nix new file mode 100755 index 000000000..8860a6622 --- /dev/null +++ b/nix/nixpkgs-upgrade.nix @@ -0,0 +1,41 @@ +{ curl +, jq +, writeShellScriptBin +, nix +}: +# Utility script for pinning the latest unstable version of Nixpkgs. + +# Instead of pinning Nixpkgs based on the huge Git repository, we reference a +# specific tarball that only contains the source of the revision that we want +# to pin. +let + name = + "nixpkgs-upgrade"; + + refUrl = + https://api.github.com/repos/nixos/nixpkgs/git/ref/heads/nixpkgs-unstable; + + githubV3Header = + "Accept: application/vnd.github.v3+json"; + + tarballUrlBase = + https://github.com/nixos/nixpkgs/archive/; +in +writeShellScriptBin name + '' + set -euo pipefail + + commitHash="$(${curl}/bin/curl "${refUrl}" -H "${githubV3Header}" | ${jq}/bin/jq -r .object.sha)" + tarballUrl="${tarballUrlBase}$commitHash.tar.gz" + tarballHash="$(${nix}/bin/nix-prefetch-url --unpack "$tarballUrl")" + currentDate="$(date --iso)" + + cat << EOF + # Pinned version of Nixpkgs, generated with ${name}. + { + date = "$currentDate"; + rev = "$commitHash"; + tarballHash = "$tarballHash"; + } + EOF + '' diff --git a/nix/nixpkgs-version.nix b/nix/nixpkgs-version.nix new file mode 100644 index 000000000..4607daded --- /dev/null +++ b/nix/nixpkgs-version.nix @@ -0,0 +1,6 @@ +# Pinned version of Nixpkgs, generated with nixpkgs-upgrade. +{ + date = "2020-04-19"; + rev = "10100a97c8964e82b30f180fda41ade8e6f69e41"; + tarballHash = "011f36kr3c1ria7rag7px26bh73d1b0xpqadd149bysf4hg17rln"; +} diff --git a/nix/overlays/gitignore.nix b/nix/overlays/gitignore.nix new file mode 100644 index 000000000..6581ab8c7 --- /dev/null +++ b/nix/overlays/gitignore.nix @@ -0,0 +1,19 @@ +self: super: +# Overlay that adds the `gitignoreSource` function from Hercules-CI. +# This function is useful for filtering which files are added to the Nix store. +# See: https://github.com/hercules-ci/gitignore.nix + +# To update to a newer revision, the simplest way is to add a new commit hash +# from GitHub under `rev` and to then add the hash that Nix suggests on first +# use. +{ + gitignoreSource = + let + gitignoreSrc = super.fetchFromGitHub { + owner = "hercules-ci"; + repo = "gitignore"; + rev = "2ced4519f865341adcb143c5d668f955a2cb997f"; + sha256 = "sha256:0fc5bgv9syfcblp23y05kkfnpgh3gssz6vn24frs8dzw39algk2z"; + }; + in (super.callPackage gitignoreSrc {}).gitignoreSource; +} diff --git a/postgrest.cabal b/postgrest.cabal index 2f40cf93f..6e5dc62aa 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -88,6 +88,13 @@ library default-extensions: OverloadedStrings QuasiQuotes NoImplicitPrelude + ghc-options: -O2 -Werror -Wall -fwarn-identities + -fno-spec-constr -optP-Wno-nonportable-include-path + -- -fno-spec-constr may help keep compile time memory use in check, + -- see https://gitlab.haskell.org/ghc/ghc/issues/16017#note_219304 + -- -optP-Wno-nonportable-include-path + -- prevents build failures on case-insensitive filesystems (macos), + -- see https://github.com/commercialhaskell/stack/issues/3918 executable postgrest main-is: Main.hs @@ -114,6 +121,8 @@ executable postgrest QuasiQuotes NoImplicitPrelude ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I2" + -O2 -Werror -Wall -fwarn-identities + -fno-spec-constr -optP-Wno-nonportable-include-path if !os(windows) build-depends: unix diff --git a/shell.nix b/shell.nix new file mode 100644 index 000000000..69bca8533 --- /dev/null +++ b/shell.nix @@ -0,0 +1,12 @@ +with (import ./default.nix); +pkgs.lib.overrideDerivation env ( + base: { + buildInputs = + base.buildInputs ++ [ + pkgs.cabal-install + pkgs.cabal2nix + pkgs.postgresql + nixpkgsUpgrade + ]; + } +)