Skip to content

Commit

Permalink
[aura] Static builds with stack-haskell-nix
Browse files Browse the repository at this point in the history
Hi, Colin. I'm finally doing that "look at how magical NixOS is" thing
from 6 years ago.

The patches are to bodge static-haskell-nix into building with Stackage
lts-15.12, which means stack2nix on Stack 2.1 and also means
static-haskell-nix on GHC 8.8.3 (and Cabal 3.0.1.0).

I'm not sure how much of this is readily upstreamable.
  • Loading branch information
bb010g committed May 22, 2020
1 parent 845b857 commit 03cc037
Show file tree
Hide file tree
Showing 12 changed files with 39,535 additions and 8 deletions.
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Nix
/nix/sources.nix linguist-vendored
/nix/sources.json linguist-generated
/nix/*-stack2nix-output.nix linguist-generated
38,914 changes: 38,914 additions & 0 deletions nix/aura-stack2nix-output.nix

Large diffs are not rendered by default.

135 changes: 130 additions & 5 deletions nix/default.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,131 @@
{ ... } @ commonArgs:
let common = with common; {
pkgs = import <nixpkgs> { };
ghc = pkgs.ghc;
let
inherit (builtins) foldl';
fix = unfix: let self = unfix self; in self;
extends = overlay: unfix: self:
let
super = unfix self;
selfUnmerged = overlay self super;
in super // selfUnmerged;
extendList = foldl' (unfix: overlay: extends overlay unfix);

commonOverlay.base = common: commonSuper: with common; {

nixpkgs = <nixpkgs>;
nixpkgsArg = { };
pkgs = import nixpkgs nixpkgsArg;
sources = import ./sources.nix { };

# matching stack.yaml
haskellCompiler = "ghc883"; # lts-15.13 (ghc-8.8.3)
# pins e.g. extra-deps without hashes or revisions
hackageSnapshot = "2020-05-19T00:00:00Z";

haskellPackages = pkgs.haskell.packages.${haskellCompiler};
ghc = haskellPackages.ghc;

# stack build / stack run
stackProject = import ./stack.nix { inherit common; };
} // commonArgs; in common
# "$(nix-build --no-out-link ./nix -A staticProject.fullBuildScript)"
staticProject = import ./static.nix { inherit common; };

static-haskell-nix = pkgs.applyPatches {
name = "static-haskell-nix-src";
src = sources.static-haskell-nix.outPath;
patches = [
./static-haskell-nix-fixed_stack2nix-override.patch
./static-haskell-nix-newer-cabal-versions.patch
];
};
# lts-15+ requires Stack >= 2.0
# https://www.stackage.org/blog/2020/02/discontinuing-legacy-snapshots
# You can set `haskellPackages` if you like,
# but be careful with the compiler you move to (currently "ghc865").
stack2nix = import (pkgs.applyPatches {
name = "stack2nix-src";
src = sources.stack2nix;
patches = [
# Make GHC base libraries dependent on GHC version.
# https://github.com/input-output-hk/stack2nix/pull/172
./stack2nix-per-ghc-base-packages.patch
# (pkgs.fetchpatch {
# url = "https://github.com/input-output-hk/stack2nix/commit/c009e33af30c76b8fe94388382d816079fb5ac4e.patch";
# sha256 = "0x44lcmzywvzhymch0hhg4j1r3592v51xw552kawm2zd0cfp8qhz";
# })
];
postPatch = ''
cp ${sources.ghc-utils}/library-versions/pkg_versions.txt ./pkg_versions.txt
sed -i \
-e '1s/ }:/, haskellPackages ? null } @ args:/' \
-e 's/\(import .\/stack2nix.nix\) { inherit pkgs; }/\1 args/' \
./default.nix
sed ./pkg_versions.txt \
-e '/^\(#\|$\)/d; s/\(\S\+\)\/\S\+/"\1",/g; s/^\(\S\+\)\s*/ , ("\1", [/; s/,\?$/])/' | sed '1s/^ ,/ghcBaseLibsMap = M.fromListWith (++)\n [/; $s/$/\n ]/' \
>> ./src/Stack2nix/External/Stack.hs
'';
}) { inherit pkgs; };

};

