├── .buildkite └── pipeline.yml ├── .github └── FUNDING.yml ├── .gitmodules ├── .in-static-haskell-nix ├── Main.hs ├── README.md ├── Setup.hs ├── ci.nix ├── default.nix ├── example-scotty-app.cabal ├── nixpkgs.nix ├── setup-git-hooks ├── static-stack ├── README.md ├── build-static-stack.sh └── default.nix ├── static-stack2nix-builder-example ├── .gitignore ├── README.md ├── Setup.hs ├── app │ └── Main.hs ├── default.nix ├── example-project.cabal ├── package.yaml ├── src │ └── Lib.hs └── stack.yaml ├── static-stack2nix-builder ├── default.nix └── stack2nix-script.nix ├── submodules-update ├── survey └── default.nix └── update-example-commits.sh /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | 3 | # The order here is defined for fast feedback when breaking something. 4 | 5 | # -O0 builds come first for fast feedback. 6 | 7 | - label: (-O0) -A working 8 | command: | 9 | NIX_PATH=nixpkgs=nixpkgs nix-build --no-link survey/default.nix \ 10 | --arg disableOptimization true -A working 11 | 12 | - label: (-O0) -A workingStackageExecutables 13 | command: | 14 | NIX_PATH=nixpkgs=nixpkgs nix-build --no-link survey/default.nix \ 15 | --arg disableOptimization true -A workingStackageExecutables 16 | 17 | # Normal builds 18 | 19 | - label: -A working 20 | command: | 21 | NIX_PATH=nixpkgs=nixpkgs nix-build --no-link survey/default.nix \ 22 | -A working 23 | 24 | - label: -A workingStackageExecutables 25 | command: | 26 | NIX_PATH=nixpkgs=nixpkgs nix-build --no-link survey/default.nix \ 27 | -A workingStackageExecutables 28 | 29 | # integer-simple builds 30 | 31 | # Doesn't pass yet 32 | # - label: (integer-simple) 33 | # command: | 34 | # NIX_PATH=nixpkgs=nixpkgs nix-build --no-link survey/default.nix \ 35 | # --arg integer-simple true -A working -A workingStackageExecutables 36 | 37 | # Note on GHC versions: 38 | # It really only makes sense to test the compiler version matching the version 39 | # of Stackage that was the base for nixpkgs's `haskellPackages`. 40 | # Any other GHC version should be tested via `stack2nix` builds. 41 | 42 | # Other 43 | 44 | - label: stack2nix-example 45 | command: | 46 | cd static-stack2nix-builder-example/ && $(nix-build --no-link -A fullBuildScript) 47 | 48 | # Stack via stack2nix 49 | 50 | - label: static-stack 51 | command: "cd static-stack && ./build-static-stack.sh" 52 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: static-haskell-nix 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nixpkgs"] 2 | path = nixpkgs 3 | url = https://github.com/NixOS/nixpkgs.git 4 | -------------------------------------------------------------------------------- /.in-static-haskell-nix: -------------------------------------------------------------------------------- 1 | The presence of this file indicates that we're inside the static-haskell-nix project. 2 | -------------------------------------------------------------------------------- /Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | import Web.Scotty 4 | 5 | import Data.Monoid (mconcat) 6 | 7 | main = scotty 3000 $ do 8 | get "/:word" $ do 9 | beam <- param "word" 10 | html $ mconcat ["

Scotty, ", beam, " me up!

