on iko/ghcjs-patch-master help sign in
Add config.hsPkgs.shellFor function

I needed to rearrange a few things to write this function.

commitbfa5b37bcb4036a0d640ae04b642f40277a0745a authorRodney Lorrimar <rodney.lorrimar@iohk.io> dateSun 12 May 2019, 21:47 (7 years ago) parent9df12ca
6 files changed+282−206
builder/comp-builder.nix+5−110 builder/default.nix+26−83 builder/hspkg-builder.nix+90 builder/make-config-files.nix+114 builder/shell-for.nix+40 modules/component-driver.nix+7−13
builder/comp-builder.nix+5−110
@@ -1,4 +1,4 @@
1−{ stdenv, buildPackages, ghc, lib, pkgconfig, writeText, runCommand, haskellLib, nonReinstallablePkgs, ghcForComponent, hsPkgs }:
1+{ stdenv, buildPackages, ghc, lib, pkgconfig, haskellLib, makeConfigFiles, ghcForComponent, hsPkgs }:
22
33{ componentId
44, component
@@ -39,115 +39,10 @@ let
3939 then "${name}-all"
4040 else "${name}-${componentId.ctype}-${componentId.cname}";
4141
42− flagsAndConfig = field: xs: lib.optionalString (xs != []) ''
43− echo ${lib.concatStringsSep " " (map (x: "--${field}=${x}") xs)} >> $out/configure-flags
44− echo "${field}: ${lib.concatStringsSep " " xs}" >> $out/cabal.config
45− '';
46−
47− flatDepends =
48− let
49− makePairs = map (p: rec { key="${val}"; val=(p.components.library or p); });
50− closure = builtins.genericClosure {
51− startSet = makePairs component.depends;
52− operator = {val,...}: makePairs val.config.depends;
53− };
54− in map ({val,...}: val) closure;
55−
56− exactDep = pdbArg: p: ''
57− if id=$(target-pkg ${pdbArg} field ${p} id --simple-output); then
58− echo "--dependency=${p}=$id" >> $out/configure-flags
59− fi
60− if ver=$(target-pkg ${pdbArg} field ${p} version --simple-output); then
61− echo "constraint: ${p} == $ver" >> $out/cabal.config
62− echo "constraint: ${p} installed" >> $out/cabal.config
63− fi
64− '';
65−
66− envDep = pdbArg: p: ''
67− if id=$(target-pkg ${pdbArg} field ${p} id --simple-output); then
68− echo "package-id $id" >> $out/ghc-environment
69− fi
70− '';
71−
72− configFiles = runCommand "${fullName}-config" { nativeBuildInputs = [ghc]; } (''
73− mkdir -p $out
74−
75− # Calls ghc-pkg for the target platform
76− target-pkg() {
77− ${ghc.targetPrefix}ghc-pkg "$@"
78− }
79−
80− target-pkg init $out/package.conf.d
81−
82− ${lib.concatStringsSep "\n" (lib.mapAttrsToList flagsAndConfig {
83− "extra-lib-dirs" = map (p: "${lib.getLib p}/lib") component.libs;
84− "extra-include-dirs" = map (p: "${lib.getDev p}/include") component.libs;
85− "extra-framework-dirs" = map (p: "${p}/Library/Frameworks") component.frameworks;
86− })}
87−
88− # Copy over the nonReinstallablePkgs from the global package db.
89− # Note: we need to use --global-package-db with ghc-pkg to prevent it
90− # from looking into the implicit global package db when registering the package.
91− ${lib.concatMapStringsSep "\n" (p: ''
92− target-pkg describe ${p} | target-pkg --force --global-package-db $out/package.conf.d register - || true
93− '') nonReinstallablePkgs}
94−
95− ${lib.concatMapStringsSep "\n" (p: ''
96− target-pkg --package-db ${p}/package.conf.d dump | target-pkg --force --package-db $out/package.conf.d register -
97− '') flatDepends}
98−
99− # Note: we pass `clear` first to ensure that we never consult the implicit global package db.
100− ${flagsAndConfig "package-db" ["clear" "$out/package.conf.d"]}
101−
102− echo ${lib.concatStringsSep " " (lib.mapAttrsToList (fname: val: "--flags=${lib.optionalString (!val) "-" + fname}") flags)} >> $out/configure-flags
103−
104− # Provide a GHC environment file
105− cat > $out/ghc-environment <<EOF
106− package-db $out/package.conf.d
107− EOF
108− ${lib.concatMapStringsSep "\n" (p: envDep "--package-db ${p.components.library or p}/package.conf.d" p.identifier.name) component.depends}
109− ${lib.concatMapStringsSep "\n" (envDep "") (lib.remove "ghc" nonReinstallablePkgs)}
110−
111− '' + lib.optionalString component.doExactConfig ''
112− echo "--exact-configuration" >> $out/configure-flags
113− echo "allow-newer: ${package.identifier.name}:*" >> $out/cabal.config
114− echo "allow-older: ${package.identifier.name}:*" >> $out/cabal.config
115−
116− ${lib.concatMapStringsSep "\n" (p: exactDep "--package-db ${p.components.library}/package.conf.d" p.identifier.name) component.depends}
117− ${lib.concatMapStringsSep "\n" (exactDep "") nonReinstallablePkgs}
118−
119− ''
120− # This code originates in the `generic-builder.nix` from nixpkgs. However GHC has been fixed
121− # to drop unused libraries referneced from libraries; and this patch is usually included in the
122− # nixpkgs's GHC builds. This doesn't sadly make this stupid hack unnecessary. It resurfes in
123− # the form of Cabal trying to be smart. Cabal when linking a library figures out that you likely
124− # need those `rpath` entries, and passes `-optl-Wl,-rpath,...` for each dynamic library path to
125− # GHC, thus subverting the linker and forcing it to insert all those RPATHs weather or not they
126− # are needed. We therfore reuse the linker hack here to move all al dynamic lirbaries into a
127− # common folder (as links) and thus prevent Cabal from going nuts.
128− #
129− # TODO: Fix Cabal.
130− # TODO: this is only needed if we do dynamic libraries.
131− + lib.optionalString stdenv.isDarwin ''
132− # Work around a limit in the macOS Sierra linker on the number of paths
133− # referenced by any one dynamic library:
134− #
135− # Create a local directory with symlinks of the *.dylib (macOS shared
136− # libraries) from all the dependencies.
137− local dynamicLinksDir="$out/lib/links"
138− mkdir -p $dynamicLinksDir
139− for d in $(grep dynamic-library-dirs "$out/package.conf.d/"*|awk '{print $2}'|sort -u); do
140− ln -s "$d/"*.dylib $dynamicLinksDir
141− done
142− # Edit the local package DB to reference the links directory.
143− for f in "$out/package.conf.d/"*.conf; do
144− sed -i "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," $f
145− done
146− '' + ''
147− target-pkg --package-db $out/package.conf.d recache
148− '' + ''
149− target-pkg --package-db $out/package.conf.d check
150− '');
42+ configFiles = makeConfigFiles {
43+ inherit (package) identifier;
44+ inherit component fullName flags;
45+ };
15146
15247 finalConfigureFlags = lib.concatStringsSep " " (
15348 [ "--prefix=$out"
builder/default.nix+26−83
@@ -1,91 +1,34 @@
1−{ pkgs, buildPackages, stdenv, lib, haskellLib, ghc, buildGHC, fetchurl, writeText, runCommand, pkgconfig, nonReinstallablePkgs, ghcForComponent, hsPkgs }:
2−
3−{ flags
4−, package
5−, components
6−, cabal-generator
7−
8−, name
9−, sha256
10−, src
11−, revision
12−, revisionSha256
13−, patches
14−
15−, preUnpack
16−, postUnpack
17−, preConfigure
18−, postConfigure
19−, preBuild
20−, postBuild
21−, preCheck
22−, postCheck
23−, preInstall
24−, postInstall
25−, preHaddock
26−, postHaddock
27−
28−, shellHook
29−
30−, ...
31−}@config:
1+{ pkgs, buildPackages, stdenv, lib, haskellLib, ghc, buildGHC, fetchurl, pkgconfig, nonReinstallablePkgs, hsPkgs }:
322
333let
34− cabalFile = if revision == null || revision == 0 then null else
35− fetchurl {
36− name = "${name}-${toString revision}.cabal";
37− url = "https://hackage.haskell.org/package/${name}/revision/${toString revision}.cabal";
38− sha256 = revisionSha256;
39− };
40−
41− defaultSetupSrc = builtins.toFile "Setup.hs" ''
42− import Distribution.Simple
43− main = defaultMain
44− '';
45− defaultSetup = buildPackages.runCommand "default-Setup" { nativeBuildInputs = [buildGHC]; } ''
46− cat ${defaultSetupSrc} > Setup.hs
47− mkdir -p $out/bin
48− ${buildGHC.targetPrefix}ghc Setup.hs --make -o $out/bin/Setup
49− '';
50−
51− setup = if package.buildType == "Simple"
52− then defaultSetup
53− else stdenv.mkDerivation {
54− name = "${name}-setup";
55− nativeBuildInputs = [buildGHC];
56− inherit src;
57− phases = ["unpackPhase" "buildPhase" "installPhase"];
58− buildPhase = ''
59− for f in Setup.hs Setup.lhs; do
60− if [ -f $f ]; then
61− echo Compiling package $f
62− ghc $f --make -o ./Setup
63− setup=$(pwd)/Setup
64− fi
65− done
66− [ -f ./Setup ] || (echo Failed to build Setup && exit 1)
67− '';
68−
69− installPhase = ''
70− mkdir -p $out/bin
71− install ./Setup $out/bin/Setup
72− '';
73− };
4+ # Builds a single component of a package.
5+ comp-builder = haskellLib.weakCallPackage pkgs ./comp-builder.nix {
6+ inherit ghc haskellLib makeConfigFiles ghcForComponent hsPkgs;
7+ };
748
75− comp-builder = haskellLib.weakCallPackage pkgs ./comp-builder.nix { inherit ghc haskellLib nonReinstallablePkgs ghcForComponent hsPkgs; };
9+ # Wraps GHC to provide dependencies in a way that works for both the
10+ # component builder and for nix-shells.
11+ ghcForComponent = import ./ghc-for-component-wrapper.nix {
12+ inherit lib ghc;
13+ inherit (buildPackages) stdenv runCommand makeWrapper;
14+ inherit (buildPackages.xorg) lndir;
15+ };
7616
77− buildComp = componentId: component: comp-builder {
78− inherit componentId component package name src flags setup cabalFile cabal-generator patches revision
79− preUnpack postUnpack preConfigure postConfigure
80− preBuild postBuild preCheck postCheck
81− preInstall postInstall preHaddock postHaddock
82− shellHook
83− ;
17+ # Builds a derivation which contains a ghc package-db of
18+ # dependencies for a component.
19+ makeConfigFiles = haskellLib.weakCallPackage pkgs ./make-config-files.nix {
20+ inherit ghc haskellLib nonReinstallablePkgs;
8421 };
8522
8623in {
87− components = haskellLib.applyComponents buildComp config;
88− inherit (package) identifier;
89− inherit setup cabalFile;
90− isHaskell = true;
24+ # Build a Haskell package from its config.
25+ # TODO: this pkgs is the adjusted pkgs, but pkgs.pkgs is unadjusted
26+ build-package = haskellLib.weakCallPackage pkgs ./hspkg-builder.nix {
27+ inherit haskellLib ghc buildGHC comp-builder;
28+ };
29+
30+ # Same as haskellPackages.shellFor in nixpkgs.
31+ shellFor = haskellLib.weakCallPackage pkgs ./shell-for.nix {
32+ inherit hsPkgs ghcForComponent makeConfigFiles;
33+ };
9134}
builder/hspkg-builder.nixnew file+90
@@ -0,0 +1,90 @@
1+{ pkgs, buildPackages, stdenv, lib, haskellLib, ghc, buildGHC, fetchurl, runCommand, pkgconfig, comp-builder }:
2+
3+
4+{ flags
5+, package
6+, components
7+, cabal-generator
8+
9+, name
10+, sha256
11+, src
12+, revision
13+, revisionSha256
14+, patches
15+
16+, preUnpack
17+, postUnpack
18+, preConfigure
19+, postConfigure
20+, preBuild
21+, postBuild
22+, preCheck
23+, postCheck
24+, preInstall
25+, postInstall
26+, preHaddock
27+, postHaddock
28+
29+, shellHook
30+
31+, ...
32+}@config:
33+
34+let
35+ cabalFile = if revision == null || revision == 0 then null else
36+ fetchurl {
37+ name = "${name}-${toString revision}.cabal";
38+ url = "https://hackage.haskell.org/package/${name}/revision/${toString revision}.cabal";
39+ sha256 = revisionSha256;
40+ };
41+
42+ defaultSetupSrc = builtins.toFile "Setup.hs" ''
43+ import Distribution.Simple
44+ main = defaultMain
45+ '';
46+ defaultSetup = buildPackages.runCommand "default-Setup" { nativeBuildInputs = [buildGHC]; } ''
47+ cat ${defaultSetupSrc} > Setup.hs
48+ mkdir -p $out/bin
49+ ${buildGHC.targetPrefix}ghc Setup.hs --make -o $out/bin/Setup
50+ '';
51+
52+ setup = if package.buildType == "Simple"
53+ then defaultSetup
54+ else stdenv.mkDerivation {
55+ name = "${name}-setup";
56+ nativeBuildInputs = [buildGHC];
57+ inherit src;
58+ phases = ["unpackPhase" "buildPhase" "installPhase"];
59+ buildPhase = ''
60+ for f in Setup.hs Setup.lhs; do
61+ if [ -f $f ]; then
62+ echo Compiling package $f
63+ ghc $f --make -o ./Setup
64+ setup=$(pwd)/Setup
65+ fi
66+ done
67+ [ -f ./Setup ] || (echo Failed to build Setup && exit 1)
68+ '';
69+
70+ installPhase = ''
71+ mkdir -p $out/bin
72+ install ./Setup $out/bin/Setup
73+ '';
74+ };
75+
76+ buildComp = componentId: component: comp-builder {
77+ inherit componentId component package name src flags setup cabalFile cabal-generator patches revision
78+ preUnpack postUnpack preConfigure postConfigure
79+ preBuild postBuild preCheck postCheck
80+ preInstall postInstall preHaddock postHaddock
81+ shellHook
82+ ;
83+ };
84+
85+in {
86+ components = haskellLib.applyComponents buildComp config;
87+ inherit (package) identifier;
88+ inherit setup cabalFile;
89+ isHaskell = true;
90+}
builder/make-config-files.nixnew file+114
@@ -0,0 +1,114 @@
1+{ stdenv, lib, haskellLib, ghc, nonReinstallablePkgs, runCommand }:
2+
3+let
4+ flagsAndConfig = field: xs: lib.optionalString (xs != []) ''
5+ echo ${lib.concatStringsSep " " (map (x: "--${field}=${x}") xs)} >> $out/configure-flags
6+ echo "${field}: ${lib.concatStringsSep " " xs}" >> $out/cabal.config
7+ '';
8+
9+ flatDepends = component:
10+ let
11+ makePairs = map (p: rec { key="${val}"; val=(p.components.library or p); });
12+ closure = builtins.genericClosure {
13+ startSet = makePairs component.depends;
14+ operator = {val,...}: makePairs val.config.depends;
15+ };
16+ in map ({val,...}: val) closure;
17+
18+ exactDep = pdbArg: p: ''
19+ if id=$(target-pkg ${pdbArg} field ${p} id --simple-output); then
20+ echo "--dependency=${p}=$id" >> $out/configure-flags
21+ fi
22+ if ver=$(target-pkg ${pdbArg} field ${p} version --simple-output); then
23+ echo "constraint: ${p} == $ver" >> $out/cabal.config
24+ echo "constraint: ${p} installed" >> $out/cabal.config
25+ fi
26+ '';
27+
28+ envDep = pdbArg: p: ''
29+ if id=$(target-pkg ${pdbArg} field ${p} id --simple-output); then
30+ echo "package-id $id" >> $out/ghc-environment
31+ fi
32+ '';
33+
34+in { identifier, component, fullName, flags ? {} }:
35+
36+ runCommand "${fullName}-config" { nativeBuildInputs = [ghc]; } (''
37+ mkdir -p $out
38+
39+ # Calls ghc-pkg for the target platform
40+ target-pkg() {
41+ ${ghc.targetPrefix}ghc-pkg "$@"
42+ }
43+
44+ target-pkg init $out/package.conf.d
45+
46+ ${lib.concatStringsSep "\n" (lib.mapAttrsToList flagsAndConfig {
47+ "extra-lib-dirs" = map (p: "${lib.getLib p}/lib") component.libs;
48+ "extra-include-dirs" = map (p: "${lib.getDev p}/include") component.libs;
49+ "extra-framework-dirs" = map (p: "${p}/Library/Frameworks") component.frameworks;
50+ })}
51+
52+ # Copy over the nonReinstallablePkgs from the global package db.
53+ # Note: we need to use --global-package-db with ghc-pkg to prevent it
54+ # from looking into the implicit global package db when registering the package.
55+ ${lib.concatMapStringsSep "\n" (p: ''
56+ target-pkg describe ${p} | target-pkg --force --global-package-db $out/package.conf.d register - || true
57+ '') nonReinstallablePkgs}
58+
59+ ${lib.concatMapStringsSep "\n" (p: ''
60+ target-pkg --package-db ${p}/package.conf.d dump | target-pkg --force --package-db $out/package.conf.d register -
61+ '') (flatDepends component)}
62+
63+ # Note: we pass `clear` first to ensure that we never consult the implicit global package db.
64+ ${flagsAndConfig "package-db" ["clear" "$out/package.conf.d"]}
65+
66+ echo ${lib.concatStringsSep " " (lib.mapAttrsToList (fname: val: "--flags=${lib.optionalString (!val) "-" + fname}") flags)} >> $out/configure-flags
67+
68+ # Provide a GHC environment file
69+ cat > $out/ghc-environment <<EOF
70+ package-db $out/package.conf.d
71+ EOF
72+ ${lib.concatMapStringsSep "\n" (p: envDep "--package-db ${p.components.library or p}/package.conf.d" p.identifier.name) component.depends}
73+ ${lib.concatMapStringsSep "\n" (envDep "") (lib.remove "ghc" nonReinstallablePkgs)}
74+
75+ '' + lib.optionalString component.doExactConfig ''
76+ echo "--exact-configuration" >> $out/configure-flags
77+ echo "allow-newer: ${identifier.name}:*" >> $out/cabal.config
78+ echo "allow-older: ${identifier.name}:*" >> $out/cabal.config
79+
80+ ${lib.concatMapStringsSep "\n" (p: exactDep "--package-db ${p.components.library}/package.conf.d" p.identifier.name) component.depends}
81+ ${lib.concatMapStringsSep "\n" (exactDep "") nonReinstallablePkgs}
82+
83+ ''
84+ # This code originates in the `generic-builder.nix` from nixpkgs. However GHC has been fixed
85+ # to drop unused libraries referneced from libraries; and this patch is usually included in the
86+ # nixpkgs's GHC builds. This doesn't sadly make this stupid hack unnecessary. It resurfes in
87+ # the form of Cabal trying to be smart. Cabal when linking a library figures out that you likely
88+ # need those `rpath` entries, and passes `-optl-Wl,-rpath,...` for each dynamic library path to
89+ # GHC, thus subverting the linker and forcing it to insert all those RPATHs weather or not they
90+ # are needed. We therfore reuse the linker hack here to move all al dynamic lirbaries into a
91+ # common folder (as links) and thus prevent Cabal from going nuts.
92+ #
93+ # TODO: Fix Cabal.
94+ # TODO: this is only needed if we do dynamic libraries.
95+ + lib.optionalString stdenv.isDarwin ''
96+ # Work around a limit in the macOS Sierra linker on the number of paths
97+ # referenced by any one dynamic library:
98+ #
99+ # Create a local directory with symlinks of the *.dylib (macOS shared
100+ # libraries) from all the dependencies.
101+ local dynamicLinksDir="$out/lib/links"
102+ mkdir -p $dynamicLinksDir
103+ for d in $(grep dynamic-library-dirs "$out/package.conf.d/"*|awk '{print $2}'|sort -u); do
104+ ln -s "$d/"*.dylib $dynamicLinksDir
105+ done
106+ # Edit the local package DB to reference the links directory.
107+ for f in "$out/package.conf.d/"*.conf; do
108+ sed -i "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," $f
109+ done
110+ '' + ''
111+ target-pkg --package-db $out/package.conf.d recache
112+ '' + ''
113+ target-pkg --package-db $out/package.conf.d check
114+ '')
builder/shell-for.nixnew file+40
@@ -0,0 +1,40 @@
1+{ buildPackages, lib, stdenv, ghcForComponent, makeConfigFiles, hsPkgs }:
2+
3+{ packages, withHoogle ? true, ... } @ args:
4+
5+let
6+ selected = packages hsPkgs;
7+ name = if lib.length selected == 1
8+ then "ghc-shell-for-${(lib.head selected).name}"
9+ else "ghc-shell-for-packages";
10+ ghcEnv = ghcForComponent {
11+ componentName = name;
12+ configFiles = makeConfigFiles {
13+ fullName = args.name or name;
14+ identifier.name = name;
15+ component = {
16+ depends = selected;
17+ libs = [];
18+ frameworks = [];
19+ doExactConfig = false;
20+ };
21+ };
22+ };
23+ mkDrvArgs = builtins.removeAttrs args ["packages" "withHoogle"];
24+
25+ # fixme: check if systemInputs and nativeBuildInputs are necessary
26+ systemInputs = [];
27+ nativeBuildInputs = [];
28+in
29+ stdenv.mkDerivation (mkDrvArgs // {
30+ name = mkDrvArgs.name or name;
31+
32+ buildInputs = systemInputs ++ mkDrvArgs.buildInputs or [];
33+ nativeBuildInputs = [ ghcEnv ] ++ nativeBuildInputs ++ mkDrvArgs.nativeBuildInputs or [];
34+ phases = ["installPhase"];
35+ installPhase = "echo $nativeBuildInputs $buildInputs > $out";
36+ LANG = "en_US.UTF-8";
37+ LOCALE_ARCHIVE = lib.optionalString (stdenv.hostPlatform.libc == "glibc") "${buildPackages.glibcLocales}/lib/locale/locale-archive";
38+
39+ passthru.ghc = ghcEnv;
40+ })
modules/component-driver.nix+7−13
@@ -1,19 +1,10 @@
11{ config, pkgs, lib, haskellLib, buildModules, ... }:
22let
3− # TODO: this pkgs is the adjusted pkgs, but pkgs.pkgs is unadjusted
4− new-builder = haskellLib.weakCallPackage pkgs ../builder {
3+ builder = haskellLib.weakCallPackage pkgs ../builder {
54 inherit haskellLib;
65 ghc = config.ghc.package;
76 buildGHC = buildModules.config.ghc.package;
87 inherit (config) nonReinstallablePkgs hsPkgs;
9− inherit ghcForComponent;
10− };
11−
12− ghcForComponent = import ../builder/ghc-for-component-wrapper.nix {
13− inherit lib;
14− inherit (pkgs.buildPackages) stdenv runCommand makeWrapper;
15− inherit (pkgs.buildPackages.xorg) lndir;
16− ghc = config.ghc.package;
178 };
189
1910in
@@ -49,8 +40,11 @@ in
4940 type = lib.types.unspecified;
5041 };
5142
52− config.hsPkgs = { buildPackages = buildModules.config.hsPkgs; }
53− // lib.mapAttrs
54− (name: pkg: if pkg == null then null else new-builder pkg)
43+ config.hsPkgs =
44+ { inherit (builder) shellFor;
45+ buildPackages = buildModules.config.hsPkgs;
46+ } //
47+ lib.mapAttrs
48+ (name: pkg: if pkg == null then null else builder.build-package pkg)
5549 (config.packages // lib.genAttrs config.nonReinstallablePkgs (_: null));
5650}