commonOverlay.pin = common: commonSuper: with common; {

# If pinned, you'll probably want the static-haskell-nix binary subsituter.
# substituter: https://static-haskell-nix.cachix.org
# trusted-public-key: 1:Q17HawmAwaM1/BfIxaEDKAxwTOyRVhPG5Ji9K3+FvUU=
pinned = true;

nixpkgs = if pinned then sources.nixpkgs else commonSuper.nixpkgs;
nixpkgsArg = if pinned then {
config = { };
overlays = [ ];
} else commonSuper.nixpkgsArg;

};

# By patching the Nixpkgs source, patches won't be dropped due to
# reevaluations by a `pkgs.path` (not reusing the passed `pkgs` value).
# (This isn't as much of an issue for e.g. normal `<nixpkgs>` due to ambient
# mutable configuration, like `~/.config/nixpkgs/config.nix`.)
commonOverlay.patchNixpkgs = common: commonSuper: with common; {

nixpkgsUnpatched = commonSuper.nixpkgs;
pkgsUnpatched = import nixpkgsUnpatched nixpkgsArg;

nixpkgs = pkgsUnpatched.applyPatches {
name = "nixpkgs";
src = nixpkgsUnpatched;
patches = let
# configure.ac: make cross-compiler detection stricter
# (broken since GHC 8.2.1: Fix configure detection.)
# https://gitlab.haskell.org/ghc/ghc/commit/18f06878ed5d8cb0cf366a876f2bfea29647e5f0
# https://gitlab.haskell.org/ghc/ghc/merge_requests/2234
# https://gitlab.haskell.org/ghc/ghc/commit/4cbd5b47a00a29b7835710f1b91bb93ac8e3f790
nixpkgs-ghc-2234-cross-detect = [
./nixpkgs-ghc-2234-cross-detect.patch
];

# ghc: do not use ld.gold with musl libc #84670
# https://github.com/NixOS/nixpkgs/pull/84741
# GHC does not compile on pkgsMusl after the ld.gold change
# https://github.com/NixOS/nixpkgs/issues/84670
nixpkgs-84670-ghc-musl-no-ld-gold = [
(pkgsUnpatched.fetchpatch {
url = "https://github.com/NixOS/nixpkgs/commit/acfccf7f5ba83ce4ec279ce4416e5de5edca2fb7.patch";
sha256 = "0iv2mbw80qjr7d7gcpv0n7j7qdpn2vxky4zimw6qf9w409pbil2f";
})
];
in
# nixpkgs-ghc-2234-cross-detect ++
nixpkgs-84670-ghc-musl-no-ld-gold ++
[ ];
};

};

in { ... } @ commonArgs: fix (extendList (common: commonArgs) [
commonOverlay.base
commonOverlay.pin
commonOverlay.patchNixpkgs
(common: commonSuper: commonArgs)
])
24 changes: 24 additions & 0 deletions nix/nixpkgs-ghc-2234-cross-detect.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--- a/pkgs/development/compilers/ghc/8.8.3.nix
+++ b/pkgs/development/compilers/ghc/8.8.3.nix
@@ -2,7 +2,7 @@

# build-tools
, bootPkgs
-, autoconf, automake, coreutils, fetchurl, perl, python3, m4, sphinx
+, autoconf, automake, coreutils, fetchpatch, fetchurl, perl, python3, m4, sphinx
, bash

, libiconv ? null, ncurses
@@ -100,6 +100,11 @@

outputs = [ "out" "doc" ];

+ patches = [ (fetchpatch {
+ url = "https://gitlab.haskell.org/ghc/ghc/commit/4cbd5b47a00a29b7835710f1b91bb93ac8e3f790.patch";
+ sha256 = "09z0l2yy41b7wpp70f9xwfyxmar8ics48hm34892aqvrk715h8jk";
+ }) ];
+
postPatch = "patchShebangs .";