"] 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Funding button](https://opencollective.com/static-haskell-nix/tiers/backer/badge.svg?label=Fund%20this%20project%20on%20OpenCollective.%20Existing%20backers%3A&color=brightgreen)](https://opencollective.com/static-haskell-nix) [![Buildkite build status](https://badge.buildkite.com/4e51728716c0939ac47c5ebd005429c90b8a06fd7e3e15f7d3.svg)](https://buildkite.com/nh2/static-haskell-nix) 2 | 3 | # static-haskell-nix 4 | 5 | With this repository you can easily build most Haskell programs into fully static Linux executables. 6 | 7 | * results are fully static executables (`ldd` says `not a dynamic executable`) 8 | * to make that possible, each exe and all dependencies (including ghc) are built against [`musl`](https://www.musl-libc.org/) instead of glibc 9 | 10 | static-haskell-nix can [successfully build > 90% of Stackage executables](https://github.com/nh2/static-haskell-nix/issues/4#issuecomment-406838083), so chances are high it can build yours. 11 | 12 | ## History 13 | 14 | `glibc` encourages dynamic linking to the extent that correct functionality under static linking is somewhere between difficult and bug-ridden. 15 | For this reason, static linking, despite its many advantages (details [here](https://github.com/NixOS/nixpkgs/issues/43795)) has become less and less common. 16 | 17 | Due to GHC's dependency on a libc, and many libraries depending on C libraries for which Linux distributions often do not include static library archive files, this situation has resulted in fully static Haskell programs being extremely hard to produce for the common Haskeller, even though the language is generally well-suited for static linking. 18 | 19 | This project solves this. 20 | 21 | It was inspired by a [blog post by Vaibhav Sagar](https://vaibhavsagar.com/blog/2018/01/03/static-haskell-nix/), 22 | and a [comment by Will Dietz](https://github.com/NixOS/nixpkgs/pull/37598#issuecomment-375117019) about musl. 23 | 24 | Work on this so far was sponsored largely by my free time, [FP Complete](https://haskell.fpcomplete.com/) and their clients, and the contributors mentioned [here](https://github.com/NixOS/nixpkgs/issues/43795#issue-342546855). 25 | 26 | By now we have a nixpkgs issue on [Fully static Haskell executables](https://github.com/NixOS/nixpkgs/issues/43795) (progress on which is currently this repo, with plans to later merge it into nixpkgs), and [a merged nixpkgs overlay for static nixpkgs in general](https://github.com/NixOS/nixpkgs/pull/48803). 27 | 28 | There's also nixpkgs's `pkgsStatic` package set, which can also build many Haskell packages statically with `musl`. Differences are: 29 | 30 | * Type of compilation: 31 | * `pkgsStatic` uses cross-compilation infrastructure, which is inherently more complex, and more difficult to get into. 32 | * `static-haskell-nix` just replaces the libc, and compiles normally. This allows to build packages that cannot (yet) be cross-compiled. 33 | * `.a` + `.so` files: 34 | * `pkgsStatic` does _exclusively_ static builds, it generates only `.a` files and no `.so` files. 35 | * `static-haskell-nix` generates both `.a` and `.so` files, which allows more intermediate software to run (e.g. some build systems using Python libraries doing `dlopen()` on some `.so` files). 36 | * In the past, this made a big difference for TemplateHaskell, which worked well only when `.so` files are present. This seems to have improved. `static-haskell-nix` now has an off-by-default flag `useArchiveFilesForTemplateHaskell` that users are encouraged to test. 37 | * Hacky fixes: 38 | * `static-haskell-nix` contains a large amount of per-package fixes for static builds for which we haven't found a way to integrate them cleanly into nixpkgs yet. 39 | * Pinning: 40 | * `static-haskell-nix` does not impede nixpkgs progress, as it is maintained out of the nixkpgs. 41 | 42 | In general, any contribution to `static-haskell-nix` or `pkgsStatic` benefits the respective other one. 43 | 44 | A goal is to shrink `static-haskell-nix` over time, moving those parts into nixpkgs that do not slow down nixpkgs's fast pace. 45 | 46 | ## Funding 47 | 48 | You can support this project financially [on OpenCollective](https://opencollective.com/static-haskell-nix). Goals: 49 | 50 | * [x] **Dedicated build server** - [Goal reached!](https://opencollective.com/static-haskell-nix/updates/build-server-funding-goal-reached) Thanks to our awesome [contributors](https://opencollective.com/static-haskell-nix#contributors)! 51 | 52 | The first and main goal is to get to ~28 EUR/month to buy a cheap Hetzner dedicated build server for fast CI and pushing to Cachix. It will also allow anybody to download almost any executable on Stackage pre-built as a static binary, so that people can try out Haskell programs easily without having to install lots of dependencies. 53 | 54 | Because the server is so cheap, already 1 or 2 EUR/month will bring us to that goal quickly. 55 | 56 | [Hercules CI Logo](https://hercules-ci.com) 57 | The **storage infrastructure** ([Cachix](https://cachix.org)) for downloading pre-built packages is **sponsored by the [awesome guys](https://hercules-ci.com/#about) from Hercules CI**. 58 | They are building a nix-based CI service you can safely run on your own infrastructure. _static-haskell-nix_ also uses it. 59 |
If your company or project needs that, check [**Hercules CI**](https://hercules-ci.com) out! 60 | 61 | ## Testing 62 | 63 | We have multiple CIs: 64 | 65 | * [HerculesCI](https://hercules-ci.com/github/nh2/static-haskell-nix/): Builds with pinned nixpkgs. 66 | Publicly visible, but requires free sign-in. Click the most recent job to which 100s of binaries we build. 67 | * [BuildKite](https://buildkite.com/nh2/static-haskell-nix/): 68 | * Builds with pinned nixpkgs (submodule): Should always be green. 69 | * Builds with latest nixpkgs `unstable`, daily: Shows up as **Scheduled build**. 70 | May break when nixpkgs upstream changes. 71 | 72 | ## Building a minimal example (don't use this in practice) 73 | 74 | `default.nix` builds an example executable (originally from https://github.com/vaibhavsagar/experiments). Run: 75 | 76 | ``` 77 | NIX_PATH=nixpkgs=nixpkgs nix-build --no-link 78 | ``` 79 | 80 | This prints a path that contains the fully linked static executable in the `bin` subdirectory. 81 | 82 | This example is so that you get the general idea. 83 | In practice, you probably want to use one of the approaches from the "Building arbitrary packages" or "Building stack projects" sections below. 84 | 85 | ## Binary caches for faster building (optional) 86 | 87 | Install [cachix](https://static-haskell-nix.cachix.org/) and run `cachix use static-haskell-nix` before your `nix-build`. 88 | 89 | If you get a warning during `cachix use`, read [this](https://github.com/cachix/cachix/issues/56#issuecomment-423820198). 90 | 91 | If you don't want to install `cachix` for some reason or `cachix use` doesn't work, you should also be able to manually set up your `nix.conf` (e.g. in `$HOME/.config/nix/nix.conf`; you may have to create the file) to have contents like this: 92 | 93 | ``` 94 | extra-substituters = http://static-haskell-nix-ci.nh2.me:5000 https://cache.nixos.org https://static-haskell-nix.cachix.org 95 | extra-trusted-public-keys = static-haskell-nix-ci-cache-key:Z7ZpqYFHVs467ctsqZADpjQ/XkSHx6pm5XBZ4KZW3/w= static-haskell-nix.cachix.org-1:Q17HawmAwaM1/BfIxaEDKAxwTOyRVhPG5Ji9K3+FvUU= 96 | ``` 97 | 98 | or append to command lines: 99 | 100 | ```sh 101 | --option extra-substituters 'http://static-haskell-nix-ci.nh2.me:5000' --option extra-trusted-public-keys 'static-haskell-nix-ci-cache-key:Z7ZpqYFHVs467ctsqZADpjQ/XkSHx6pm5XBZ4KZW3/w=' 102 | ``` 103 | 104 | Note that you may not get cached results if you use a different `nix` version than I used to produce the cache (I used `2.0.4` as of writing, which you can get from [here](https://nixos.org/releases/nix/nix-2.0.4/install)). 105 | 106 | ## Building arbitrary packages 107 | 108 | The [`survey`](./survey) directory maintains a select set of Haskell executables that are known and not known to work with this approach; contributions are welcome to grow the set of working executables. 109 | Run for example: 110 | 111 | ``` 112 | NIX_PATH=nixpkgs=nixpkgs nix-build --no-link survey/default.nix -A working 113 | ``` 114 | 115 | There are multiple package sets available in the survey (select via `-A`): 116 | 117 | * `working` -- build all exes known to be working 118 | * `notWorking` -- build all exes known to be not working (help welcome to make them work) 119 | * `haskellPackages.somePackage` -- build a specific package from our overridden package set 120 | 121 | If you are a nix user, you can easily `import` this functionality and add an override to add your own packages. 122 | 123 | ## Building `stack` projects 124 | 125 | The [`static-stack2nix-builder-example`](./static-stack2nix-builder-example) directory shows how to build any `stack`-based project statically. 126 | 127 | Until Stack 2.3, the official static build of `stack` itself was built using this method (Stack >= 2.3 static builds are built in an Alpine Docker image after GHC on Alpine started working again, see [here](https://github.com/commercialhaskell/stack/pull/5267)). 128 | The [`static-stack`](./static-stack) directory shows how Stack itself can be built statically with static-haskell-nix. 129 | `stack` is a big package with many dependencies, demonstrating that it works also for large projects. 130 | 131 | ## Related important open issues 132 | 133 | You can contribute to these to help static Haskell executables: 134 | 135 | * https://github.com/haskell/cabal/issues/8455 136 | 137 | ## FAQ 138 | 139 | * I get `cannot find section .dynamic`. Is this an error? 140 | * No, this is an informational message printed by `patchelf`. If your final looks like 141 | ``` 142 | ... 143 | cannot find section .dynamic 144 | /nix/store/dax3wjbjfrcwj6r3mafxj5fx6wcg5zbp-stack-2.3.0.1 145 | ``` 146 | then `/nix/store/dax3wjbjfrcwj6r3mafxj5fx6wcg5zbp-stack-2.3.0.1` is your final output _store path_ whose `/bin` directory contains your static executable. 147 | * I get `stack2nix: user error (No such package mypackage-1.2.3 in the cabal database. Did you run cabal update?)`. 148 | * You most likely have to bump the date like `hackageSnapshot = "2019-05-08T00:00:00Z";` to a newer date (past the time that package-version was added to Hackage). 149 | * I get a linker error. 150 | What's a good way to investigate what the linker invocation is? 151 | * Pass `-v` to Cabal, and to GHC itself: 152 | ```sh 153 | nix-build --expr '(import ./survey/default.nix {}).haskellPackages.YOURPACKAGE.overrideAttrs (old: { configureFlags = (old.configureFlags or []) ++ ["-v" "--ghc-options=-v"]; })' 154 | ``` 155 | Look for `*** Linker:` in the GHC output. 156 | * Can I build Stack projects with resolvers that are too old to be supported by Stack >= 2? 157 | * No. For that you need need to use an old `static-haskell-nix` version: The one before [this PR](https://github.com/nh2/static-haskell-nix/pull/98) was merged. 158 | * I get some other error. Can I just file an issue and have you help me with it? 159 | * Yes. If possible (especially if your project is open source), please push some code so that your issue can be easily reproduced. 160 | 161 | 162 | ## Open questions 163 | 164 | * Nixpkgs issue [Provide middle-ground overlay between pkgsMusl and pkgsStatic](https://github.com/NixOS/nixpkgs/issues/61575): 165 | 166 | Should nixpkgs provide a `makeStaticAndSharedLibraries` adapter to provide a package set? 167 | That might be better (but more difficult) than what we do now, with `dontDisableStaticOverlay`, because: 168 | * `dontDisableStatic` is to prevent `--disable-static` to autoconf, which is really specific to C + autoconf. 169 | A package set should do more than that, also for Meson, CMake, etc. 170 | `nh2` started implementing this idea in nixpkgs branch `static-haskell-nix-nixos-24.05-makeStaticAndSharedLibraries`. 171 | 172 | * Can we avoid building bootstrap tools? 173 | * Our current overlays also build `xgcc`, `gcc`, `binutils`, and so on. 174 | * This is because we override all packages to have e.g. `.a` files, and some of those are also dependencies of e.g. `gcc`. 175 | * `pkgsStatic` avoids that by being a `cross` toolchain. 176 | * But might this cause additional issues? 177 | Because `cross` may have additional complexities when building the actual packages we're interested in, vs just switching the libc ("native" compilation)? 178 | Unclear. 179 | * For now, we accept those additional builds. 180 | 181 | * How should we handle `pkg-config` regarding static dependencies? 182 | 183 | E.g. `libtiff` depends on `lerc` and `libtiff-4.pc` correctly declares 184 | 185 | ``` 186 | Libs.private: -llzma -lLerc -ljpeg -ldeflate -lz -lm 187 | Requires.private: liblzma libjpeg libdeflate zlib 188 | ``` 189 | 190 | But the `.pc` file does not include the path on which `libLerc.a` can be found, nor does anything in nixpkgs set `PKG_CONFIG_PATH` such that `Lerc.pc` is on it. 191 | Thus, linking a static binary that uses `libtiff-4.pc` fails with 192 | 193 | ``` 194 | cannot find -lLerc: No such file or directory 195 | ``` 196 | 197 | * Should we use `propagatedBuildInputs` for this? 198 | * Yes! We can use `stdenvAdapters.propagateBuildInputs`. 199 | * Current problem: Using that in a native compilation (instead of cross as `pkgsMusl` does) causes: 200 | ``` 201 | error: build of '/nix/store/...-stdenv-linux.drv' failed: output '/nix/store/...-stdenv-linux' is not allowed to refer to the following paths: 202 | /nix/store/...-binutils-patchelfed-ld-wrapper-2.41 203 | /nix/store/...-pcre2-10.43-dev 204 | /nix/store/...-gmp-with-cxx-6.3.0-dev 205 | /nix/store/...-musl-iconv-1.2.3 206 | /nix/store/...-binutils-2.41 207 | /nix/store/...-bootstrap-tools 208 | ``` 209 | * John Ericson explained that the bootstrap rebuild avoidance (mentioned in a point above) also solves this issue for `pkgsStatic`. 210 | So we probably need to do something similar. 211 | * After fixing that, we still need to fix `libtiff` to include `lerc` in `Requires.private`. 212 | * Done in https://github.com/NixOS/nixpkgs/pull/320105 213 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /ci.nix: -------------------------------------------------------------------------------- 1 | # HerculesCI config 2 | let 3 | survey = import ./survey {}; 4 | in survey.working // survey.workingStackageExecutables 5 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | # Note: This is just a minimal example. For proper usage, see the README. 2 | 3 | { nixpkgs ? (import ./nixpkgs.nix).pkgsMusl, compiler ? "ghc8107", strip ? true }: 4 | 5 | 6 | let 7 | 8 | pkgs = nixpkgs.pkgsMusl; 9 | 10 | example-scotty-app = { mkDerivation, base, scotty, stdenv }: 11 | mkDerivation { 12 | pname = "example-scotty-app"; 13 | version = "0.1.0.0"; 14 | src = pkgs.lib.sourceByRegex ./. [ 15 | ".*\\.cabal$" 16 | "^Setup.hs$" 17 | "^Main.hs$" 18 | ]; 19 | isLibrary = false; 20 | isExecutable = true; 21 | enableSharedExecutables = false; 22 | enableSharedLibraries = false; 23 | executableHaskellDepends = [ base scotty ]; 24 | license = pkgs.lib.licenses.bsd3; 25 | configureFlags = [ 26 | "--ghc-option=-optl=-static" 27 | "--extra-lib-dirs=${pkgs.gmp6.override { withStatic = true; }}/lib" 28 | "--extra-lib-dirs=${pkgs.zlib.static}/lib" 29 | "--extra-lib-dirs=${pkgs.libffi.overrideAttrs (old: { dontDisableStatic = true; })}/lib" 30 | ] ++ pkgs.lib.optionals (!strip) [ 31 | "--disable-executable-stripping" 32 | ] ; 33 | }; 34 | 35 | normalHaskellPackages = pkgs.haskell.packages.${compiler}; 36 | 37 | haskellPackages = with pkgs.haskell.lib; normalHaskellPackages.override { 38 | overrides = self: super: { 39 | # Dependencies we need to patch 40 | }; 41 | }; 42 | 43 | drv = haskellPackages.callPackage example-scotty-app {}; 44 | 45 | in 46 | if pkgs.lib.inNixShell then drv.env else drv 47 | -------------------------------------------------------------------------------- /example-scotty-app.cabal: -------------------------------------------------------------------------------- 1 | name: example-scotty-app 2 | version: 0.1.0.0 3 | license: BSD3 4 | build-type: Simple 5 | cabal-version: >=1.10 6 | 7 | executable example-scotty-app 8 | main-is: Main.hs 9 | build-depends: base >=4.9 && <5 10 | , scotty 11 | default-language: Haskell2010 12 | ld-options: -static 13 | -------------------------------------------------------------------------------- /nixpkgs.nix: -------------------------------------------------------------------------------- 1 | # If this env var is set, use latest nixpkgs unstable. 2 | # We use that for scheduled builds tracking nixpkgs unstable on CI. 3 | # Of course that is NOT reproducible. 4 | if builtins.getEnv "STATIC_HASKELL_NIX_CI_NIXPKGS_UNSTABLE_BUILD" == "1" 5 | then 6 | let 7 | # You can set e.g. to build with `master`: 8 | # STATIC_HASKELL_NIX_CI_NIXPKGS_UNSTABLE_BUILD=1 9 | # NIXPKGS_URL=https://github.com/NixOS/nixpkgs/archive/master.tar.gz 10 | NIXPKGS_URL_var = builtins.getEnv "NIXPKGS_URL"; 11 | nixpkgsUrl = 12 | if NIXPKGS_URL_var != null && NIXPKGS_URL_var != "" 13 | then NIXPKGS_URL_var 14 | else "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz"; 15 | nixpkgs = import (fetchTarball nixpkgsUrl) {}; 16 | msg = "Using version ${nixpkgs.lib.version} of nixpkgs-unstable channel."; 17 | in builtins.trace msg nixpkgs 18 | else 19 | # If a `./nixpkgs` submodule exists, use that. 20 | # Note that this will take precedence over setting NIX_PATH! 21 | # We prefer this such that `static-stack2nix-builder` and specifically 22 | # `static-stack2nix-builder-example` can just import `nixpkgs.nix` 23 | # in CI and when called during development to get the right version of 24 | # nixpkgs. 25 | if builtins.pathExists ./nixpkgs/pkgs 26 | then import ./nixpkgs {} 27 | # Pinned nixpkgs version; should be kept up-to-date with our submodule. 28 | # This is nixos-23.11 as of 2024-01-01, with minimal patches currently having open nixpkgs PR (see commits for PR links). 29 | else import (fetchTarball https://github.com/nh2/nixpkgs/archive/ede5282c487a1fd2de64303ba59adad6726f1225.tar.gz) {} 30 | -------------------------------------------------------------------------------- /setup-git-hooks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | # Must be run from top of the git directory 5 | 6 | mkdir -p .git/hooks 7 | rm -f .git/hooks/post-checkout 8 | ln -s ../../submodules-update .git/hooks/post-checkout 9 | ln -s ../../submodules-update .git/hooks/post-rewrite 10 | -------------------------------------------------------------------------------- /static-stack/README.md: -------------------------------------------------------------------------------- 1 | # Fully statically linked `stack` 2 | 3 | This builds a fully statically linked `stack` executable that should work on any 64-bit Linux distribution. 4 | 5 | It uses nix to build everything, including `ghc`, against the `musl` libc. 6 | 7 | ## Building 8 | 9 | ``` 10 | $(nix-build --no-link -A fullBuildScript --argstr stackDir /absolute/path/to/stack/source) 11 | ``` 12 | 13 | We use the `$(nix-build ...)` script approach in order to pin the version of `nix` itself for reproducibility, and because the call to `stack2nix` needs Internet access and thus has to run outside of the nix build sandbox. 14 | 15 | If you get an error such as: 16 | 17 | > stack2nix: user error (No such package foo-1.2.3.4 in the cabal database. Did you run cabal update?) 18 | 19 | then update the `hackageSnapshot` date in `default.nix` to a date that includes the package and version. 20 | 21 | ## Binary caches for faster building (optional) 22 | 23 | You can use the caches described in the [top-level README](../README.md#binary-caches-for-faster-building-optional) for faster building. 24 | 25 | ## `stack` binaries 26 | 27 | Static `stack` binaries I built this way, for download: 28 | 29 | * The [official static stack v2.1.3 release](https://github.com/commercialhaskell/stack/releases/tag/v2.1.3) is built using this 30 | * The [official static stack v2.1.1 release](https://github.com/commercialhaskell/stack/releases/tag/v2.1.1) is built using this 31 | * The [official static stack v1.9.3 release](https://github.com/commercialhaskell/stack/releases/tag/v1.9.3) is built using this 32 | * [stack v1.7.1 for 64-bit Linux](https://github.com/nh2/stack/releases/tag/v1.6.5) 33 | * [stack v1.6.5 for 64-bit Linux](https://github.com/nh2/stack/releases/tag/v1.6.5) 34 | -------------------------------------------------------------------------------- /static-stack/build-static-stack.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | mkdir -p static-stack-test-dir 6 | curl -L https://github.com/commercialhaskell/stack/archive/v2.7.1.tar.gz | tar -xz -C static-stack-test-dir 7 | 8 | $(nix-build --no-link -A fullBuildScript --argstr stackDir $PWD/static-stack-test-dir/stack-*) 9 | -------------------------------------------------------------------------------- /static-stack/default.nix: -------------------------------------------------------------------------------- 1 | # Builds a static `stack` executable from a stack source dir. 2 | # 3 | # Usage: 4 | # 5 | # $(nix-build --no-link -A fullBuildScript --argstr stackDir /absolute/path/to/stack/source) 6 | { 7 | stackDir ? "/absolute/path/to/stack/source", 8 | stack2nix-output-path ? "custom-stack2nix-output.nix", 9 | }: 10 | let 11 | cabalPackageName = "stack"; 12 | compiler = "ghc8104"; # matching stack-lts-12.yaml 13 | 14 | pkgs = import ../nixpkgs {}; 15 | 16 | stack2nix-script = import ../static-stack2nix-builder/stack2nix-script.nix { 17 | inherit pkgs; 18 | inherit compiler; 19 | stack-project-dir = stackDir; # where stack.yaml is 20 | hackageSnapshot = "2021-07-12T00:00:00Z"; # pins e.g. extra-deps without hashes or revisions 21 | }; 22 | 23 | static-stack2nix-builder = import ../static-stack2nix-builder/default.nix { 24 | normalPkgs = pkgs; 25 | inherit cabalPackageName compiler stack2nix-output-path; 26 | # disableOptimization = true; # for compile speed 27 | }; 28 | 29 | static_package = with pkgs.haskell.lib; 30 | overrideCabal 31 | (appendConfigureFlags 32 | static-stack2nix-builder.static_package 33 | [ 34 | # Official release flags: 35 | "-fsupported-build" 36 | "-fhide-dependency-versions" 37 | "-f-disable-git-info" # stack2nix turns that on, we turn it off again 38 | ] 39 | ) 40 | (old: { 41 | # Enabling git info needs these extra deps. 42 | # TODO Make `stack2nix` accept per-package Cabal flags, 43 | # so that `cabal2nix` would automatically add 44 | # the right dependencies for us. 45 | executableHaskellDepends = (old.executableHaskellDepends or []) ++ 46 | (with static-stack2nix-builder.haskell-static-nix_output.haskellPackages; [ 47 | githash 48 | optparse-simple 49 | ]); 50 | # Put `git` on PATH, because `githash` calls it. 51 | preConfigure = '' 52 | export PATH=${pkgs.git}/bin:$PATH 53 | git --version 54 | ''; 55 | }); 56 | 57 | # Full invocation, including pinning `nix` version itself. 58 | fullBuildScript = pkgs.writeShellScript "stack2nix-and-build-script.sh" '' 59 | set -eu -o pipefail 60 | STACK2NIX_OUTPUT_PATH=$(${stack2nix-script}) 61 | ${pkgs.nix}/bin/nix-build --no-link -A static_package --argstr stack2nix-output-path "$STACK2NIX_OUTPUT_PATH" "$@" 62 | ''; 63 | 64 | in 65 | { 66 | inherit static_package; 67 | inherit fullBuildScript; 68 | # For debugging: 69 | inherit stack2nix-script; 70 | inherit static-stack2nix-builder; 71 | } 72 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/README.md: -------------------------------------------------------------------------------- 1 | # example-project 2 | 3 | Example stack-based project that shows how to use `static-stack2nix-builder`. 4 | 5 | ## Usage in your project 6 | 7 | Copy the [`default.nix`](./default.nix) into your project, and adjust it as needed. 8 | 9 | It has instructions for running inside. 10 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Lib 4 | 5 | main :: IO () 6 | main = someFunc 7 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/default.nix: -------------------------------------------------------------------------------- 1 | # Run using: 2 | # 3 | # $(nix-build --no-link -A fullBuildScript) 4 | { 5 | stack2nix-output-path ? "custom-stack2nix-output.nix", 6 | }: 7 | let 8 | cabalPackageName = "example-project"; 9 | compiler = "ghc8104"; # matching stack.yaml 10 | 11 | # Pin static-haskell-nix version. 12 | static-haskell-nix = 13 | if builtins.pathExists ../.in-static-haskell-nix 14 | then toString ../. # for the case that we're in static-haskell-nix itself, so that CI always builds the latest version. 15 | # Update this hash to use a different `static-haskell-nix` version: 16 | else fetchTarball https://github.com/nh2/static-haskell-nix/archive/57147ba740363712f589d24dfa005c8c7f6d1056.tar.gz; 17 | 18 | # Pin nixpkgs version 19 | # By default to the one `static-haskell-nix` provides, but you may also give 20 | # your own as long as it has the necessary patches, using e.g. 21 | # pkgs = import (fetchTarball https://github.com/nh2/nixpkgs/archive/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa123.tar.gz) {}; 22 | pkgs = import "${static-haskell-nix}/nixpkgs.nix"; 23 | 24 | stack2nix-script = import "${static-haskell-nix}/static-stack2nix-builder/stack2nix-script.nix" { 25 | inherit pkgs; 26 | inherit compiler; 27 | stack-project-dir = toString ./.; # where stack.yaml is 28 | hackageSnapshot = "2021-07-11T00:00:00Z"; # pins e.g. extra-deps without hashes or revisions 29 | }; 30 | 31 | static-stack2nix-builder = import "${static-haskell-nix}/static-stack2nix-builder/default.nix" { 32 | normalPkgs = pkgs; 33 | inherit cabalPackageName compiler stack2nix-output-path; 34 | # disableOptimization = true; # for compile speed 35 | }; 36 | 37 | # Full invocation, including pinning `nix` version itself. 38 | fullBuildScript = pkgs.writeShellScript "stack2nix-and-build-script.sh" '' 39 | set -eu -o pipefail 40 | STACK2NIX_OUTPUT_PATH=$(${stack2nix-script}) 41 | export NIX_PATH=nixpkgs=${pkgs.path} 42 | ${pkgs.nix}/bin/nix-build --no-link -A static_package --argstr stack2nix-output-path "$STACK2NIX_OUTPUT_PATH" "$@" 43 | ''; 44 | 45 | in 46 | { 47 | static_package = static-stack2nix-builder.static_package; 48 | inherit fullBuildScript; 49 | # For debugging: 50 | inherit stack2nix-script; 51 | inherit static-stack2nix-builder; 52 | } 53 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/example-project.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | 3 | -- This file has been generated from package.yaml by hpack version 0.31.2. 4 | -- 5 | -- see: https://github.com/sol/hpack 6 | -- 7 | -- hash: f29499e26d59c63f4043d8f0d8ba9bf25eb9fb9877cde46564e5e1579fe55b11 8 | 9 | name: example-project 10 | version: 0.1.0.0 11 | author: Niklas Hambüchen 12 | maintainer: mail@nh2.me 13 | license: BSD3 14 | build-type: Simple 15 | 16 | library 17 | exposed-modules: 18 | Lib 19 | other-modules: 20 | Paths_example_project 21 | hs-source-dirs: 22 | src 23 | build-depends: 24 | base >=4.7 && <5 25 | default-language: Haskell2010 26 | 27 | executable example-project-exe 28 | main-is: Main.hs 29 | other-modules: 30 | Paths_example_project 31 | hs-source-dirs: 32 | app 33 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 34 | build-depends: 35 | base >=4.7 && <5 36 | , example-project 37 | default-language: Haskell2010 38 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/package.yaml: -------------------------------------------------------------------------------- 1 | name: example-project 2 | version: 0.1.0.0 3 | license: BSD3 4 | author: "Niklas Hambüchen" 5 | maintainer: "mail@nh2.me" 6 | 7 | dependencies: 8 | - base >= 4.7 && < 5 9 | 10 | library: 11 | source-dirs: src 12 | 13 | executables: 14 | example-project-exe: 15 | source-dirs: app 16 | main: Main.hs 17 | ghc-options: 18 | - -threaded 19 | - -rtsopts 20 | - -with-rtsopts=-N 21 | dependencies: 22 | - example-project 23 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/src/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib 2 | ( someFunc 3 | ) where 4 | 5 | someFunc :: IO () 6 | someFunc = putStrLn "someFunc" 7 | -------------------------------------------------------------------------------- /static-stack2nix-builder-example/stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-18.2 2 | packages: 3 | - . 4 | -------------------------------------------------------------------------------- /static-stack2nix-builder/default.nix: -------------------------------------------------------------------------------- 1 | # Helper to build static executables from a Haskell stack project's source dir. 2 | # Use this after having generated a stack2nix output, e.g. with 3 | # `stack2nix-script.nix`. 4 | { 5 | # The name of the cabal package to build, e.g. "pandoc". 6 | cabalPackageName ? "myproject", 7 | 8 | # Compiler name in nixpkgs, e.g. "ghc8104". 9 | # Must match the one in the `resolver` in `stack.yaml`. 10 | # If you get this wrong, you'll likely get an error like 11 | # : cannot satisfy -package-id Cabal-2.4.1.0-ALhzvdqe44A7vLWPOxSupv 12 | # TODO: Make `stack2nix` tell us that. 13 | compiler ? "ghc8104", 14 | 15 | # Path to `stack2nix` output that shall be used as Haskell packages. 16 | # You should usually give this the store path that `stack2nix-script` outputs. 17 | stack2nix-output-path, 18 | 19 | # Pin nixpkgs version. 20 | normalPkgs ? import (fetchTarball https://github.com/nh2/nixpkgs/archive/8d536f36256d30d8fa47b24caafb1af6405889f3.tar.gz) {}, 21 | 22 | # Use `integer-simple` instead of `integer-gmp` to avoid linking in 23 | # this LGPL dependency statically. 24 | integer-simple ? false, 25 | 26 | # Enable for faster building, but not proper releases. 27 | disableOptimization ? false, 28 | }: 29 | let 30 | 31 | static-haskell-nix_pkgsMusl = (import ../survey/default.nix { 32 | inherit normalPkgs; 33 | inherit compiler; 34 | inherit disableOptimization; 35 | }).pkgs; 36 | 37 | stack2nix_output = import stack2nix-output-path { pkgs = static-haskell-nix_pkgsMusl; }; 38 | 39 | pkgs_with_stack2nix_packages_inside = static-haskell-nix_pkgsMusl.extend (final: previous: { 40 | haskell = final.lib.recursiveUpdate previous.haskell { 41 | packages."${compiler}" = stack2nix_output; 42 | }; 43 | }); 44 | 45 | haskell-static-nix_output = (import ../survey/default.nix { 46 | normalPkgs = pkgs_with_stack2nix_packages_inside; 47 | inherit compiler; 48 | inherit disableOptimization; 49 | inherit integer-simple; 50 | }); 51 | 52 | static_package = haskell-static-nix_output.haskellPackages."${cabalPackageName}"; 53 | 54 | # Provide this to make builds extra reproducible by also pinning the version 55 | # of `nix` itself, as changing nix versions can change the build env. 56 | pinnedNix = normalPkgs.nix; 57 | 58 | in { 59 | inherit static-haskell-nix_pkgsMusl; 60 | inherit stack2nix_output; 61 | inherit pkgs_with_stack2nix_packages_inside; 62 | inherit haskell-static-nix_output; 63 | inherit static_package; 64 | inherit pinnedNix; 65 | } 66 | -------------------------------------------------------------------------------- /static-stack2nix-builder/stack2nix-script.nix: -------------------------------------------------------------------------------- 1 | # Creates a script that runs `stack2nix` on a given stack project's source dir. 2 | # Arguments given to the script are given to `stack2nix`. 3 | # Running the script adds the generated output file to the nix store 4 | # and prints the store store path to stdout. 5 | # 6 | { 7 | # nixpkgs to use. 8 | pkgs, 9 | 10 | # ghc to use; only because without a GHC on path, stack complains: 11 | # stack2nix: No compiler found, expected minor version match with ghc-8.10.4 (x86_64) (based on resolver setting ... 12 | # This happens even when using the Stack API (as stack2nix does), 13 | # and stack2nix doen't currently accept or set the `--system-ghc` 14 | # flag to skip the check (maybe it should to eschew this option; 15 | # I suspect our operation here never uses GHC). 16 | # TODO: This shouldn't be necessary since `stack2nix` commit 17 | # Set `--system-ghc` via stack API. 18 | # But somehow stack2nix still complains about it; 19 | # perhaps we didn't use the Stack API correctly. 20 | compiler, 21 | 22 | # Path to directory containing `stack.yaml`. 23 | stack-project-dir, 24 | 25 | # Hackage snapshot time to pass to `stack2nix`. 26 | # This determines the versions of package revisions in stack `extra-deps` 27 | # that are not pinned to a revision. 28 | # Example: "2019-05-08T00:00:00Z" 29 | hackageSnapshot, 30 | 31 | # stack.yaml file to use. 32 | # Must be in the `stack-project-dir` (usually next to wherever the normal 33 | # stack.yaml is) because Stack will search for its `packages` relative 34 | # to this file. 35 | # Useful when you want to give a customised stack.yaml, 36 | # e.g. when adding extra cabal flags to packages for static builds, 37 | # such as the `integer-simple` flag to the `text` library. 38 | stack-yaml ? "stack.yaml", 39 | }: 40 | # `stack2nix` requires `cabal` on $PATH. 41 | # We put our nixpkgs's version of `nix` on $PATH for reproducibility. 42 | # Everything but `nix-store --add` must print to stderr so that the 43 | # script prints only the final store path to stdout. 44 | # The output is generated to a `mktemp --directory` so that parallel 45 | # invocations don't influence each other. 46 | # Note in this script we should qualify all executables from nix packages 47 | # (or put them on PATH accordingly) 48 | # as it's run in the user's shell (not in a normal nix build environment, 49 | # since `stack2nix` needs internet access), so we can't make any assumptions 50 | # about shell builtins or what's on PATH. For example, if `mktemp` is from 51 | # `busybox` instead of `coreutils`, it may not support the `--directory` 52 | # option. 53 | # And for example the `nixos/nix` Docker container is minimal and supplies 54 | # many executables from `busybox`, such as `mktemp` and `wget`. 55 | let 56 | # Shell utils called by stack2nix or the script itself: 57 | add_to_PATH = [ 58 | "${pkgs.coreutils}/bin" # `mktemp` et al 59 | "${pkgs.cabal-install}/bin" # `cabal` 60 | "${pkgs.nix}/bin" # various `nix-*` commands 61 | "${pkgs.wget}/bin" # `wget` 62 | "${pkgs.haskell.compiler.${compiler}}/bin" # `ghc` version matching target stack.yaml 63 | ]; 64 | 65 | fixed_stack2nix = 66 | pkgs.haskellPackages.callCabal2nix "stack2nix" (pkgs.fetchFromGitHub { 67 | owner = "nh2"; 68 | repo = "stack2nix"; 69 | rev = "c20097d4edf82256484a733544579d4b5e0f2808"; 70 | sha256 = "1lpwc20q62z9a9fpksd9q10x1jz8l29psx4dqsff759srj4chy9p"; 71 | }) {}; 72 | in 73 | pkgs.writeShellScript "stack2nix-build-script.sh" '' 74 | set -eu -o pipefail 75 | export NIX_PATH=nixpkgs=${pkgs.path} 76 | export PATH=${pkgs.lib.concatStringsSep ":" add_to_PATH}:$PATH 77 | OUT_DIR=$(mktemp --directory -t stack2nix-output-dir.XXXXXXXXXX) 78 | set -x 79 | ${fixed_stack2nix}/bin/stack2nix "${stack-project-dir}" --stack-yaml "${stack-yaml}" --hackage-snapshot "${hackageSnapshot}" -o "$OUT_DIR/stack2nix-output.nix" "$@" 1>&2 80 | nix-store --add "$OUT_DIR/stack2nix-output.nix" 81 | '' 82 | -------------------------------------------------------------------------------- /submodules-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | [ "$SKIP_POST_CHECKOUT_HOOK" = 1 ] && exit 0 3 | git submodule sync && git submodule update --init --recursive 4 | -------------------------------------------------------------------------------- /survey/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | tracing ? false, # Enable this to see debug traces 3 | 4 | normalPkgs ? import ../nixpkgs.nix, 5 | 6 | overlays ? [], 7 | 8 | approach ? # "pkgsMusl" or "pkgsStatic" 9 | # TODO `pkgsStatic` support is currently not maintained and will likely be removed, 10 | # because `pkgsMusl` is a better base for what we need. 11 | # See https://github.com/NixOS/nixpkgs/issues/61575 12 | # TODO Find out why `pkgsStatic` creates ~3x larger binaries. 13 | "pkgsMusl", # does not exercise cross compilation 14 | # "pkgsStatic", # exercises cross compilation 15 | 16 | # Note that we must NOT use something like `import normalPkgs.path {}`. 17 | # It is bad because it removes previous overlays. 18 | pkgs ? (normalPkgs.appendOverlays [ 19 | ])."${approach}", 20 | 21 | # When changing this, also change the default version of Cabal declared below 22 | compiler ? "ghc965", 23 | 24 | # Tries to use `.a` files when evaluating TH, instead of `.so` files. 25 | useArchiveFilesForTemplateHaskell ? false, 26 | 27 | # See https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/libraries/version-history 28 | defaultCabalPackageVersionComingWithGhc ? 29 | ({ 30 | ghc8107 = "Cabal_3_2_1_0"; 31 | ghc902 = "Cabal_3_4_1_0"; 32 | ghc928 = "Cabal_3_6_3_0"; 33 | ghc948 = "Cabal_3_8_1_0"; 34 | ghc963 = "Cabal_3_10_1_0"; 35 | ghc965 = "Cabal_3_10_3_0"; 36 | }."${compiler}"), 37 | 38 | # Use `integer-simple` instead of `integer-gmp` to avoid linking in 39 | # this LGPL dependency statically. 40 | integer-simple ? false, 41 | 42 | # Enable for fast iteration. 43 | # Note that this doesn't always work. I've already found tons of bugs 44 | # in packages when `-O0` is used, like 45 | # * https://github.com/bos/double-conversion/issues/26 46 | # * https://github.com/bos/blaze-textual/issues/11 47 | # and also a few ultra-weird errors like `hpack` failing to link with 48 | # errors like this when building `hpack` from a `stack2nix` project 49 | # built statically: 50 | # /nix/store/...-binutils-2.31.1/bin/ld: /nix/store/...-Cabal-2.4.1.0/lib/ghc-8.6.4/x86_64-linux-ghc-8.6.4/Cabal-2.4.1.0-.../libHSCabal-2.4.1.0-CZ6S6W3ko5J53WiB3G8d5G.a(Class.o):(.text.s2t5E_info+0x45): undefined reference to `parseczm3zi1zi13zi0zm2FiyouGhSt6Ln2s2okK4LQ_TextziParsecziPrim_zlz3fUzg2_info' 51 | # after which resuming the build with `./Setup build --ghc-option=-O` 52 | # (with `-O`!) I saw the likely cause: 53 | # /nix/store/waszsfh43jli6p8d0my8cb5ahrcksxif-Cabal-2.4.1.0/lib/ghc-8.6.4/x86_64-linux-ghc-8.6.4/Cabal-2.4.1.0-CZ6S6W3ko5J53WiB3G8d5G/Distribution/Parsec/Class.hi 54 | # Declaration for explicitEitherParsec 55 | # Unfolding of explicitEitherParsec: 56 | # Can't find interface-file declaration for variable $fApplicativeParsecT1 57 | # Probable cause: bug in .hi-boot file, or inconsistent .hi file 58 | # Use -ddump-if-trace to get an idea of which file caused the error 59 | # so use this carefully. 60 | # I hope that garbage like the last error will go away once we finally 61 | # no longer have to patch Cabal and inject it into packages. 62 | disableOptimization ? false, 63 | }: 64 | 65 | let 66 | 67 | trace = message: value: 68 | if tracing then builtins.trace message value else value; 69 | 70 | lib = pkgs.lib; 71 | 72 | approachPkgs = pkgs; 73 | 74 | # Function that tells us if a given entry in a `haskellPackages` package set 75 | # is a proper Haskell package (as opposed to some fancy function like 76 | # `.override` and the likes). 77 | isProperHaskellPackage = val: 78 | lib.isDerivation val && # must pass lib.isDerivation 79 | val ? env; # must have an .env key 80 | 81 | # Function that tells us if a given Haskell package has an executable. 82 | # Pass only Haskell packages to this! 83 | # Filter away other stuff with `isProperHaskellPackage` first. 84 | isExecutable = pkg: 85 | (pkgs.haskell.lib.overrideCabal pkg (drv: { 86 | passthru.isExecutable = drv.isExecutable or false; 87 | })).isExecutable; 88 | 89 | # Turn e.g. `Cabal_1_2_3_4` into `1.2.3.4`. 90 | cabalDottedVersion = 91 | builtins.replaceStrings ["_"] ["."] 92 | (builtins.substring 93 | (builtins.stringLength "Cabal_") 94 | (builtins.stringLength defaultCabalPackageVersionComingWithGhc) 95 | defaultCabalPackageVersionComingWithGhc 96 | ); 97 | 98 | areCabalPatchesRequired = 99 | builtins.length (requiredCabalPatchesList cabalDottedVersion) != 0; 100 | 101 | # Nixpkgs contains both Hackage and Stackage packages. 102 | # We want to build only executables that are on Stackage because 103 | # we know that those should build. 104 | # Find all Stackage package names here so we can use them 105 | # as a filter. 106 | # Done by parsing the configuration file that contains 107 | # which packages come from Stackage. 108 | # Contains a list of package names (strings). 109 | stackagePackages = 110 | let 111 | stackageInfoPath = pkgs.path + "/pkgs/development/haskell-modules/configuration-hackage2nix/stackage.yaml"; 112 | pythonWithYaml = normalPkgs.python3Packages.python.withPackages (pkgs: [pkgs.pyyaml]); 113 | stackage-packages-file = normalPkgs.runCommand "stackage-packages" {} '' 114 | ${pythonWithYaml}/bin/python -c 'import yaml, json; x = yaml.safe_load(open("${stackageInfoPath}")); print(json.dumps([line.split(" ")[0] for line in x["default-package-overrides"]]))' > $out 115 | ''; 116 | stackage-packages = builtins.fromJSON (builtins.readFile stackage-packages-file); 117 | in 118 | stackage-packages; 119 | 120 | # Turns a list into a "set" (map where all values are {}). 121 | keySet = list: builtins.listToAttrs (map (name: lib.nameValuePair name {}) list); 122 | 123 | # Making it a set for faster lookup 124 | stackagePackagesSet = keySet stackagePackages; 125 | isStackagePackage = name: builtins.hasAttr name stackagePackagesSet; 126 | 127 | stackageCommit = "8832644c5601994e27f4c5a0d986941c85b52abc"; 128 | stackage-build-constraints-yaml = pkgs.fetchurl { 129 | # Needs to be updated when nixpkgs updates the Stackage LTS from which packages come. 130 | # But we use it only for the blacklist so keeping it tightly up to date is not so critical. 131 | url = "https://raw.githubusercontent.com/commercialhaskell/stackage/${stackageCommit}/build-constraints.yaml"; 132 | sha256 = "1g9w1bicjbji52zjkspa9vqw0ghy8zm59wcmrb53iz87h23c0qkh"; 133 | }; 134 | # The Stackage `build-constraints.yaml` filed as a nix value. 135 | stackage-build-constraints = 136 | let 137 | pythonWithYaml = pkgs.python3Packages.python.withPackages (pkgs: [pkgs.pyyaml]); 138 | # We remove the "packages" key because that one has all the author names, 139 | # which contain unicode escapes, which `builtins.fromJSON` cannot handle 140 | # (as of nix 2.0.4). 141 | build-constraints-json-file = normalPkgs.runCommand "stackage-build-constraints-${stackageCommit}.json" {} '' 142 | ${pythonWithYaml}/bin/python -c 'import yaml, json; x = yaml.load(open("${stackage-build-constraints-yaml}")); del x["packages"]; print(json.dumps(x))' > $out 143 | ''; 144 | in 145 | builtins.fromJSON (builtins.readFile build-constraints-json-file); 146 | 147 | buildPlatformHaskellPackagesWithFixedCabal = with pkgs.haskell.lib; 148 | let 149 | # For cross (`pkgsStatic`) the Setup.hs -> ./Setup compilation happens on 150 | # the *host* platform, not the target platform, so we need to use the 151 | # normal (no-musl) Cabal for that. 152 | # For non-cross (`pkgsMusl`) we need to use the musl-Cabal because 153 | # otherwise we get linking errors with missing glibc symbols. 154 | pkgsToUseForSetupExe = 155 | if approach == "pkgsStatic" 156 | then normalPkgs 157 | else pkgs; 158 | haskellPackagesToUseForSetupExe = 159 | if integer-simple 160 | then pkgsToUseForSetupExe.haskell.packages.integer-simple."${compiler}" 161 | else pkgsToUseForSetupExe.haskell.packages."${compiler}"; 162 | in 163 | haskellPackagesToUseForSetupExe.override (old: { 164 | overrides = pkgs.lib.composeExtensions (old.overrides or (_: _: {})) (self: super: { 165 | 166 | Cabal = 167 | # Note [When Cabal is `null` in a package set] 168 | # 169 | # If null, super.Cabal is a non-overriden package coming with GHC. 170 | # In that case, we can't patch it (we can't add patches to derivations that are null). 171 | # So we need to instead add a not-with-GHC Cabal package and patch that. 172 | # The best choice for that is the version that comes with the GHC. 173 | # Unfortunately we can't query that easily, so we maintain that manually 174 | # in `defaultCabalPackageVersionComingWithGhc`. 175 | # Currently all our Cabal patches are upstreamed, so this is technically 176 | # not necessary currently; however, we keep this infrastructure in case 177 | # we need to patch Cabal again in the future. 178 | # 179 | # If there are no patches to apply, keep original Cabal, 180 | # even if `null` (to get the one that comes with GHC). 181 | if not areCabalPatchesRequired 182 | then super.Cabal 183 | else 184 | if builtins.isNull super.Cabal 185 | then applyPatchesToCabalDrv super."${defaultCabalPackageVersionComingWithGhc}" 186 | else applyPatchesToCabalDrv super.Cabal; 187 | 188 | }); 189 | }); 190 | 191 | # TODO `haskellPackagesWithFailingStackageTestsDisabled` is currently unused 192 | # now that we've switched to overlays, we may want to use it again in the future. 193 | 194 | # A `haskellPackages` set in which tests are skipped (`dontCheck`) for 195 | # all packages that are marked as failing their tests on Stackage 196 | # or known for failing their tests for other reasons. 197 | # Note this may disable more tests than necessary because some packages' 198 | # tests may work fine in nix when they don't work on Stackage, 199 | # for example due to more system dependencies being available. 200 | haskellPackagesWithFailingStackageTestsDisabled = with pkgs.haskell.lib; haskellPackages.override (old: { 201 | overrides = pkgs.lib.composeExtensions (old.overrides or (_: _: {})) (self: super: 202 | let 203 | # This map contains the package names that we don't want to run tests on, 204 | # either because they fail on Stackage or because they fail for us 205 | # with specific reasons given. 206 | skipTestPackageNames = 207 | stackage-build-constraints.expected-test-failures ++ 208 | stackage-build-constraints.skipped-tests ++ 209 | [ 210 | # Tests don't pass on local checkout either (checked on ef3e203e9578) 211 | # because its own executable is not in PATH ("ghc: could not execute: doctest-driver-gen") 212 | "doctest-driver-gen" 213 | # https://github.com/ekmett/ad/issues/73 (floating point precision) 214 | # TODO: Remove when https://github.com/ekmett/ad/pull/76 is merged and available 215 | "ad" 216 | ]; 217 | # Making it a set for faster lookup 218 | failuresSet = keySet skipTestPackageNames; 219 | isFailure = name: builtins.hasAttr name failuresSet; 220 | 221 | packagesWithTestsToDisable = 222 | lib.filterAttrs (name: value: 223 | if isFailure name 224 | then 225 | trace "disabling tests (because it is in skipTestPackageNames) for ${name}" 226 | true 227 | else false 228 | ) super; 229 | packagesWithTestsDisabled = 230 | lib.mapAttrs (name: value: 231 | # We have to do a null check because some builtin packages like 232 | # `text` seem to have just `null` as a value. Not sure why that is. 233 | (if value != null then dontCheck value else value) 234 | ) packagesWithTestsToDisable; 235 | numPackagesTestsDisabled = lib.length (builtins.attrNames packagesWithTestsDisabled); 236 | in 237 | trace "Disabled tests for ${toString numPackagesTestsDisabled} packages" 238 | packagesWithTestsDisabled 239 | ); 240 | }); 241 | 242 | 243 | # Stackage package names we want to blacklist. 244 | blacklist = [ 245 | # TODO: Try to remove when https://github.com/NixOS/nixpkgs/pull/128746 is available to us 246 | "alsa-pcm" "alsa-seq" "ALUT" "OpenAL" "sdl2" "sdl2-gfx" "sdl2-image" "sdl2-mixer" "sdl2-ttf" 247 | 248 | # depends on `sbv` -> `openjdk`, which pulls in a huge dependency closure 249 | "crackNum" 250 | 251 | # Incorrectly depends on `ocaml`, which `pkgsMusl` cannot currently build 252 | # (error: `ld: -r and -pie may not be used together`). 253 | # TODO: Remove when https://github.com/NixOS/cabal2nix/pull/509 has landed. 254 | "liquid-fixpoint" 255 | 256 | # Doesn't build in `normalPkgs.haskellPackages` either 257 | "mercury-api" 258 | 259 | # https://github.com/nh2/static-haskell-nix/issues/6#issuecomment-420494800 260 | "sparkle" 261 | 262 | # These ones currently don't compile for not-yet-investigated reasons: 263 | "amqp-utils" 264 | "elynx" 265 | "hopenpgp-tools" 266 | "hw-eliasfano" 267 | "hw-ip" 268 | "leveldb-haskell" 269 | "mpi-hs" 270 | "mpi-hs-binary" 271 | "mpi-hs-cereal" 272 | "place-cursor-at" 273 | "sandwich-webdriver" 274 | "slynx" 275 | "spacecookie" 276 | "zip" 277 | ]; 278 | 279 | # All Stackage executables who (and whose dependencies) are not marked 280 | # as broken in nixpkgs. 281 | # This is a subset of a `haskellPackages` package set. 282 | stackageExecutables = 283 | let 284 | normalHaskellPackages = 285 | if integer-simple 286 | then pkgs.haskell.packages.integer-simple."${compiler}" 287 | else pkgs.haskell.packages."${compiler}"; 288 | 289 | stackageExecutables = 290 | let 291 | # Predicate copied from nixpkgs' `transitive-broken-packages.nix`: 292 | isEvaluatingUnbroken = v: (builtins.tryEval (v.outPath or null)).success && lib.isDerivation v && !v.meta.broken; 293 | in 294 | lib.filterAttrs 295 | (name: p: 296 | p != null && # packages that come with GHC are `null` 297 | isStackagePackage name && 298 | !(lib.elem name blacklist) && 299 | isExecutable p && 300 | isEvaluatingUnbroken p 301 | ) 302 | normalHaskellPackages; 303 | 304 | stackageExecutablesNames = builtins.attrNames stackageExecutables; 305 | nMany = lib.length stackageExecutablesNames; 306 | in 307 | trace 308 | ("selected stackage executables:\n" 309 | + lib.concatStringsSep "\n" stackageExecutablesNames 310 | + "\n---\n${toString nMany} Stackage executables total" 311 | ) 312 | stackageExecutables; 313 | 314 | # Making it a set for faster lookup 315 | stackageExecutablesSet = keySet (builtins.attrNames stackageExecutables); 316 | isStackageExecutable = name: builtins.hasAttr name stackageExecutablesSet; 317 | 318 | # Cherry-picking cabal fixes 319 | 320 | makeCabalPatch = { name, url, sha256 }: 321 | let 322 | # We use `runCommand` on a plain patch file instead of using 323 | # `fetchpatch`'s `includes` or `stripLen` features to not run 324 | # into the perils of: 325 | # https://github.com/NixOS/nixpkgs/issues/48567 326 | plainPatchFile = pkgs.fetchpatch { inherit name url sha256; }; 327 | 328 | # Explanation: 329 | # * A patch created for the cabal project's source tree will 330 | # always have subdirs `Cabal` and `cabal-install`; the 331 | # `Cabal` nix derivation is already the `Cabal` subtree. 332 | # * We run `filterdiff -i` to keep only changes from the patch 333 | # that apply to the `Cabal` subtree. 334 | # * We run `filterdiff -x` to remove Changelog files which 335 | # almost always conflict. 336 | # * `-p1` strips away the `a/` and `b/` before `-i`/`-x` apply. 337 | # * `strip=2` removes e.g `a/Cabal` so that the patch applies 338 | # directly to that source tree, `--add*prefix` adds back the 339 | # `a/` and `b/` that `patch -p1` expects. 340 | patchOnCabalLibraryFilesOnly = pkgs.runCommand "${name}-Cabal-only" {} '' 341 | ${pkgs.patchutils}/bin/filterdiff \ 342 | -p1 -i 'Cabal/*' -x 'Cabal/ChangeLog.md' \ 343 | --strip=2 --addoldprefix=a/ --addnewprefix=b/ \ 344 | ${plainPatchFile} > $out 345 | 346 | if [ ! -s "$out" ]; then 347 | echo "error: Filtered patch '$out' is empty (while the original patch file was not)!" 1>&2 348 | echo "Check your includes and excludes." 1>&2 349 | echo "Normalizd patch file was:" 1>&2 350 | cat "${plainPatchFile}" 1>&2 351 | exit 1 352 | fi 353 | ''; 354 | 355 | in 356 | patchOnCabalLibraryFilesOnly; 357 | 358 | # Returns the list of patches that a given cabal derivation needs to work well 359 | # for static building. 360 | requiredCabalPatchesList = cabalDottedVersionString: 361 | # Patches we know are merged in a certain cabal version 362 | # (we include them conditionally here anyway, for the case 363 | # that the user specifies a different Cabal version e.g. via 364 | # `stack2nix`): 365 | if pkgs.lib.versionOlder cabalDottedVersionString "3.0.0.0" 366 | then 367 | (builtins.concatLists [ 368 | # -L flag deduplication 369 | # https://github.com/haskell/cabal/pull/5356 370 | (lib.optional (pkgs.lib.versionOlder cabalDottedVersionString "2.4.0.0") (makeCabalPatch { 371 | name = "5356.patch"; 372 | url = "https://github.com/haskell/cabal/commit/fd6ff29e268063f8a5135b06aed35856b87dd991.patch"; 373 | sha256 = "1l5zwrbdrra789c2sppvdrw3b8jq241fgavb8lnvlaqq7sagzd1r"; 374 | })) 375 | # Patches that as of writing aren't merged yet: 376 | ]) ++ [ 377 | # TODO Move this into the above section when merged in some Cabal version: 378 | # --enable-executable-static 379 | # https://github.com/haskell/cabal/pull/5446 380 | (if pkgs.lib.versionOlder cabalDottedVersionString "2.4.0.0" 381 | then 382 | # Older cabal, from https://github.com/nh2/cabal/commits/dedupe-more-include-and-linker-flags-enable-static-executables-flag-pass-ld-options-to-ghc-Cabal-v2.2.0.1 383 | (makeCabalPatch { 384 | name = "5446.patch"; 385 | url = "https://github.com/haskell/cabal/commit/748f07b50724f2618798d200894f387020afc300.patch"; 386 | sha256 = "1zmbalkdbd1xyf0kw5js74bpifhzhm16c98kn7kkgrwql1pbdyp5"; 387 | }) 388 | else 389 | (makeCabalPatch { 390 | name = "5446.patch"; 391 | url = "https://github.com/haskell/cabal/commit/cb221c23c274f79dcab65aef3756377af113ae21.patch"; 392 | sha256 = "02qalj5y35lq22f19sz3c18syym53d6bdqzbnx9f6z3m7xg591p1"; 393 | }) 394 | ) 395 | # TODO Move this into the above section when merged in some Cabal version: 396 | # ld-option passthrough 397 | # https://github.com/haskell/cabal/pull/5451 398 | (if pkgs.lib.versionOlder cabalDottedVersionString "2.4.0.0" 399 | then 400 | # Older cabal, from https://github.com/nh2/cabal/commits/dedupe-more-include-and-linker-flags-enable-static-executables-flag-pass-ld-options-to-ghc-Cabal-v2.2.0.1 401 | (makeCabalPatch { 402 | name = "5451.patch"; 403 | url = "https://github.com/haskell/cabal/commit/b66be72db3b34ea63144b45fcaf61822e0fade87.patch"; 404 | sha256 = "0hndkfb96ry925xzx85km8y8pfv5ka5jz3jvy3m4l23jsrsd06c9"; 405 | }) 406 | else 407 | (makeCabalPatch { 408 | name = "5451.patch"; 409 | url = "https://github.com/haskell/cabal/commit/0aeb541393c0fce6099ea7b0366c956e18937791.patch"; 410 | sha256 = "0pa9r79730n1kah8x54jrd6zraahri21jahasn7k4ng30rnnidgz"; 411 | }) 412 | ) 413 | ] 414 | # cabal >= 3.0.0.0 currently needs no patches. 415 | else []; 416 | 417 | applyPatchesToCabalDrv = cabalDrv: pkgs.haskell.lib.overrideCabal cabalDrv (old: { 418 | patches = (old.patches or []) ++ requiredCabalPatchesList cabalDrv.version; 419 | }); 420 | 421 | useFixedCabal = if !areCabalPatchesRequired then (drv: drv) else 422 | let 423 | patchIfCabal = drv: 424 | if (drv.pname or "") == "Cabal" # the `ghc` package has not `pname` attribute, so we default to "" here 425 | then applyPatchesToCabalDrv drv 426 | else drv; 427 | patchCabalInPackageList = drvs: 428 | let 429 | # Packages that come with the GHC version used have 430 | # `null` as their derivation (e.g. `text` or `Cabal` 431 | # if they are not overridden). We filter them out here. 432 | nonNullPackageList = builtins.filter (drv: !(builtins.isNull drv)) drvs; 433 | in 434 | map patchIfCabal nonNullPackageList; 435 | fixedCabal = buildPlatformHaskellPackagesWithFixedCabal.Cabal; 436 | in 437 | drv: (pkgs.haskell.lib.overrideCabal drv (old: { 438 | # If the package already depends on some explicit version 439 | # of Cabal, patch it, so that it has --enable-executable-static. 440 | # If it doesn't (it depends on the version of Cabal that comes 441 | # with GHC instead), add the same version that comes with 442 | # that GHC, but with our patches. 443 | # Unfortunately we don't have the final package set at hand 444 | # here, so we use the `haskellPackagesWithLibsReadyForStaticLinking` 445 | # one instead which has set `Cabal = ...` appropriately. 446 | setupHaskellDepends = patchCabalInPackageList ((old.setupHaskellDepends or []) ++ [fixedCabal]); 447 | # We don't need to add it to `libraryHaskellDepends` (see note 448 | # [Fixed Cabal for Setup.hs->somePackage->Cabal dependencies]) 449 | # here because we already add it to the package set itself 450 | # down in `haskellLibsReadyForStaticLinkingOverlay`. 451 | # In fact, adding it here breaks e.g. the example in 452 | # `static-stack`, because `stack2nix` adds stacks specified 453 | # `Cabal` dependency as `libraryHaskellDepends` 454 | # (which is then patched via `applyPatchesToCabalDrv` in 455 | # `haskellLibsReadyForStaticLinkingOverlay`) and adding 456 | # it here would add a second, different Cabal version to the 457 | # ghc package DB. 458 | })).overrideAttrs (old: { 459 | # Adding the fixed Cabal version to `setupHaskellDepends` is not enough: 460 | # There may already be one in there, in which case GHC picks an 461 | # arbitrary one. 462 | # So we determine the package key of the Cabal we want, and pass it 463 | # directly to GHC. 464 | # Tip: If you want to debug this when it's failing, see 465 | # https://github.com/NixOS/nixpkgs/issues/65210#issuecomment-513515829 466 | # A common reason for it to fail is when the wrong `compiler` is given; 467 | # in that case, the build log of the `Cabal` package involved will show 468 | # two different ghc versions, and the output's `lib` directory will also 469 | # contain 2 different ghc versions (one with the `.o` files and one with 470 | # the `.conf` file). 471 | preCompileBuildDriver = '' 472 | cabalPackageId=$(basename --suffix=.conf ${fixedCabal}/lib/ghc-*/package.conf.d/*.conf) 473 | echo "Determined cabalPackageId as $cabalPackageId" 474 | 475 | setupCompileFlags="$setupCompileFlags -package-id $cabalPackageId" 476 | ''; 477 | }); 478 | 479 | # Takes a zlib derivation and overrides it to have both .a and .so files. 480 | statify_zlib = zlib_drv: 481 | (zlib_drv.override { 482 | shared = true; 483 | static = true; 484 | splitStaticOutput = false; 485 | }).overrideAttrs (old: { dontDisableStatic = true; }); 486 | 487 | # Takes a curl derivation and overrides it to have both .a and .so files, 488 | # and have the `curl` executable be statically linked. 489 | statify_curl_including_exe = curl_drv: zlib_both: 490 | (curl_drv.override (old: { 491 | # Disable gss support, because that requires `krb5`, which 492 | # (as mentioned in note [krb5 can only be static XOR shared]) is a 493 | # library that cannot build both .a and .so files in its build system. 494 | # That means that if we enable it, we can no longer build the 495 | # dynamically-linked `curl` binary from the overlay 496 | # `archiveFilesOverlay` below where `statify_curl_including_exe` is used. 497 | gssSupport = false; 498 | zlib = zlib_both; 499 | brotliSupport = false; # When brotli is enabled, the `curl` package currently fails to link in `CCLD curl` with error `ld: ../lib/.libs/libcurl.so: undefined reference to `_kBrotliPrefixCodeRanges'` 500 | })).overrideAttrs (old: { 501 | dontDisableStatic = true; 502 | 503 | configureFlags = (old.configureFlags or []) ++ [ 504 | "--enable-static" 505 | # Use environment variable to override the `pkg-config` command 506 | # to have `--static`, as even curl's `--enable-static` configure option 507 | # does not currently make it itself invoke `pkg-config` with that flag. 508 | # See: https://github.com/curl/curl/issues/503#issuecomment-150680789 509 | # While one would usually do 510 | # PKG_CONFIG="pkg-config --static" ./configure ... 511 | # nix's generic stdenv builder does not support passing environment 512 | # variables before `./configure`, and doing `PKG_CONFIG = "false";` 513 | # as a nix attribute doesn't work either for unknown reasons 514 | # (it gets set in the `bash` executing the build, but something resets 515 | # it for the child process invocations); luckily, `./configure` 516 | # also accepts env variables at the end as arguments. 517 | # However, they apparently have to be single paths, so passing 518 | # ./configure ... PKG_CONFIG="pkg-config --static" 519 | # does not work, so we use `writeScript` instead. 520 | # 521 | # (Personally I think that passing `--enable-static` to curl should 522 | # probably instruct it to pass `--static` to `pkg-config` itself.) 523 | "PKG_CONFIG=${pkgs.writeScript "pkgconfig-static-wrapper" "exec pkg-config --static $@"}" 524 | ]; 525 | 526 | # Additionally, flags to also build a static `curl` executable: 527 | 528 | # Note: It is important that in the eventual `libtool` invocation, 529 | # `-all-static` comes before (or instead of) `-static`. 530 | # This is because the first of them "wins setting the mode". 531 | # See https://lists.gnu.org/archive/html/libtool/2006-12/msg00047.html 532 | # libtool makes various problems with static linking. 533 | # Some of them are is well-described by 534 | # https://github.com/sabotage-linux/sabotage/commit/57a989a2e23c9e46501da1227f371da59d212ae4 535 | # However, so far, `-all-static` seems to have the same effect 536 | # of convincing libtool to NOT drop the `-static` flag. 537 | # Other places where this was dicussed (in case you have to debug this in 538 | # the future) are: 539 | # https://debbugs.gnu.org/cgi/bugreport.cgi?bug=11064 540 | # https://github.com/esnet/iperf/issues/632 541 | # Another common thing that people do is to pass `-static --static`, 542 | # with the intent that `--static` isn't eaten by libtool but still 543 | # accepted by e.g. gcc. In our case as of writing (nixpkgs commit bc94dcf50), 544 | # this isn't enough. That is because: 545 | # * The `--with-*=/path` options given to curl's `./configure` 546 | # are usually `.lib` split outputs that contain only headers and 547 | # pkg-config `.pc` files. OK so far. 548 | # * For some of these, e.g. for libssh2, curl's `./configure` turns them 549 | # into `LDFLAGS=-L/...libssh2-dev/lib`, which doesn't do anything to 550 | # libtool, gcc or ld, because `*-dev/lib` contains only `lib/pkgconfig` 551 | # and no libraries. 552 | # * But for others, e.g. for libnghttp2, curl's `./configure` resolves 553 | # them by taking the actual `-L` flags out of the `.pc` file, and turns 554 | # them into e.g. `LDFLAGS=-L/...nghttp2-lib/lib`, which contains 555 | # `{ *.la, *.a, *.so }`. 556 | # * When libtool is invoked with such `LDFLAGS`, it adds two entries to 557 | # `./lib/libcurl.la`'s `dependency_libs=`: `-L/...nghttp2-lib/lib` and 558 | # `/...nghttp2-lib/lib/*.la`. 559 | # When the `.la` path is given, libtool will read it, and pass the 560 | # `.so` file referred to within as a positional argument to e.g. gcc, 561 | # even when linking statically, which will result in linker error 562 | # ld: attempted static link of dynamic object `/...-nghttp2-lib/lib/libnghttp2.so' 563 | # I believe this is what 564 | # https://github.com/sabotage-linux/sabotage/commit/57a989a2e23c9e46501da1227f371da59d212ae4 565 | # fixes. 566 | # If we pass `-all-static` to libtool, it won't do the things in the last 567 | # bullet point, causing static linking to succeed. 568 | makeFlags = [ "curl_LDFLAGS=-all-static" ]; 569 | }); 570 | 571 | 572 | fixGhc = ghcPackage0: lib.pipe ghcPackage0 [ 573 | # musl does not support libdw's alleged need for `dlopen()`, see: 574 | # https://github.com/nh2/static-haskell-nix/pull/116#issuecomment-1585786484 575 | # 576 | # Nixpkgs has the `enableDwarf` argument only for GHCs versions that are built 577 | # with Hadrian (`common-hadrian.nix`), which in nixpkgs is the case for GHC >= 9.6. 578 | # So set `enableDwarf = true`, but not for older versions known to not use Hadrian. 579 | (ghcPackage: 580 | if lib.any (prefix: lib.strings.hasPrefix prefix compiler) ["ghc8" "ghc90" "ghc92" "ghc94"] 581 | then ghcPackage # GHC < 9.6, no Hadrian 582 | else ghcPackage.override { enableDwarf = false; } 583 | ) 584 | (ghcPackage: 585 | ghcPackage.override { 586 | enableRelocatedStaticLibs = useArchiveFilesForTemplateHaskell; 587 | enableShared = !useArchiveFilesForTemplateHaskell; 588 | } 589 | ) 590 | ]; 591 | 592 | 593 | # For static builds, all `buildInputs` should become `propagatedBuildInputs`. 594 | # This is because a final link will necessarily have access to all recursive 595 | # dependencies. 596 | # 597 | # (`pkgsStatic` does this too, as `makeStatic` in `adapters.nix` uses 598 | # the `propagateBuildInputs` adapter.) 599 | # 600 | # Examples where this matters: 601 | # 602 | # * `pkg-config`: 603 | # * `libwebp` depends on `libtiff` which depends on `lerc`. 604 | # `libtiff-4.pc` correctly declares (with my patch 605 | # https://gitlab.com/libtiff/libtiff/-/merge_requests/633): 606 | # 607 | # Libs.private: -llzma -lLerc -ljpeg -ldeflate -lz -lm 608 | # Requires.private: liblzma Lerc libjpeg libdeflate zlib 609 | # 610 | # But the `.pc` file does not include the path on which `libLerc.a` 611 | # can be found. 612 | # Thus we would normally get error: 613 | # 614 | # cannot find -lLerc: No such file or directory 615 | # 616 | # That is supposed to be resolved via `pkg-config --static --libs libtiff-4`, 617 | # which is supposed to chase down the `Requires.private: Lerc` dependency, 618 | # finding the correct path of `libLerc.a` from `Lerc.pc`. 619 | # But for that to work `Lerc.pc` must be on `PKG_CONFIG_PATH`. 620 | # nixpkgs includes the `PKG_CONFIG_PATH` of `lerc` in the build 621 | # of `libwebp` only if `lerc` is in `propagatedBuildInputs` of `libtiff`. 622 | propagatedBuildInputsOverlay = final: previous: { 623 | # Doing this like `pkgsStatic` does it via `makeStatic` in `adapters.nix`. 624 | # Problem build error: 625 | # error: build of '/nix/store/...-stdenv-linux.drv' failed: output '/nix/store/...-stdenv-linux' is not allowed to refer to the following paths: 626 | # /nix/store/...-binutils-patchelfed-ld-wrapper-2.41 627 | # /nix/store/...-pcre2-10.43-dev 628 | # /nix/store/...-gmp-with-cxx-6.3.0-dev 629 | # /nix/store/...-musl-iconv-1.2.3 630 | # /nix/store/...-binutils-2.41 631 | # /nix/store/...-bootstrap-tools 632 | # 633 | # Tipp by sterni: 634 | # 635 | # You only want to apply the adapter to the `pkgsHostTarget` package set's `stdenv`. 636 | # There is no clean way to really detect in an overlay what 637 | # "absolute" position in the chained package sets you are modifying. 638 | # `pkgsStatic` uses `isStatic` as an indicator 639 | # (see `adaptStdenv` in `pkgs/stdenv/cross/default.nix`). 640 | # (I suppose this adapter will only work for cross nixpkgs in its current state.) 641 | stdenv = previous.stdenvAdapters.propagateBuildInputs previous.stdenv; 642 | }; 643 | 644 | # Workaround to the above, overriding packages manually that need it. 645 | propagatedBuildInputsManuallyOverlay = final: previous: 646 | let 647 | prop = drv: drv.overrideAttrs (oldAttrs: { 648 | propagatedBuildInputs = (oldAttrs.propagatedBuildInputs or []) ++ (oldAttrs.buildInputs or []); 649 | }); 650 | in 651 | { 652 | libtiff = prop previous.libtiff; 653 | gd = prop previous.gd; 654 | }; 655 | 656 | # pkgsPropagatedBuildInputs = pkgs.extend propagatedBuildInputsOverlay; 657 | pkgsPropagatedBuildInputs = pkgs.extend propagatedBuildInputsManuallyOverlay; 658 | 659 | 660 | applyDontDisableStatic = pkgsSet: lib.mapAttrs (pkgName: pkgValue: 661 | if pkgValue ? overrideAttrs then 662 | pkgValue.overrideAttrs (old: { dontDisableStatic = true; }) 663 | else 664 | pkgValue) 665 | pkgsSet; 666 | 667 | dontDisableStaticOverlay = final: previous: 668 | (applyDontDisableStatic previous) // { 669 | xorg = applyDontDisableStatic previous.xorg; 670 | }; 671 | 672 | pkgsDontDisableStatic = pkgsPropagatedBuildInputs.extend dontDisableStaticOverlay; 673 | 674 | 675 | # Overlay that enables `.a` files for as many system packages as possible. 676 | # This is in *addition* to `.so` files. 677 | # See also https://github.com/NixOS/nixpkgs/issues/61575 678 | # TODO Instead of overriding each individual package manually, 679 | # override them all at once similar to how `makeStaticLibraries` 680 | # in `adapters.nix` does it (but without disabling shared). 681 | archiveFilesOverlay = final: previous: { 682 | 683 | # Note [Packages that cause bootstrap compiler recompilation] 684 | # The following packages are compiler bootstrap dependencies. 685 | # While we could override them to have static libraries 686 | # (now that https://github.com/NixOS/nixpkgs/issues/61682 is fixed), 687 | # we don't currently because that would make even the compiler bootstrapping recompile. 688 | # Instead we make up new package names with `_static` at the end, 689 | # and explicitly give them to packages. 690 | # See also the above link for the total list of packages that are relevant for this. 691 | # As of original finding it is, as per `pkgs/stdenv/linux/default.nix`: 692 | # gzip bzip2 xz bash coreutils diffutils findutils gawk 693 | # gnumake gnused gnutar gnugrep gnupatch patchelf 694 | # attr acl zlib pcre 695 | # TODO: Check if this really saves enough compilation to be worth the added complexity. 696 | # Alternatively, try to override the bootstrap compiler to use the original 697 | # ones; then we can override the normal names here. 698 | acl_static = previous.acl.overrideAttrs (old: { dontDisableStatic = true; }); 699 | attr_static = previous.attr.overrideAttrs (old: { dontDisableStatic = true; }); 700 | bash_static = previous.bash.overrideAttrs (old: { dontDisableStatic = true; }); 701 | bzip2_static = (previous.bzip2.override { enableStatic = true; }).overrideAttrs (old: { dontDisableStatic = true; }); 702 | coreutils_static = previous.coreutils.overrideAttrs (old: { dontDisableStatic = true; }); 703 | diffutils_static = previous.diffutils.overrideAttrs (old: { dontDisableStatic = true; }); 704 | findutils_static = previous.findutils.overrideAttrs (old: { dontDisableStatic = true; }); 705 | gawk_static = previous.gawk.overrideAttrs (old: { dontDisableStatic = true; }); 706 | gnugrep_static = previous.gnugrep.overrideAttrs (old: { dontDisableStatic = true; }); 707 | gnumake_static = previous.gnumake.overrideAttrs (old: { dontDisableStatic = true; }); 708 | gnupatch_static = previous.gnupatch.overrideAttrs (old: { dontDisableStatic = true; }); 709 | gnused_static = previous.gnused.overrideAttrs (old: { dontDisableStatic = true; }); 710 | gnutar_static = previous.gnutar.overrideAttrs (old: { dontDisableStatic = true; }); 711 | gzip_static = previous.gzip.overrideAttrs (old: { dontDisableStatic = true; }); 712 | patchelf_static = previous.patchelf.overrideAttrs (old: { dontDisableStatic = true; }); 713 | pcre_static = previous.pcre.overrideAttrs (old: { dontDisableStatic = true; }); 714 | xz_static = previous.xz.overrideAttrs (old: { dontDisableStatic = true; }); 715 | zlib_both = statify_zlib previous.zlib; 716 | # Also override the original packages with a throw (which as of writing 717 | # has no effect) so we can know when the bug gets fixed in the future. 718 | # [previously there were overrides here, but they stopped working, read below] 719 | # For unknown reason we can't do this check on `zlib`, because if we do, we get: 720 | # 721 | # while evaluating the attribute 'zlib_static' at /home/niklas/src/haskell/static-haskell-nix/survey/default.nix:498:5: 722 | # while evaluating the attribute 'zlib.override' at /home/niklas/src/haskell/static-haskell-nix/survey/default.nix:525:5: 723 | # while evaluating 'issue_61682_throw' at /home/niklas/src/haskell/static-haskell-nix/survey/default.nix:455:29, called from /home/niklas/src/haskell/static-haskell-nix/survey/default.nix:525:12: 724 | # If you see this, nixpkgs #61682 has been fixed and zlib should be overridden 725 | # 726 | # So somehow, the above `zlib_static` uses *this* `zlib`, even though 727 | # the above uses `previous.zlib.override` and thus shouldn't see this one. 728 | 729 | # Disable failing tests for postgresql on musl that should have no impact 730 | # on the libpq that we need (collate.icu.utf8 and foreign regression tests) 731 | # This approach is copied from PostgREST, see https://github.com/PostgREST/postgrest/pull/2002/files#diff-72929db01d3c689277a1e7777b5df1dbbb20c5de41d1502ff8ac6b443a4e74c6R45 732 | postgresql = (previous.postgresql_14.overrideAttrs (old: { doCheck = false; })).override { 733 | # We need libpq, which does not need systemd, 734 | # and systemd doesn't currently build with musl. 735 | systemdSupport = false; 736 | # Kerberos is problematic on static: 737 | # configure: error: could not find function 'gss_init_sec_context' required for GSSAPI 738 | gssSupport = false; 739 | }; 740 | 741 | procps = previous.procps.override { 742 | # systemd doesn't currently build with musl. 743 | withSystemd = false; 744 | }; 745 | 746 | fontconfig = previous.fontconfig.overrideAttrs (old: { 747 | configureFlags = (old.configureFlags or []) ++ [ 748 | "--enable-static" 749 | ]; 750 | }); 751 | fontforge = previous.fontforge.override ({ 752 | # Currently produces linker errors like: 753 | # ld: ../../lib/libfontforge.so.4: undefined reference to `_kBrotliPrefixCodeRanges' 754 | # ld: ../../lib/libfontforge.so.4: undefined reference to `woff2::Write255UShort(std::vector 755 | # ld: ../../lib/libfontforge.so.4: undefined reference to `BrotliGetDictionary' 756 | # ld: ../../lib/libfontforge.so.4: undefined reference to `woff2::Store255UShort(int, unsigned long*, unsigned char*)' 757 | # ld: ../../lib/libfontforge.so.4: undefined reference to `BrotliDefaultAllocFunc' 758 | woff2 = null; 759 | }); 760 | 761 | libjpeg = previous.libjpeg.override (old: { enableStatic = true; }); 762 | libjpeg_turbo = previous.libjpeg_turbo.override (old: { enableStatic = true; }); 763 | 764 | openblas = (previous.openblas.override { enableStatic = true; }); 765 | 766 | libusb1 = previous.libusb1.override { withStatic = true; enableUdev = false; }; 767 | 768 | openssl = previous.openssl.override { static = true; }; 769 | 770 | zstd = previous.zstd.override { enableStatic = true; }; 771 | 772 | # Disabling kerberos support for now, as openssh's `./configure` fails to 773 | # detect its functions due to linker error, so the build breaks, see #68. 774 | openssh = previous.openssh.override { withKerberos = false; }; 775 | 776 | krb5 = previous.krb5.override { 777 | # Note [krb5 can only be static XOR shared] 778 | # krb5 does not support building both static and shared at the same time. 779 | # That means *anything* on top of this overlay trying to link krb5 780 | # dynamically from this overlay will fail with linker errors. 781 | staticOnly = true; 782 | }; 783 | 784 | # For unclear reasons, brotli builds its `.a` files by default, but adds a `-static` 785 | # infix to their names: 786 | # 787 | # libbrotlicommon-static.a 788 | # libbrotlidec-static.a 789 | # libbrotlienc-static.a 790 | # 791 | # Its pkg-config `.pc` file thus does not support static linking. See: 792 | # https://github.com/google/brotli/issues/795#issuecomment-1373595520 793 | # and 794 | # https://github.com/google/brotli/pull/655#issuecomment-864395830 795 | # 796 | # In my opinion this is rather pointless, because `.a` files are always "static". 797 | # 798 | # We correct it here by renaming the files accordingly. 799 | brotli = previous.brotli.overrideAttrs (old: { 800 | postInstall = '' 801 | for f in "$lib"/lib/*-static.a; do 802 | ln -s --verbose "$f" "$(dirname "$f")/$(basename "$f" '-static.a').a" 803 | done 804 | ''; 805 | }); 806 | 807 | # woff2 currently builds against the `brotli` static libs only with a patch 808 | # that's enabled by its `static` argument. 809 | woff2 = previous.woff2.override { static = true; }; 810 | 811 | libnfc = previous.libnfc.override { static = true; }; 812 | 813 | # See comments on `statify_curl_including_exe` for the interaction with krb5! 814 | # As mentioned in [Packages that cause bootstrap compiler recompilation], we can't 815 | # override zlib to have static libs, so we have to pass in `zlib_both` explicitly 816 | # so that `curl` can use it. 817 | curl = statify_curl_including_exe previous.curl final.zlib_both; 818 | 819 | # curlMinimal also needs to be statified. 820 | curlMinimal = statify_curl_including_exe previous.curlMinimal final.zlib_both; 821 | 822 | # `fetchurl` uses our overridden `curl` above, but `fetchurl` overrides 823 | # `zlib` in `curl`, see 824 | # https://github.com/NixOS/nixpkgs/blob/4a5c0e029ddbe89aa4eb4da7949219fe4e3f8472/pkgs/top-level/all-packages.nix#L296-L299 825 | # so because of [Packages that cause bootstrap compiler recompilation], 826 | # it will undo our `zlib` override in `curl` done above (for `curl` 827 | # use via `fetchurl`). 828 | # So we need to explicitly put our zlib into that one's curl here. 829 | fetchurl = previous.fetchurl.override (old: { 830 | # Can't use `zlib_both` here (infinite recursion), so we 831 | # re-`statify_zlib` `final.zlib` here (interesting that 832 | # `previous.zlib` also leads to infinite recursion at time of writing). 833 | # We also disable kerberos (`gssSupport`) here again, because for 834 | # some unknown reason it sneaks back in. 835 | curl = old.curl.override { zlib = statify_zlib final.zlib; gssSupport = false; }; 836 | }); 837 | 838 | R = (previous.R.override { 839 | # R supports EITHER static or shared libs. 840 | static = true; 841 | # The Haskell package `H` depends on R, which pulls in OpenJDK, 842 | # which is not patched for musl support yet in nixpkgs. 843 | # Disable Java support for now. 844 | javaSupport = false; 845 | }).overrideAttrs (old: { 846 | # Testsuite newly seems to have at least one segfaulting test case. 847 | # Disable test suite for now; Alpine also does it: 848 | # https://git.alpinelinux.org/aports/tree/community/R/APKBUILD?id=e2bce14c748aacb867713cb81a91fad6e8e7f7f6#n56 849 | doCheck = false; 850 | }); 851 | 852 | bash-completion = previous.bash-completion.overrideAttrs (old: { 853 | # Disable tests because it some of them seem dynamic linking specific: 854 | # FAILED test/t/test_getconf.py::TestGetconf::test_1 - assert 856 | doCheck = false; 857 | }); 858 | 859 | # As of writing, emacs still doesn't build, erroring with: 860 | # Segmentation fault ./temacs --batch --no-build-details --load loadup bootstrap 861 | emacs = previous.emacs.override { 862 | # Requires librsvg (in Rust), which gives: 863 | # missing bootstrap url for platform x86_64-unknown-linux-musl 864 | withX = false; 865 | withGTK3 = false; # needs to be disabled because `withX` is disabled above 866 | systemd = null; # does not build with musl 867 | }; 868 | 869 | # Disable sphinx for some python packages to not pull in its huge 870 | # dependency tree; it pulls in among others: 871 | # Ruby, Python, Perl, Rust, OpenGL, Xorg, gtk, LLVM. 872 | # 873 | # Which Python packages to override is informed by this dependency tree: 874 | # nix-store -q --tree $(NIX_PATH=nixpkgs=nixpkgs nix-instantiate survey/default.nix -A workingStackageExecutables) 875 | # and searching for `-sphinx`. 876 | python3 = previous.python3.override { 877 | # Careful, we're using a different self and super here! 878 | packageOverrides = finalPython: previousPython: { 879 | 880 | fonttools = previousPython.fonttools.override { 881 | sphinx = null; 882 | }; 883 | 884 | matplotlib = previousPython.matplotlib.override { 885 | sphinx = null; 886 | }; 887 | 888 | }; 889 | }; 890 | 891 | # I'm not sure why this is needed. As far as I can tell, libdevil doesn't 892 | # directly link to libdeflate. But libdevil does link to libtiff, which 893 | # uses libdeflate. 894 | # 895 | # However, if libdevil doesn't have libdeflate as a buildInput, building 896 | # libdevil fails with linking errors. 897 | # 898 | # I wasn't able to report this upstream, because in nixpkgs libdevil and 899 | # pkgsMusl.libdevil build correctly. Some transitive dependencies for 900 | # pkgsStatic.libdevil fail to build, so it is hard to say whether this is a 901 | # static-haskell-nix problem, or just a static-linking problem. 902 | libdevil = previous.libdevil.overrideAttrs (oldAttrs: { 903 | buildInputs = oldAttrs.buildInputs ++ [ final.libdeflate ]; 904 | }); 905 | 906 | }; 907 | 908 | pkgsWithArchiveFiles = pkgsDontDisableStatic.extend archiveFilesOverlay; 909 | 910 | 911 | setupGhcOverlay = final: previous: 912 | let 913 | initialHaskellPackages = 914 | if integer-simple 915 | # Note we don't have to set the `-finteger-simple` flag for packages that GHC 916 | # depends on (e.g. text), because nix + GHC already do this for us: 917 | # https://github.com/ghc/ghc/blob/ghc-8.4.3-release/ghc.mk#L620-L626 918 | # https://github.com/peterhoeg/nixpkgs/commit/50050f3cc9e006daa6800f15a29e258c6e6fa4b3#diff-2f6f8fd152c14d37ebd849aa6382257aR35 919 | then final.haskell.packages.integer-simple."${compiler}" 920 | else final.haskell.packages."${compiler}"; 921 | in 922 | { 923 | haskell = previous.haskell // { 924 | packages = previous.haskell.packages // { 925 | "${compiler}" = previous.haskell.packages."${compiler}".override { 926 | ghc = fixGhc previous.haskell.compiler."${compiler}"; 927 | }; 928 | }; 929 | }; 930 | haskellPackages = initialHaskellPackages; 931 | }; 932 | 933 | pkgsWithGhc = pkgsWithArchiveFiles.extend setupGhcOverlay; 934 | 935 | 936 | # This overlay "fixes up" Haskell libraries so that static linking works. 937 | # See note "Don't add new packages here" below! 938 | haskellLibsReadyForStaticLinkingOverlay = final: previous: { 939 | # Helper function to add pkg-config static lib flags to a Haskell derivation. 940 | # We put it directly into the `pkgs` package set so that following overlays 941 | # can use it as well if they want to. 942 | # 943 | # Note that for linking the order of libraries given on the command line matters: 944 | # https://stackoverflow.com/questions/11893996/why-does-the-order-of-l-option-in-gcc-matter 945 | # Before my GHC change https://gitlab.haskell.org/ghc/ghc/merge_requests/1589 946 | # was merged that ensured the order is correct, we used a hack using 947 | # `--ld-option=-Wl,--start-group` to make the order not matter. 948 | staticHaskellHelpers.addStaticLinkerFlagsWithPkgconfig = haskellDrv: pkgConfigNixPackages: pkgconfigFlagsString: 949 | with final.haskell.lib; overrideCabal haskellDrv (old: { 950 | # We can't pass all linker flags in one go as `ld-options` because 951 | # the generic Haskell builder doesn't let us pass flags containing spaces. 952 | preConfigure = builtins.concatStringsSep "\n" [ 953 | (old.preConfigure or "") 954 | # Note: Assigning the `pkg-config` output to a variable instead of 955 | # substituting it directly in the `for` loop so that `set -e` catches 956 | # when it fails. 957 | # See https://unix.stackexchange.com/questions/23026/how-can-i-get-bash-to-exit-on-backtick-failure-in-a-similar-way-to-pipefail/23099#23099 958 | # This was a bug for long where we didn't notice; shell is unsafe garbage. 959 | '' 960 | set -e 961 | 962 | PKGCONFIG_OUTPUT=$(pkg-config --static ${pkgconfigFlagsString}) 963 | 964 | configureFlags+=$(for flag in $PKGCONFIG_OUTPUT; do echo -n " --ld-option=$flag"; done) 965 | '' 966 | ]; 967 | # TODO Somehow change nixpkgs (the generic haskell builder?) so that 968 | # putting `curl_static` into `libraryPkgconfigDepends` is enough 969 | # and the manual modification of `configureFlags` is not necessary. 970 | libraryPkgconfigDepends = (old.libraryPkgconfigDepends or []) ++ pkgConfigNixPackages; 971 | # `generic-builder.nix` already adds `pkg-config` as a `nativeBuildInput` 972 | # e.g. if `libraryPkgconfigDepends` is not empty, but if it was, and we 973 | # override it here, it does not notice, so we add `pkg-config` explicitly 974 | # in that case. 975 | buildTools = (old.buildTools or []) ++ [ pkgs.pkg-config ]; 976 | }); 977 | 978 | 979 | haskellPackages = previous.haskellPackages.override (old: { 980 | overrides = final.lib.composeExtensions (old.overrides or (_: _: {})) (self: super: 981 | with final.haskell.lib; 982 | with final.staticHaskellHelpers; 983 | let 984 | callCabal2nix = 985 | final.haskellPackages.callCabal2nix; 986 | 987 | add_integer-simple_if_needed = haskellPkgs: haskellPkgs // ( 988 | # If the `integer-simple` flag is given, and there isn't already 989 | # an `integer-simple` in the Haskell package set, add one as `null`. 990 | # This works around the problem that `stack2nix`-generated Haskell 991 | # package sets lack the `integer-simple` entries, even when everything 992 | # is compiled with integer-simple, which leads to 993 | # Setup: Encountered missing dependencies: 994 | # integer-simple 995 | # This PR fixes it in stack2nix: 996 | # https://github.com/input-output-hk/stack2nix/pull/167 997 | # We still maintain the addition here so that users can use upstream 998 | # `stack2nix` without problems. 999 | if integer-simple && !(builtins.hasAttr "integer-simple" haskellPkgs) then { 1000 | integer-simple = null; 1001 | } else {}); 1002 | 1003 | in add_integer-simple_if_needed { 1004 | 1005 | # This overrides settings for all Haskell packages. 1006 | mkDerivation = attrs: super.mkDerivation (attrs // { 1007 | 1008 | # Disable haddocks to save time and because for some reason, haddock (e.g. for aeson) 1009 | # fails with 1010 | # : can't load .so/.DLL for: libgmp.so (libgmp.so: cannot open shared object file: No such file or directory) 1011 | # when we use `pkgsStatic`. Need to investigate. 1012 | doHaddock = false; 1013 | 1014 | # Disable profiling to save build time. 1015 | enableLibraryProfiling = false; 1016 | enableExecutableProfiling = false; 1017 | 1018 | # Skip tests on -O0 because some tests are extremely slow on -O0. 1019 | # This prevents us from finding upstream correctness issues that 1020 | # appear only with -O0, 1021 | # such as https://github.com/bos/double-conversion/issues/26 1022 | # but that's OK for now as we want -O0 mainly for faster feedback. 1023 | # doCheck = !disableOptimization; 1024 | 1025 | # If `disableOptimization` is on for fast iteration, pass `-O0` to GHC. 1026 | # We use `buildFlags` instead of `configureFlags` so that it's 1027 | # also in effect for packages which specify e.g. 1028 | # `ghc-options: -O2` in their .cabal file. 1029 | buildFlags = (attrs.buildFlags or []) ++ builtins.concatLists [ 1030 | (final.lib.optional disableOptimization "--ghc-option=-O0") 1031 | # GHC compilation does not scale well on high-core machines like our CI. 1032 | # Making compilation more efficient. 1033 | # (map (o: "--ghc-option=" + o) [ 1034 | # "-j4" # Limit parallel compilation 1035 | # "+RTS" 1036 | # "-maxN4" # Limit threads 1037 | # "-A64M" "-qb0" # See https://trofi.github.io/posts/193-scaling-ghc-make.html 1038 | # "-RTS" 1039 | # ]) 1040 | ]; 1041 | 1042 | # Note [Slow stripping]: 1043 | # There is currently a 300x `strip` performance regression in 1044 | # `binutils`, making some strips take 5 minutes instead of 1 second. 1045 | # Disable stripping of libraries (`.a` files) until it's solved: 1046 | # https://github.com/NixOS/nixpkgs/issues/129467 1047 | # https://sourceware.org/bugzilla/show_bug.cgi?id=28058 1048 | # We continue to strip executables because they don't seem to 1049 | # be affected by the regression. 1050 | # 1051 | # There is also the consideration to keep `dontStrip = true;` the default, 1052 | # so that people can decide at the very end whether they prefer small 1053 | # executable sizes, or increased debuggability by keeping debug symbols. 1054 | # However, until the `-g` issue 1055 | # https://gitlab.haskell.org/ghc/ghc/-/issues/15960 1056 | # is figured out, it may be best to not enable `-g`, and thus 1057 | # not-stripping isn't as useful. 1058 | configureFlags = (attrs.configureFlags or []) ++ [ "--disable-library-stripping" ]; 1059 | }); 1060 | 1061 | # Note: 1062 | # 1063 | # Don't add new packages here. 1064 | # 1065 | # Only override existing ones in the minimal way possible. 1066 | # This is for the use case that somebody passes us a Haskell package 1067 | # set (e.g. generated with `stack2nix`) and just wants us to fix 1068 | # up all their packages so that static linking works. 1069 | # If we unconditionally add packages here, we will override 1070 | # whatever packages they've passed us in. 1071 | 1072 | # Override zlib Haskell package to use the system zlib package 1073 | # that has `.a` files added. 1074 | # This is because the system zlib package can't be overridden accordingly, 1075 | # see note [Packages that cause bootstrap compiler recompilation]. 1076 | zlib = super.zlib.override { zlib = final.zlib_both; }; 1077 | 1078 | # The `properties` test suite takes > 30 minutes with `-O0`. 1079 | aeson-diff = 1080 | (if disableOptimization then dontCheck else lib.id) 1081 | super.aeson-diff; 1082 | 1083 | # Tests use `inspection-testing` which cannot work on `-O0`. 1084 | ap-normalize = 1085 | (if disableOptimization then dontCheck else lib.id) 1086 | super.ap-normalize; 1087 | 1088 | # Fails the tests 1089 | # issue379.Lazy.unionWith 1090 | # issue379.union 1091 | # TODO: File an upstream bug for that, after reproducing it outside of nix. 1092 | unordered-containers = 1093 | (if disableOptimization then dontCheck else lib.id) 1094 | super.unordered-containers; 1095 | 1096 | # Fails the tests 1097 | # FromJSONKey 1098 | # -: OK 1099 | # -: OK 1100 | # -: OK 1101 | # -: OK 1102 | # -: FAIL 1103 | # tests/UnitTests.hs:309: 1104 | # Const Text 1105 | # Use -p '$0=="tests.unit.FromJSONKey.-"' to rerun this test only. 1106 | # 1107 | # TODO: File an upstream bug for that, after reproducing it outside of nix. 1108 | aeson = 1109 | (if disableOptimization then dontCheck else lib.id) 1110 | super.aeson; 1111 | 1112 | # Has an inspection test which fails on -O0, which makse sense. 1113 | postgresql-simple = 1114 | (if disableOptimization then dontCheck else lib.id) 1115 | super.postgresql-simple; 1116 | 1117 | arbtt = 1118 | addStaticLinkerFlagsWithPkgconfig 1119 | super.arbtt 1120 | (with final; [ 1121 | nettle 1122 | 1123 | xorg.libX11 1124 | xorg.libXdmcp 1125 | xorg.libXau 1126 | xorg.libxcb 1127 | ]) 1128 | "--libs nettle x11-xcb"; 1129 | 1130 | # `criterion`'s test suite fails with a timeout if its dependent 1131 | # libraries (apparently `bytestring`) are compiled with `-O0`. 1132 | # Even increasing the timeout 5x did not help! 1133 | criterion = 1134 | (if disableOptimization then dontCheck else lib.id) 1135 | super.criterion; 1136 | 1137 | # `double-conversion`'s test suite fails when `-O0` is used 1138 | # because `realToFrac NaN /= NaN` on `-O0` (Haskell does not 1139 | # provide a reasonable way to convert `Double -> CDouble`, 1140 | # totally bonkers). 1141 | # See https://github.com/bos/double-conversion/issues/26 1142 | double-conversion = 1143 | (if disableOptimization then dontCheck else lib.id) 1144 | super.double-conversion; 1145 | 1146 | blaze-textual = 1147 | let 1148 | # `blaze-textual`'s implementation is wrong when `-O0` is used, 1149 | # see https://github.com/bos/blaze-textual/issues/11. 1150 | # If we did `disableOptimization`, re-enable it for this package. 1151 | # TODO Remove this when https://github.com/bos/blaze-textual/pull/12 is merged and in nixpkgs. 1152 | handleDisableOptimisation = drv: 1153 | if disableOptimization 1154 | then appendBuildFlag drv "--ghc-option=-O" 1155 | else drv; 1156 | # `blaze-textual` has a flag that needs to be given explicitly 1157 | # if `integer-simple` is to be used. 1158 | # TODO Put this into the `integer-simple` compiler set in nixpkgs? In: 1159 | # https://github.com/NixOS/nixpkgs/blob/ef89b398/pkgs/top-level/haskell-packages.nix#L184 1160 | handleIntegerSimple = drv: 1161 | if integer-simple 1162 | then enableCabalFlag drv "integer-simple" 1163 | else drv; 1164 | in handleIntegerSimple (handleDisableOptimisation super.blaze-textual); 1165 | 1166 | # `weigh`'s test suite fails when `-O0` is used 1167 | # because that package inherently relies on optimisation to be on. 1168 | weigh = 1169 | (if disableOptimization then dontCheck else lib.id) 1170 | super.weigh; 1171 | 1172 | # Tests use `inspection-testing` which cannot work on `-O0`. 1173 | generic-data = 1174 | (if disableOptimization then dontCheck else lib.id) 1175 | super.generic-data; 1176 | 1177 | # Tests use `inspection-testing` which cannot work on `-O0`. 1178 | # This seems to happen only in nixpkgs, I could not reproduce it with `stack test --fast`. 1179 | # 1180 | # 1) cast correctly fails to extract aChar from A 1181 | # Make sure the expression has an NFData instance! See docs at https://github.com/CRogers/should-not-typecheck#nfdata-a-constraint. Full error: 1182 | # tests/Examples.hs:188:7: error: [GHC-39999] 1183 | # • Ambiguous type variable ‘a0’ arising from a use of ‘shouldNotTypecheck’ 1184 | # prevents the constraint ‘(NFData a0)’ from being solved. 1185 | # Probable fix: use a type annotation to specify what ‘a0’ should be. 1186 | records-sop = 1187 | (if disableOptimization then dontCheck else lib.id) 1188 | super.records-sop; 1189 | 1190 | # `HsOpenSSL` has a bug where assertions are only triggered on `-O0`. 1191 | # This breaks its test suite. 1192 | # https://github.com/vshabanov/HsOpenSSL/issues/44 1193 | HsOpenSSL = 1194 | (if disableOptimization then dontCheck else lib.id) 1195 | super.HsOpenSSL; 1196 | 1197 | # Note [Fixed Cabal for Setup.hs->somePackage->Cabal dependencies] 1198 | # We have to add our fixed Cabal to the package set because otherwise 1199 | # packages that depend on Cabal (e.g. `cabal-doctest`) will depend 1200 | # on the unfixed Cabal, and when some other Setup.hs depends 1201 | # on such a package, GHC will choose the unfixed Cabal to use. 1202 | # `pkgsStatic` does not need this because with it, because when 1203 | # cross-compiling, the Setup.hs is compiled with a completely different 1204 | # package set. 1205 | # Example packages: 1206 | # Some getting: unrecognized 'configure' option `--enable-executable-static' 1207 | # influxdb 1208 | # wreq 1209 | # servant-server 1210 | # Some getting: *** abort because of serious configure-time warning from Cabal (multiple different package versions in project) 1211 | # stack2nix 1212 | Cabal = if !areCabalPatchesRequired then super.Cabal else # no patches needed -> don't even try to access any attributes 1213 | if approach == "pkgsMusl" 1214 | then ( # Example package where this matters: `focuslist` 1215 | # See note [When Cabal is `null` in a package set]. 1216 | # Also note we can't just use `buildPlatformHaskellPackagesWithFixedCabal.Cabal` 1217 | # here because that one may have different dependencies 1218 | # (e.g. `text` may have been overridden here but not there), 1219 | # which would lead to the 1220 | # This package indirectly depends on multiple versions of the same package 1221 | # warning. 1222 | if builtins.isNull super.Cabal 1223 | # Note this addition is an exception to the "Don't add new packages here" 1224 | # rule from above, and we only do it if Cabal is not yet 1225 | # in the package set. 1226 | then applyPatchesToCabalDrv super."${defaultCabalPackageVersionComingWithGhc}" 1227 | else applyPatchesToCabalDrv super.Cabal 1228 | ) 1229 | else super.Cabal; # `pkgsStatic` does not need that 1230 | 1231 | # Helpers for other packages 1232 | 1233 | hpc-coveralls = appendPatch super.hpc-coveralls (builtins.fetchurl "https://github.com/guillaume-nargeot/hpc-coveralls/pull/73/commits/344217f513b7adfb9037f73026f5d928be98d07f.patch"); 1234 | 1235 | conduit-extra = 1236 | # TODO Remove this once we no longer care about conduit-extra < 1.3.1.1. 1237 | # Test-suite failing nondeterministically, see https://github.com/snoyberg/conduit/issues/385 1238 | # I've already checked that it's fixed on 1.3.1.1; we just keep this 1239 | # for a while longer for `stack2nix` users. 1240 | (if final.lib.versionOlder super.conduit-extra.version "1.3.1.1" then dontCheck else lib.id) 1241 | super.conduit-extra; 1242 | 1243 | # See https://github.com/hslua/hslua/issues/67 1244 | # It's not clear if it's safe to disable this as key functionality may be broken 1245 | hslua = dontCheck super.hslua; 1246 | 1247 | # Test suite takes > 1h CPU time with 1600% CPU on my CI machine. 1248 | hw-balancedparens = dontCheck super.hw-balancedparens; 1249 | 1250 | # Test suite tries to connect to dbus, can't work in sandbox. 1251 | credential-store = dontCheck super.credential-store; 1252 | 1253 | # Test suite calls all kinds of shell utilities, can't work in sandbox. 1254 | dotenv = dontCheck super.dotenv; 1255 | 1256 | # Test suite fails time-dependently: 1257 | # https://github.com/peti/cabal2spec/commit/6078778c06be45eb468f4770a3924c7be190f558 1258 | # TODO: Remove once a release > 2.4.1 is available to us. 1259 | cabal2spec = dontCheck super.cabal2spec; 1260 | 1261 | # Single test suite failure: 1262 | # set;get socket option (Pub): FAIL 1263 | # *** Failed! Exception: 'ZMQError { errno = 22, source = "setByteStringOpt", message = "Invalid argument" }' (after 1 test): 1264 | # ZapDomain (Restricted "") 1265 | # Use --quickcheck-replay=307313 to reproduce. 1266 | zeromq4-haskell = dontCheck super.zeromq4-haskell; 1267 | 1268 | # Fails in doctests with: 1269 | # doctests: /nix/store/v5lw9170rw5s9vm69qsmd5ybns7yv2dj-ghc-8.6.4/lib/ghc-8.6.4/ghc-prim-0.5.3/HSghc-prim-0.5.3.o: unknown symbol `exp' 1270 | # doctests: doctests: unable to load package `ghc-prim-0.5.3' 1271 | lens-regex = dontCheck super.lens-regex; 1272 | 1273 | # Fails in doctests with: 1274 | # focuslist-doctests: /nix/store/v5lw9170rw5s9vm69qsmd5ybns7yv2dj-ghc-8.6.4/lib/ghc-8.6.4/ghc-prim-0.5.3/HSghc-prim-0.5.3.o: unknown symbol `exp' 1275 | # focuslist-doctests: focuslist-doctests: unable to load package `ghc-prim-0.5.3' 1276 | focuslist = dontCheck super.focuslist; 1277 | 1278 | # Fails in doctests with: 1279 | # doctests: /nix/store/nda51m9gymbx9qvzmjpfd4393jqq0gdm-ghc-8.6.5/lib/ghc-8.6.5/ghc-prim-0.5.3/HSghc-prim-0.5.3.o: unknown symbol `exp' 1280 | # doctests: doctests: unable to load package `ghc-prim-0.5.3' 1281 | yesod-paginator = dontCheck super.yesod-paginator; 1282 | 1283 | # Workaround for `zip`'s dependency `bzlib-conduit` using `extra-libraries: bz` 1284 | # instead of `pkgconfig-depends`. 1285 | # TODO: Make a PR to fix that. 1286 | zip = 1287 | addStaticLinkerFlagsWithPkgconfig 1288 | (super.zip.overrideAttrs (old: { configureFlags = (old.configureFlags or []) ++ ["--ghc-options=-v"]; })) 1289 | [ final.bzip2_static ] 1290 | "--libs bzip2"; 1291 | 1292 | # Override libs explicitly that can't be overridden with overlays. 1293 | # See note [Packages that cause bootstrap compiler recompilation]. 1294 | regex-pcre = super.regex-pcre.override { pcre = final.pcre_static; }; 1295 | pcre-light = super.pcre-light.override { pcre = final.pcre_static; }; 1296 | bzlib-conduit = super.bzlib-conduit.override { bzip2 = final.bzip2_static; }; 1297 | 1298 | # Tests fail with: doctests: : Dynamic loading not supported 1299 | BNFC = dontCheck super.BNFC; 1300 | 1301 | # 200 ms test timeout is not suitable for massively parallel CI. 1302 | # See https://github.com/jensblanck/cdar/issues/7 1303 | cdar-mBound = dontCheck super.cdar-mBound; 1304 | 1305 | darcs = 1306 | addStaticLinkerFlagsWithPkgconfig 1307 | # (super.darcs.override { curl = curl_static; }) 1308 | super.darcs 1309 | [ final.curl ] 1310 | # Ideally we'd like to use 1311 | # pkg-config --static --libs libcurl 1312 | # but that doesn't work because that output contains `-Wl,...` flags 1313 | # which aren't accepted by `ld` and thus cannot be passed as `ld-option`s. 1314 | # See https://github.com/curl/curl/issues/2775 for an investigation of why. 1315 | "--libs-only-L --libs-only-l libcurl"; 1316 | 1317 | # For https://github.com/BurntSushi/erd/issues/40 1318 | # As of writing, not in Stackage. 1319 | # Currently fails with linker error, see `yesod-paginator` below. 1320 | erd = doJailbreak super.erd; 1321 | 1322 | # Test timeout is too tight on `-O0`. 1323 | Glob = 1324 | (if disableOptimization then dontCheck else lib.id) 1325 | super.Glob; 1326 | 1327 | # Tests fail with: doctests: : Dynamic loading not supported 1328 | headroom = dontCheck super.headroom; 1329 | 1330 | # We need to explicitly get blas from openblasCompat, since 1331 | # otherwise openblas is used and Haskell programs like `resistor-cube` 1332 | # won't be able to find libblas. 1333 | blas-ffi = super.blas-ffi.override { 1334 | blas = final.openblasCompat; 1335 | }; 1336 | 1337 | hmatrix = 1338 | # musl does not have `random_r()`. 1339 | (enableCabalFlag super.hmatrix "no-random_r") 1340 | .override { openblasCompat = final.openblasCompat; }; 1341 | 1342 | # Tests fail with: doctests: : Dynamic loading not supported 1343 | hw-xml = dontCheck super.hw-xml; 1344 | hw-packed-vector = dontCheck super.hw-packed-vector; 1345 | 1346 | # Test suite segfaults (perhaps because R's test suite also does?). 1347 | inline-r = dontCheck super.inline-r; 1348 | 1349 | # Tests fail with: doctests: : Dynamic loading not supported 1350 | openapi3 = dontCheck super.openapi3; 1351 | 1352 | mysql-json-table = 1353 | addStaticLinkerFlagsWithPkgconfig 1354 | super.mysql-json-table 1355 | [ final.openssl final.zlib_both ] 1356 | "--libs openssl zlib"; 1357 | 1358 | # TODO For the below packages, it would be better if we could somehow make all users 1359 | # of postgresql-libpq link in openssl via pkgconfig. 1360 | hasql-notifications = 1361 | addStaticLinkerFlagsWithPkgconfig 1362 | super.hasql-notifications 1363 | [ final.openssl final.postgresql ] 1364 | "--libs libpq"; 1365 | hasql-queue = 1366 | addStaticLinkerFlagsWithPkgconfig 1367 | super.hasql-queue 1368 | [ final.openssl final.postgresql ] 1369 | "--libs libpq"; 1370 | pg-harness-server = 1371 | addStaticLinkerFlagsWithPkgconfig 1372 | super.pg-harness-server 1373 | [ final.openssl final.postgresql ] 1374 | "--libs libpq"; 1375 | postgrest = 1376 | addStaticLinkerFlagsWithPkgconfig 1377 | super.postgrest 1378 | [ final.openssl final.postgresql ] 1379 | "--libs libpq"; 1380 | postgresql-orm = 1381 | addStaticLinkerFlagsWithPkgconfig 1382 | super.postgresql-orm 1383 | [ final.openssl final.postgresql ] 1384 | "--libs libpq"; 1385 | postgresql-schema = 1386 | addStaticLinkerFlagsWithPkgconfig 1387 | super.postgresql-schema 1388 | [ final.openssl final.postgresql ] 1389 | "--libs openssl libpq"; 1390 | postgresql-simple-migration = 1391 | addStaticLinkerFlagsWithPkgconfig 1392 | super.postgresql-simple-migration 1393 | [ final.openssl ] 1394 | "--libs openssl"; 1395 | squeal-postgresql = 1396 | addStaticLinkerFlagsWithPkgconfig 1397 | super.squeal-postgresql 1398 | [ final.openssl final.postgresql ] 1399 | "--libs openssl libpq"; 1400 | tmp-postgres = 1401 | addStaticLinkerFlagsWithPkgconfig 1402 | (dontCheck super.tmp-postgres) # flaky/stuck tests: https://github.com/jfischoff/tmp-postgres/issues/273 1403 | [ final.openssl ] 1404 | "--libs openssl"; 1405 | 1406 | # This one needs `libcrypto` explicitly for reasons not yet investigated. `libpq` should pull it in via `openssl`. 1407 | postgresql-migration = 1408 | addStaticLinkerFlagsWithPkgconfig 1409 | super.postgresql-migration 1410 | [ final.openssl final.postgresql ] 1411 | "--libs libpq libcrypto"; 1412 | 1413 | xml-to-json = 1414 | addStaticLinkerFlagsWithPkgconfig 1415 | super.xml-to-json 1416 | [ final.curl final.expat ] 1417 | # Ideally we'd like to use 1418 | # pkg-config --static --libs libcurl 1419 | # but that doesn't work because that output contains `-Wl,...` flags 1420 | # which aren't accepted by `ld` and thus cannot be passed as `ld-option`s. 1421 | # See https://github.com/curl/curl/issues/2775 for an investigation of why. 1422 | "--libs-only-L --libs-only-l libcurl expat"; 1423 | 1424 | nfc = 1425 | addStaticLinkerFlagsWithPkgconfig 1426 | super.nfc 1427 | [ final.libusb-compat-0_1 final.libusb1 ] 1428 | "--libs libusb"; 1429 | 1430 | # This package's dependency `rounded` currently fails its test with a patterm match error. 1431 | aern2-real = 1432 | addStaticLinkerFlagsWithPkgconfig 1433 | super.aern2-real 1434 | [ final.mpfr final.gmp ] 1435 | "--libs mpfr gmp"; 1436 | 1437 | hopenpgp-tools = 1438 | addStaticLinkerFlagsWithPkgconfig 1439 | super.hopenpgp-tools 1440 | [ final.nettle final.bzip2_static ] 1441 | "--libs nettle bzip2"; 1442 | 1443 | sdl2-gfx = 1444 | addStaticLinkerFlagsWithPkgconfig 1445 | super.sdl2-gfx 1446 | (with final; [ 1447 | nettle 1448 | SDL2 1449 | SDL2_gfx 1450 | 1451 | xorg.libX11 1452 | libXext 1453 | libXcursor 1454 | xorg.libXdmcp 1455 | libXinerama 1456 | libXi 1457 | libXrandr 1458 | libXxf86vm 1459 | libXScrnSaver 1460 | xorg.libXrender 1461 | libXfixes 1462 | xorg.libXau 1463 | xorg.libxcb 1464 | xorg.libpthreadstubs 1465 | ]) 1466 | "--libs nettle sdl2 SDL2_gfx xcursor"; 1467 | 1468 | sdl2-image = 1469 | addStaticLinkerFlagsWithPkgconfig 1470 | super.sdl2-image 1471 | (with final; [ 1472 | nettle 1473 | SDL2 1474 | SDL2_image 1475 | 1476 | xorg.libX11 1477 | libXext 1478 | libXcursor 1479 | xorg.libXdmcp 1480 | libXinerama 1481 | libXi 1482 | libXrandr 1483 | libXxf86vm 1484 | libXScrnSaver 1485 | xorg.libXrender 1486 | libXfixes 1487 | xorg.libXau 1488 | xorg.libxcb 1489 | xorg.libpthreadstubs 1490 | 1491 | libjpeg 1492 | libpng 1493 | libtiff 1494 | zlib_both 1495 | lzma 1496 | libwebp 1497 | ]) 1498 | "--libs nettle sdl2 SDL2_image xcursor libpng libjpeg libtiff-4 libwebp"; 1499 | 1500 | # Test hangs for 10 hours on CI machine. 1501 | midi = dontCheck super.midi; 1502 | 1503 | # With optimisations disabled, some tests of its test suite don't 1504 | # finish within the 25 seconds timeout. 1505 | skylighting-core = 1506 | (if disableOptimization then dontCheck else lib.id) 1507 | super.skylighting-core; 1508 | 1509 | # Test suite loops forever without optimisations.. 1510 | text-short = 1511 | (if disableOptimization then dontCheck else lib.id) 1512 | super.text-short; 1513 | 1514 | # Flaky QuickCheck test failure: 1515 | # *** Failed! "00:01": expected Just 00:00:60, found Just 00:01:00 (after 95 tests and 2 shrinks): 1516 | # See https://github.com/haskellari/time-compat/issues/23 1517 | time-compat = dontCheck super.time-compat; 1518 | 1519 | # Test suite takes > 1h CPU time with 1600% CPU on my CI machine. 1520 | # Does pass after that time though, maybe high thread counds work badly here. 1521 | tomland = dontCheck super.tomland; 1522 | 1523 | # Added for #14 1524 | tttool = callCabal2nix "tttool" (final.fetchFromGitHub { 1525 | owner = "entropia"; 1526 | repo = "tip-toi-reveng"; 1527 | rev = "f83977f1bc117f8738055b978e3cfe566b433483"; 1528 | sha256 = "05bbn63sn18s6c7gpcmzbv4hyfhn1i9bd2bw76bv6abr58lnrwk3"; 1529 | }) {}; 1530 | 1531 | # TODO Find out why these overrides are necessary, given that they all come from `final` 1532 | # (somehow without them, xmonad gives linker errors). 1533 | # Most likely it is because the `libX*` packages are available once on the top-level 1534 | # namespace (where we override them), and once under `xorg.libX*`, where we don't 1535 | # override them; it seems that `X11` depends on the latter. 1536 | X11 = super.X11.override { 1537 | libX11 = final.xorg.libX11; 1538 | libXext = final.xorg.libXext; 1539 | libXinerama = final.xorg.libXinerama; 1540 | libXrandr = final.xorg.libXrandr; 1541 | libXrender = final.xorg.libXrender; 1542 | libXScrnSaver = final.xorg.libXScrnSaver; 1543 | }; 1544 | 1545 | # Note that xmonad links, but it doesn't run, because it tries to open 1546 | # `libgmp.so.3` at run time. 1547 | xmonad = 1548 | let 1549 | # Work around xmonad in `haskell-packages.nix` having hardcoded `$doc` 1550 | # which is the empty string when haddock is disabled. 1551 | # Same as https://github.com/NixOS/nixpkgs/pull/61526 but for 1552 | # https://github.com/NixOS/cabal2nix/blob/fe32a4cdb909cc0a25d37ec371453b1bb0d4f134/src/Distribution/Nixpkgs/Haskell/FromCabal/PostProcess.hs#L294-L295 1553 | # TODO: Remove when https://github.com/NixOS/cabal2nix/pull/416 is merged and available in nixpkgs. 1554 | fixPostInstallWithHaddockDisabled = pkg: overrideCabal pkg (old: { postInstall = ""; }); 1555 | in 1556 | appendConfigureFlag (addStaticLinkerFlagsWithPkgconfig 1557 | (fixPostInstallWithHaddockDisabled super.xmonad) 1558 | (with final; [ xorg.libpthreadstubs xorg.libxcb xorg.libXau xorg.libXrender xorg.libXdmcp ]) 1559 | "--libs xcb xau xrender xdmcp") [ 1560 | ]; 1561 | 1562 | leveldb-haskell = 1563 | appendConfigureFlag super.leveldb-haskell [ 1564 | # Similar to https://github.com/nh2/static-haskell-nix/issues/10 1565 | "--ld-option=-Wl,--start-group --ld-option=-Wl,-lstdc++" 1566 | ]; 1567 | 1568 | zeromq4-patterns = 1569 | dontCheck # test suite hangs forever 1570 | (appendConfigureFlag super.zeromq4-patterns [ 1571 | # Similar to https://github.com/nh2/static-haskell-nix/issues/10 1572 | "--ld-option=-Wl,--start-group --ld-option=-Wl,-lstdc++" 1573 | ]); 1574 | 1575 | cryptonite = 1576 | if integer-simple 1577 | then disableCabalFlag super.cryptonite "integer-gmp" 1578 | else super.cryptonite; 1579 | 1580 | # The test-suite of this package loops forever on 100% CPU (at least on `-O0`). 1581 | bench-show = dontCheck super.bench-show; 1582 | # The test-suite of this package loops forever on 100% CPU (at least on `-O0`). 1583 | # TODO Investigate that because `loop` is nh2's own package. 1584 | loop = dontCheck super.loop; 1585 | # The test-suite of this package loops forever on 100% CPU (at least on `-O0`). 1586 | matrix = dontCheck super.matrix; 1587 | # The test-suite of this package loops forever on 100% CPU (at least on `-O0`). 1588 | # TODO Ask Bas about it 1589 | scientific = 1590 | if integer-simple 1591 | then dontCheck super.scientific 1592 | else super.scientific; 1593 | # The test-suite of this package loops forever on 100% CPU (at least on `-O0`). 1594 | x509-validation = 1595 | if integer-simple 1596 | then dontCheck super.x509-validation 1597 | else super.x509-validation; 1598 | 1599 | # Tests depend on util-linux which depends on systemd 1600 | hakyll = 1601 | dontCheck (overrideCabal super.hakyll (drv: { 1602 | testToolDepends = []; 1603 | })); 1604 | 1605 | # Inspection tests fail on `disableOptimization`with 1606 | # examples/Fusion.hs:25:1: sumUpSort `hasNoType` GHC.Types.[] failed expectedly. 1607 | inspection-testing = 1608 | (if disableOptimization then dontCheck else lib.id) 1609 | super.inspection-testing; 1610 | 1611 | # Inspection tests fail on `disableOptimization`with 1612 | # examples/Fusion.hs:25:1: sumUpSort `hasNoType` GHC.Types.[] failed expectedly 1613 | algebraic-graphs = 1614 | (if disableOptimization then dontCheck else lib.id) 1615 | super.algebraic-graphs; 1616 | 1617 | # Test suite tries to connect to the Internet 1618 | aur = dontCheck super.aur; 1619 | 1620 | # Test suite tries to run `minisat` which is not on PATH 1621 | ersatz = dontCheck super.ersatz; 1622 | 1623 | # Seems to time out on `-O0` (but does not print that timeout 1624 | # is the failure reason). 1625 | numeric-prelude = 1626 | (if disableOptimization then dontCheck else lib.id) 1627 | super.numeric-prelude; 1628 | 1629 | # Assertion failure with `-O0` because then assertions are actually checked: 1630 | # https://github.com/haskell/ghc-events/issues/106 1631 | ghc-events = 1632 | (if disableOptimization then dontCheck else lib.id) 1633 | super.ghc-events; 1634 | 1635 | # Test suite uses `timeout` to check performance, bad idea. 1636 | # https://github.com/frasertweedale/hs-jose/blob/c2f6690df5672dc1e089d1fa9ab7a4298f06c55d/test/Perf.hs#L38C16-L38C19 1637 | jose = 1638 | (if disableOptimization then dontCheck else lib.id) 1639 | super.jose; 1640 | 1641 | # doctests test suite fails with: 1642 | # /build/trifecta-2.1/src/Text/Trifecta/Util/It.hs:61: failure in expression `let keepIt a = Pure a' 1643 | # expected: 1644 | # but got: /nix/store/xz6sgnl68v00yhfk25cfankpdf7g57cs-binutils-2.31.1/bin/ld: warning: type and size of dynamic symbol `TextziTrifectaziDelta_zdfHasDeltaByteString_closure' are not defined 1645 | trifecta = dontCheck super.trifecta; 1646 | 1647 | # Test suite needs a missing dependency: 1648 | # Configuring range-set-list-0.1.3.1... 1649 | # ... 1650 | # Setup: Encountered missing or private dependencies: 1651 | # tasty >=0.8 && <1.4 1652 | range-set-list = 1653 | dontCheck (overrideCabal super.range-set-list { broken = false; }); 1654 | 1655 | # Test suite fails: 1656 | # 1657 | # doctests: FAIL 1658 | # Exception: : Dynamic loading not supported 1659 | # Parse a content-less file: FAIL 1660 | # Exception: test-files/trivial.proto: openFile: does not exist (No such file or directory) 1661 | # 2 out of 71 tests failed (1.97s) 1662 | proto3-suite = dontCheck super.proto3-suite; 1663 | 1664 | # Fix syntax error in test. 1665 | # Remove when nixpkgs has data-diverse >= 4.7.1.0, see: 1666 | # https://github.com/louispan/data-diverse/commit/50d79a011d2a9c55ca4b21a424f177d6bbd2663c 1667 | data-diverse = markUnbroken (dontCheck super.data-diverse); 1668 | }); 1669 | 1670 | }); 1671 | }; 1672 | 1673 | 1674 | pkgsWithHaskellLibsReadyForStaticLinking = pkgsWithGhc.extend haskellLibsReadyForStaticLinkingOverlay; 1675 | 1676 | # Overlay all Haskell executables are statically linked. 1677 | staticHaskellBinariesOverlay = final: previous: { 1678 | haskellPackages = previous.haskellPackages.override (old: { 1679 | overrides = final.lib.composeExtensions (old.overrides or (_: _: {})) (self: super: 1680 | let 1681 | # We have to use `useFixedCabal` here, and cannot just rely on the 1682 | # "Cabal = ..." we override up in `haskellPackagesWithLibsReadyForStaticLinking`, 1683 | # because that `Cabal` isn't used in all packages: 1684 | # If a package doesn't explicitly depend on the `Cabal` package, then 1685 | # for compiling its `Setup.hs` the Cabal package that comes with GHC 1686 | # (that is in the default GHC package DB) is used instead, which 1687 | # obviously doesn' thave our patches. 1688 | statify = drv: 1689 | with final.haskell.lib; 1690 | final.lib.foldl 1691 | appendConfigureFlag 1692 | (disableLibraryProfiling (disableSharedExecutables (useFixedCabal drv))) 1693 | (builtins.concatLists [ 1694 | [ 1695 | "--enable-executable-static" # requires `useFixedCabal` 1696 | # `enableShared` seems to be required to avoid `recompile with -fPIC` errors on some packages. 1697 | "--extra-lib-dirs=${final.ncurses.override { enableStatic = true; }}/lib" 1698 | ] 1699 | # TODO Figure out why this and the below libffi are necessary. 1700 | # `working` and `workingStackageExecutables` don't seem to need that, 1701 | # but `static-stack2nix-builder-example` does. 1702 | (final.lib.optionals (!integer-simple) [ 1703 | "--extra-lib-dirs=${final.gmp6.override { withStatic = true; }}/lib" 1704 | ]) 1705 | (final.lib.optionals (!integer-simple && approach == "pkgsMusl") [ 1706 | # GHC needs this if it itself wasn't already built against static libffi 1707 | # (which is the case in `pkgsStatic` only): 1708 | "--extra-lib-dirs=${final.libffi}/lib" 1709 | ]) 1710 | ]); 1711 | in 1712 | final.lib.mapAttrs 1713 | (name: value: 1714 | if (isProperHaskellPackage value && isExecutable value) then statify value else value 1715 | ) 1716 | super 1717 | ); 1718 | }); 1719 | }; 1720 | 1721 | 1722 | pkgsWithStaticHaskellBinaries = pkgsWithHaskellLibsReadyForStaticLinking.extend staticHaskellBinariesOverlay; 1723 | 1724 | 1725 | # Legacy names 1726 | haskellPackagesWithLibsReadyForStaticLinking = pkgsWithHaskellLibsReadyForStaticLinking.haskellPackages; 1727 | haskellPackages = pkgsWithStaticHaskellBinaries.haskellPackages; 1728 | 1729 | 1730 | 1731 | in 1732 | rec { 1733 | working = { 1734 | inherit (haskellPackages) 1735 | hello # Minimal dependencies 1736 | stack # Many dependencies 1737 | hlint 1738 | ShellCheck 1739 | cabal-install 1740 | bench 1741 | dhall 1742 | dhall-json 1743 | # postgrest # Dependency `configurator-pg-0.2.7` does not build due to `megaparsec >=7.0.0 && <9.3` (This is fixed in `configurator-pg- 0.2.8`) 1744 | # proto3-suite # Dependency `proto3-wire-1.4.0` does not build due to `bytestring >=0.10.6.0 && <0.11.0` 1745 | hsyslog # Small example of handling https://github.com/NixOS/nixpkgs/issues/43849 correctly 1746 | # aura # `aur` maked as broken in nixpkgs, but works here with `allowBroken = true;` actually 1747 | ; 1748 | } // (if approach == "pkgsStatic" then {} else { 1749 | # Packages that work with `pkgsMusl` but fail with `pkgsStatic`: 1750 | 1751 | inherit (haskellPackages) 1752 | # cachix fails on `pkgsStatic` with 1753 | # cycle detected in the references of '/nix/store/...-cachix-0.2.0-x86_64-unknown-linux-musl-bin' from '/nix/store/...-cachix-0.2.0-x86_64-unknown-linux-musl' 1754 | # because somehow the `Paths_cachix` module gets linked into the library, 1755 | # and it contains a reference to the `binDir`, which is a separate nix output. 1756 | # 1757 | # There's probably a lack of dead-code elimination with `pkgsStatic`, 1758 | # but even if that worked, this is odd because this should work even 1759 | # when you *use* the `binDir` thing in your executable. 1760 | # cachix # fails on latest nixpkgs master due to cachix -> nix -> pkgsStatic.busybox dependency, see https://github.com/nh2/static-haskell-nix/pull/61#issuecomment-544331652 1761 | # darcs fails on `pkgsStatic` because 1762 | darcs # Has native dependencies (`libcurl` and its dependencies) 1763 | # pandoc fails on `pkgsStatic` because Lua doesn't currently build there. 1764 | pandoc # Depends on Lua 1765 | # xmonad fails on `pkgsStatic` because `libXScrnSaver` fails to build there. 1766 | xmonad 1767 | ; 1768 | }); 1769 | 1770 | notWorking = { 1771 | inherit (haskellPackages) 1772 | tttool # see #14 # TODO reenable after fixing Package `HPDF-1.4.10` being marked as broken and failing to evaluate 1773 | ; 1774 | }; 1775 | 1776 | all = working // notWorking; 1777 | 1778 | 1779 | # Tries to build all executables on Stackage. 1780 | allStackageExecutables = 1781 | lib.filterAttrs (name: x: isStackageExecutable name) haskellPackages; 1782 | 1783 | workingStackageExecutables = 1784 | builtins.removeAttrs allStackageExecutables [ 1785 | # List of executables that don't work for reasons not yet investigated. 1786 | # When changing this file, we should always check if this list grows or shrinks. 1787 | "Agda" # fails on `emacs` not building 1788 | "align-audio" # like `audacity` 1789 | "Allure" # depends on `LambdaHack` also in this list 1790 | "audacity" # lots of linker errors, likely lack of pkg-config; needs investigation 1791 | "cuda" # needs `allowUnfree = true`; enabling it gives `unsupported platform for the pure Linux stdenv` 1792 | "diagrams-builder" # needs `glib` which is problematic 1793 | "diagrams-cairo" # `gtk2hs` bug building `glib`: https://github.com/nh2/static-haskell-nix/issues/4#issuecomment-1634681846 1794 | "gtk-sni-tray" # needs `gi-gtk` which requires `glib` which is problematic 1795 | "gtk3" # Haskell package `glib` fails with `Ambiguous module name ‘Gtk2HsSetup’: it was found in multiple packages: gtk2hs-buildtools-0.13.8.0 gtk2hs-buildtools-0.13.8.0` 1796 | "hackage-cli" # `cannot find -lbrotlienc` 1797 | "ihaskell" # linker error to `zmq` and various libraries 1798 | "LambdaHack" # fails `systemd` dependency erroring on `#include ` 1799 | "learn-physics" # needs opengl: `cannot find -lGLU` `-lGL` 1800 | "magico" # undefined reference to `_gfortran_concat_string' 1801 | "monomer" # needs `nanovg` which is in this list 1802 | "nanovg" # needs opengl: `cannot find -lGLU` `-lGL` 1803 | "odbc" # `cannot find -lodbc`, `cannot find -ldouble-conversion` 1804 | "pango" # `gtk2hs` bug building `glib`: https://github.com/nh2/static-haskell-nix/issues/4#issuecomment-1634681846 1805 | "qchas" # `_gfortran_concat_string` linker error via openblas 1806 | "rhine-gloss" # needs opengl: `cannot find -lGLU` `-lGL` 1807 | "sound-collage" # like `audacity` 1808 | "split-record" # like `audacity` 1809 | "termonad" # needs `glib` which is problematic 1810 | ]; 1811 | 1812 | inherit normalPkgs; 1813 | inherit approachPkgs; 1814 | # Export as `pkgs` our final overridden nixpkgs. 1815 | pkgs = pkgsWithStaticHaskellBinaries; 1816 | 1817 | inherit lib; 1818 | 1819 | inherit pkgsWithGhc; 1820 | inherit pkgsPropagatedBuildInputs; 1821 | inherit pkgsDontDisableStatic; 1822 | inherit pkgsWithArchiveFiles; 1823 | inherit pkgsWithStaticHaskellBinaries; 1824 | 1825 | inherit haskellPackagesWithFailingStackageTestsDisabled; 1826 | inherit haskellPackagesWithLibsReadyForStaticLinking; 1827 | inherit haskellPackages; 1828 | } 1829 | -------------------------------------------------------------------------------- /update-example-commits.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu -o pipefail 3 | 4 | # Convenience script for the task of updating the git commits in the 5 | # example files. 6 | 7 | # Update file 8 | COMMIT="$(git rev-parse HEAD)" 9 | perl -pi -e "s:/static-haskell-nix/archive/........................................:/static-haskell-nix/archive/${COMMIT}:g" static-stack2nix-builder-example/default.nix 10 | 11 | # Commit 12 | git reset 13 | git add static-stack2nix-builder-example/default.nix 14 | git commit -m 'Update example commits' 15 | --------------------------------------------------------------------------------