Skip to content

Setup Depends support #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion builder/comp-builder.nix
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ stdenv.mkDerivation ({

buildInputs = component.libs
++ component.frameworks
++ component.pkgconfig;
++ builtins.concatLists component.pkgconfig;

nativeBuildInputs =
[ghc buildPackages.removeReferencesTo]
Expand Down
27 changes: 7 additions & 20 deletions builder/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,13 @@ let

setup = if package.buildType == "Simple"
then defaultSetup
else stdenv.mkDerivation {
name = "${name}-setup";
nativeBuildInputs = [buildGHC];
inherit src;
phases = ["unpackPhase" "buildPhase" "installPhase"];
buildPhase = ''
for f in Setup.hs Setup.lhs; do
if [ -f $f ]; then
echo Compiling package $f
ghc $f --make -o ./Setup
setup=$(pwd)/Setup
fi
done
[ -f ./Setup ] || (echo Failed to build Setup && exit 1)
'';

installPhase = ''
mkdir -p $out/bin
install ./Setup $out/bin/Setup
'';
else haskellLib.weakCallPackage pkgs ./setup-builder.nix {
ghc = buildGHC;
setup-depends = package.setup-depends;
hsPkgs = hsPkgs.buildPackages;
inherit haskellLib nonReinstallablePkgs withPackage
package name src flags pkgconfig
;
};

comp-builder = haskellLib.weakCallPackage pkgs ./comp-builder.nix { inherit ghc haskellLib nonReinstallablePkgs withPackage hsPkgs; };
Expand Down
110 changes: 110 additions & 0 deletions builder/setup-builder.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{ stdenv, buildPackages, ghc, lib, pkgconfig, writeText, runCommand
, haskellLib, nonReinstallablePkgs, withPackage, hsPkgs, setup-depends
, package, name, src, flags }:

let
fullName = "${name}-setup";

flagsAndConfig = field: xs: lib.optionalString (xs != []) ''
echo ${lib.concatStringsSep " " (map (x: "--${field}=${x}") xs)} >> $out/configure-flags
echo "${field}: ${lib.concatStringsSep " " xs}" >> $out/cabal.config
'';

flatDepends =
let
makePairs = map (p: rec { key="${val}"; val=(p.components.library or p); });
closure = builtins.genericClosure {
startSet = makePairs setup-depends;
operator = {val,...}: makePairs val.config.depends;
};
in map ({val,...}: val) closure;

includeGhcPackage = lib.any (p: p.identifier.name == "ghc") setup-depends;

configFiles = runCommand "${fullName}-config" { nativeBuildInputs = [ghc]; } (''
mkdir -p $out

# Calls ghc-pkg for the target platform
target-pkg() {
${ghc.targetPrefix}ghc-pkg "$@"
}

target-pkg init $out/package.conf.d

# Copy over the nonReinstallablePkgs from the global package db.
# Note: we need to use --global-package-db with ghc-pkg to prevent it
# from looking into the implicit global package db when registering the package.
${lib.concatMapStringsSep "\n" (p: ''
target-pkg describe ${p} | target-pkg --force --global-package-db $out/package.conf.d register - || true
'') nonReinstallablePkgs}

${lib.concatMapStringsSep "\n" (p: ''
target-pkg --package-db ${p}/package.conf.d dump | target-pkg --force --package-db $out/package.conf.d register -
'') flatDepends}

# Note: we pass `clear` first to ensure that we never consult the implicit global package db.
${flagsAndConfig "package-db" ["clear" "$out/package.conf.d"]}

echo ${lib.concatStringsSep " " (lib.mapAttrsToList (fname: val: "--flags=${lib.optionalString (!val) "-" + fname}") flags)} >> $out/configure-flags

''
# This code originates in the `generic-builder.nix` from nixpkgs. However GHC has been fixed
# to drop unused libraries referneced from libraries; and this patch is usually included in the
# nixpkgs's GHC builds. This doesn't sadly make this stupid hack unnecessary. It resurfes in
# the form of Cabal trying to be smart. Cabal when linking a library figures out that you likely
# need those `rpath` entries, and passes `-optl-Wl,-rpath,...` for each dynamic library path to
# GHC, thus subverting the linker and forcing it to insert all those RPATHs weather or not they
# are needed. We therfore reuse the linker hack here to move all al dynamic lirbaries into a
# common folder (as links) and thus prevent Cabal from going nuts.
#
# TODO: Fix Cabal.
# TODO: this is only needed if we do dynamic libraries.
+ lib.optionalString stdenv.isDarwin ''
# Work around a limit in the macOS Sierra linker on the number of paths
# referenced by any one dynamic library:
#
# Create a local directory with symlinks of the *.dylib (macOS shared
# libraries) from all the dependencies.
local dynamicLinksDir="$out/lib/links"
mkdir -p $dynamicLinksDir
for d in $(grep dynamic-library-dirs "$out/package.conf.d/"*|awk '{print $2}'|sort -u); do
ln -s "$d/"*.dylib $dynamicLinksDir
done
# Edit the local package DB to reference the links directory.
for f in "$out/package.conf.d/"*.conf; do
sed -i "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," $f
done
'' + ''
target-pkg --package-db $out/package.conf.d recache
'' + ''
target-pkg --package-db $out/package.conf.d check
'');

in stdenv.lib.fix (drv:

stdenv.mkDerivation {
name = "${fullName}";
inherit src;
nativeBuildInputs = [ghc];

CABAL_CONFIG = configFiles + /cabal.config;
phases = ["unpackPhase" "buildPhase" "installPhase"];
buildPhase = ''
for f in Setup.hs Setup.lhs; do
if [ -f $f ]; then
echo Compiling package $f
ghc $f '' + (if includeGhcPackage then "-package ghc " else "")
+ ''-package-db ${configFiles}/package.conf.d --make -o ./Setup
setup=$(pwd)/Setup
fi
done
[ -f ./Setup ] || (echo Failed to build Setup && exit 1)
'';

installPhase = ''
mkdir -p $out/bin
install ./Setup $out/bin/Setup
'';
})


39 changes: 34 additions & 5 deletions default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
# It's also possible to override these sources with NIX_PATH.
, hackageSourceJSON ? ./hackage-src.json
, stackageSourceJSON ? ./stackage-src.json
, hackageIndexState ? null
, recentNixpkgs ? import <nixpkgs> {}
}:

let
Expand Down Expand Up @@ -38,12 +40,24 @@ let
# overridden with NIX_PATH.
fetchExternal = import ./lib/fetch-external.nix;

hackageIndex = import ./lib/hackageIndex.nix {
inherit (pkgs) runCommand;
inherit (recentNixpkgs) cabal-install;
indexState = hackageIndexState;
};

# All packages from Hackage as Nix expressions
hackage = import (fetchExternal {
name = "hackage-exprs-source";
specJSON = hackageSourceJSON;
override = "hackage";
});
hackage = if hackageIndexState == null
then import (fetchExternal {
name = "hackage-exprs-source";
specJSON = hackageSourceJSON;
override = "hackage";
})
else import (import ./lib/callHackageToNix.nix {
inherit (pkgs) runCommand;
inherit (import ./. {}) nix-tools;
inherit hackageIndex;
});

# The set of all Stackage snapshots
stackage = import (fetchExternal {
Expand Down Expand Up @@ -118,6 +132,21 @@ let
update-stackage = self.callPackage ./scripts/update-stackage.nix {};
update-pins = self.callPackage ./scripts/update-pins.nix {};
};

# Make this handy overridable fetch function available.
inherit fetchExternal;

# Takes a haskell src directory runs cabal new-configure and plan-to-nix.
# Resulting nix files are added to nix-plan subdirectory.
cabalProjectToNix = import ./lib/cabalProjectToNix.nix {
inherit pkgs hackageIndex;
inherit (pkgs) runCommand;
# TODO avoid hardcoding the GHC version
ghc = recentNixpkgs.haskell.compiler.ghc864;
inherit (recentNixpkgs) cabal-install;
inherit (recentNixpkgs.haskellPackages) hpack;
inherit (import ./. {}) nix-tools;
};
});

in
Expand Down
30 changes: 30 additions & 0 deletions lib/cabalProjectToNix.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{ pkgs, runCommand, nix-tools, cabal-install
, hackageIndex, ghc, hpack
} :
{src} :
let
cabalFiles =
pkgs.lib.cleanSourceWith {
inherit src;
filter = path: type:
type == "directory" ||
pkgs.lib.any (i: (pkgs.lib.hasSuffix i path)) [ ".project" ".cabal" "package.yaml" ];
};
plan = runCommand "plan" {
buildInputs = [ ghc hpack ];
} ''
tmp=$(mktemp -d)
cd $tmp
cp -r ${cabalFiles}/* .
chmod +w -R .
find . -name package.yaml -exec hpack "{}" \;
HOME=${hackageIndex} ${cabal-install}/bin/cabal new-configure
HOME=$out ${nix-tools}/bin/plan-to-nix --plan-json dist-newstyle/cache/plan.json -o nix-plan
cp -r nix-plan $out
'';
in
runCommand "plan-and-src" {} ''
mkdir $out
cp -r ${src}/* $out
ln -sf ${plan} $out/nix-plan
''
5 changes: 5 additions & 0 deletions lib/callHackageToNix.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{ runCommand, nix-tools
, hackageIndex
} : runCommand "hackage-nix" {} ''
HOME=${hackageIndex} ${nix-tools}/bin/hackage-to-nix $out
''
16 changes: 16 additions & 0 deletions lib/hackageIndex.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{ runCommand, cabal-install
, indexState ? "2019-04-24T21:34:04Z"
} :
let
# To avoid downloading more data than necessary this will provide a base.
cachedState = runCommand "hackage-${builtins.substring 0 4 indexState}" {} ''
mkdir -p $out
HOME=$out ${cabal-install}/bin/cabal new-update 'hackage.haskell.org,${builtins.substring 0 4 indexState}-01-01T00:00:00Z'
'';
in runCommand "hackage-${builtins.replaceStrings [":"] [""] indexState}" {} ''
mkdir -p $out
cp -r ${cachedState}/.cabal $out
chmod +w -R $out/.cabal
sed -i.back -e "s|${cachedState}|$out|g" $out/.cabal/config
HOME=$out ${cabal-install}/bin/cabal new-update 'hackage.haskell.org,${indexState}'
''
Loading