# GHC is a bit confused on its cross terminology.

57 changes: 57 additions & 0 deletions nix/sources.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

138 changes: 138 additions & 0 deletions nix/sources.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# This file has been generated by Niv.

let

#
# The fetchers. fetch_<type> fetches specs of type <type>.
#

fetch_file = pkgs: spec:
if spec.builtin or true then
builtins_fetchurl { inherit (spec) url sha256; }
else
pkgs.fetchurl { inherit (spec) url sha256; };

fetch_tarball = pkgs: name: spec:
let
ok = str: ! builtins.isNull (builtins.match "[a-zA-Z0-9+-._?=]" str);
# sanitize the name, though nix will still fail if name starts with period
name' = stringAsChars (x: if ! ok x then "-" else x) "${name}-src";
in
if spec.builtin or true then
builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
else
pkgs.fetchzip { name = name'; inherit (spec) url sha256; };

fetch_git = spec:
builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };

fetch_local = spec: spec.path;

fetch_builtin-tarball = name: throw
''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
$ niv modify ${name} -a type=tarball -a builtin=true'';

fetch_builtin-url = name: throw
''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
$ niv modify ${name} -a type=file -a builtin=true'';

#
# Various helpers
#

# The set of packages used when specs are fetched using non-builtins.
mkPkgs = sources:
let
sourcesNixpkgs =
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) {};
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
in
if builtins.hasAttr "nixpkgs" sources
then sourcesNixpkgs
else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
import <nixpkgs> {}
else
abort
''
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
add a package called "nixpkgs" to your sources.json.
'';

# The actual fetching function.
fetch = pkgs: name: spec:

if ! builtins.hasAttr "type" spec then
abort "ERROR: niv spec ${name} does not have a 'type' attribute"
else if spec.type == "file" then fetch_file pkgs spec
else if spec.type == "tarball" then fetch_tarball pkgs name spec
else if spec.type == "git" then fetch_git spec
else if spec.type == "local" then fetch_local spec
else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
else if spec.type == "builtin-url" then fetch_builtin-url name
else
abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";

# Ports of functions for older nix versions

# a Nix version of mapAttrs if the built-in doesn't exist
mapAttrs = builtins.mapAttrs or (
f: set: with builtins;
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
);

# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1);

# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));

# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
concatStrings = builtins.concatStringsSep "";

# fetchTarball version that is compatible between all the versions of Nix
builtins_fetchTarball = { url, name, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchTarball;
in
if lessThan nixVersion "1.12" then
fetchTarball { inherit name url; }
else
fetchTarball attrs;

# fetchurl version that is compatible between all the versions of Nix
builtins_fetchurl = { url, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchurl;
in
if lessThan nixVersion "1.12" then
fetchurl { inherit url; }
else
fetchurl attrs;

# Create the final "sources" from the config
mkSources = config:
mapAttrs (
name: spec:
if builtins.hasAttr "outPath" spec
then abort
"The values in sources.json should not have an 'outPath' attribute"
else
spec // { outPath = fetch config.pkgs name spec; }
) config.sources;

# The "config" used by the fetchers
mkConfig =
{ sourcesFile ? ./sources.json
, sources ? builtins.fromJSON (builtins.readFile sourcesFile)
, pkgs ? mkPkgs sources
}: rec {
# The sources, i.e. the attribute set of spec name to spec
inherit sources;

# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
inherit pkgs;
};
in
mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }
2 changes: 1 addition & 1 deletion nix/stack.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Stack is unhappy without a `ghc` attribute argument
{ common ? import ./. args, ghc ? null, ... } @ args:
{ ghc ? null, ... } @ args: let common = import ./. args; in

let
inherit (common) pkgs ghc;
Expand Down
Loading

0 comments on commit 03cc037

Please sign in to comment.