Skip to content

Commit

Permalink
WIP builder: Add a shell env for development
Browse files Browse the repository at this point in the history
  • Loading branch information
rvl committed Jan 10, 2019
1 parent ed1dbc0 commit e51136b
Show file tree
Hide file tree
Showing 14 changed files with 455 additions and 6 deletions.
13 changes: 10 additions & 3 deletions builder/comp-builder.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
, preBuild ? null, postBuild ? null
, preCheck ? null, postCheck ? null
, preInstall ? null, postInstall ? null
, shellHook ? null

, doCheck ? component.doCheck || componentId.ctype == "test"
, doCrossCheck ? component.doCrossCheck || false
Expand Down Expand Up @@ -144,6 +145,10 @@ let
++ component.configureFlags
);

executableToolDepends = lib.concatMap (c: if c.isHaskell or false
then builtins.attrValues (c.components.exes or {})
else [c]) component.build-tools;

in stdenv.mkDerivation ({
name = fullName;

Expand All @@ -153,6 +158,8 @@ in stdenv.mkDerivation ({
inherit (package) identifier;
config = component;
inherit configFiles;

isHaskellLibrary = haskellLib.isLibrary componentId;
};

meta = {
Expand All @@ -174,9 +181,7 @@ in stdenv.mkDerivation ({
nativeBuildInputs =
[ghc]
++ lib.optional (component.pkgconfig != []) pkgconfig
++ lib.concatMap (c: if c.isHaskell or false
then builtins.attrValues (c.components.exes or {})
else [c]) component.build-tools;
++ executableToolDepends;

SETUP_HS = setup + /bin/Setup;

Expand Down Expand Up @@ -228,6 +233,7 @@ in stdenv.mkDerivation ({
''}
runHook postInstall
'';

}
// lib.optionalAttrs (patches != []) { inherit patches; }
// lib.optionalAttrs (preUnpack != "") { inherit preUnpack; }
Expand All @@ -240,5 +246,6 @@ in stdenv.mkDerivation ({
// lib.optionalAttrs (postCheck != "") { inherit postCheck; }
// lib.optionalAttrs (preInstall != "") { inherit preInstall; }
// lib.optionalAttrs (postInstall != "") { inherit postInstall; }
// lib.optionalAttrs (shellHook != "") { inherit shellHook; }
// lib.optionalAttrs (stdenv.buildPlatform.libc == "glibc"){ LOCALE_ARCHIVE = "${buildPackages.glibcLocales}/lib/locale/locale-archive"; }
)
32 changes: 29 additions & 3 deletions builder/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ pkgs, buildPackages, stdenv, lib, haskellLib, ghc, buildGHC, fetchurl, writeText, runCommand, pkgconfig, nonReinstallablePkgs }:
{ pkgs, buildPackages, stdenv, lib, haskellLib, ghc, buildGHC, fetchurl, writeText, runCommand, pkgconfig, nonReinstallablePkgs, shellEnv, withPackage }:

{ flags
, package
Expand All @@ -22,6 +22,8 @@
, preInstall
, postInstall

, shellHook

, ...
}@config:

Expand Down Expand Up @@ -72,12 +74,36 @@ let
buildComp = componentId: component: comp-builder {
inherit componentId component package name src flags setup cabalFile patches
preUnpack postUnpack preConfigure postConfigure preBuild postBuild preCheck postCheck preInstall postInstall
shellHook
;
};

components' = haskellLib.applyComponents buildComp config;

devEnv = withPackage (comp-builder {
name = "${name}-devEnv-shell";
inherit setup cabalFile flags src;
componentId = { ctype = "lib"; cname = "devEnv"; }; # fixme: dummy values
component = {
# fixme: needs to be depends of all components
depends = lib.filter (p: p != components'.library)
(config.components.library.depends
++ (lib.concatMap (p: p.depends) (lib.attrValues config.components.exes))
++ (lib.concatMap (p: p.depends) (lib.attrValues config.components.tests)));
frameworks = [];
libs = [];
doExactConfig = false;
};
package = { identifier = { name = "devEnv-shell"; version = "0.0"; }; };
});

in {
components = haskellLib.applyComponents buildComp config;
components = components';
inherit (package) identifier;
inherit setup cabalFile;
inherit setup cabalFile devEnv;
isHaskell = true;
shell = shellEnv {
inherit shellHook;
ghcEnv = devEnv;
};
}
15 changes: 15 additions & 0 deletions modules/component-driver.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,22 @@ let
ghc = config.ghc.package;
buildGHC = buildModules.config.ghc.package;
inherit (config) nonReinstallablePkgs;
inherit shellEnv withPackage;
};

withPackage = package: import ./with-package-wrapper.nix {
inherit lib package;
inherit (pkgs) stdenv runCommand makeWrapper;
inherit (pkgs.xorg) lndir;
ghc = config.ghc.package;
llvmPackages = null;
};

shellEnv = import ./shell-env.nix {
inherit haskellLib;
inherit (pkgs) lib stdenv;
};

in

{
Expand Down
4 changes: 4 additions & 0 deletions modules/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ in {
type = nullOr string;
default = null;
};
shellHook = mkOption {
type = nullOr string;
default = null;
};
doCheck = mkOption {
type = bool;
default = false;
Expand Down
29 changes: 29 additions & 0 deletions modules/shell-env.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{ lib, stdenv, haskellLib }:

{ ghcEnv, shellHook }:
let
inherit (ghcEnv) baseGhc package;
ghcCommand' = if baseGhc.isGhcjs or false then "ghcjs" else "ghc";
ghcCommand = "${baseGhc.targetPrefix}${ghcCommand'}";
ghcCommandCaps = lib.toUpper ghcCommand';
in stdenv.mkDerivation {
name = "shell-for-${package.identifier.name}";

CABAL_CONFIG = package.configFiles + "/cabal.config";

"NIX_${ghcCommandCaps}" = "${ghcEnv}/bin/${ghcCommand}";
"NIX_${ghcCommandCaps}PKG" = "${ghcEnv}/bin/${ghcCommand}-pkg";
"NIX_${ghcCommandCaps}_LIBDIR" = if baseGhc.isHaLVM or false
then "${ghcEnv}/lib/HaLVM-${baseGhc.version}"
else "${ghcEnv}/lib/${ghcCommand}-${baseGhc.version}";
# fixme: docs, haddock, hoogle
# NIX_${ghcCommandCaps}_DOCDIR" = package.configFiles;

nativeBuildInputs = [ ghcEnv ];
phases = ["installPhase"];
installPhase = "echo $nativeBuildInputs $buildInputs > $out";

passthru = {
ghc = ghcEnv;
};
}
149 changes: 149 additions & 0 deletions modules/with-package-wrapper.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Copied from nixpkgs and adapted to work with the package database of
# a single component-builder package.
{ lib, stdenv, ghc, llvmPackages, package, runCommand, lndir, makeWrapper
, withLLVM ? false
, postBuild ? ""
, ghcLibdir ? null # only used by ghcjs, when resolving plugins
}:

assert ghcLibdir != null -> (ghc.isGhcjs or false);

# This wrapper works only with GHC 6.12 or later.
assert lib.versionOlder "6.12" ghc.version || ghc.isGhcjs || ghc.isHaLVM;

# It's probably a good idea to include the library "ghc-paths" in the
# compiler environment, because we have a specially patched version of
# that package in Nix that honors these environment variables
#
# NIX_GHC
# NIX_GHCPKG
# NIX_GHC_DOCDIR
# NIX_GHC_LIBDIR
#
# instead of hard-coding the paths. The wrapper sets these variables
# appropriately to configure ghc-paths to point back to the wrapper
# instead of to the pristine GHC package, which doesn't know any of the
# additional libraries.
#
# A good way to import the environment set by the wrapper below into
# your shell is to add the following snippet to your ~/.bashrc:
#
# if [ -e ~/.nix-profile/bin/ghc ]; then
# eval $(grep export ~/.nix-profile/bin/ghc)
# fi

let
isGhcjs = ghc.isGhcjs or false;
isHaLVM = ghc.isHaLVM or false;
ghc761OrLater = isGhcjs || isHaLVM || lib.versionOlder "7.6.1" ghc.version;
packageDBFlag = if ghc761OrLater then "--global-package-db" else "--global-conf";
ghcCommand' = if isGhcjs then "ghcjs" else "ghc";
ghcCommand = "${ghc.targetPrefix}${ghcCommand'}";
ghcCommandCaps= lib.toUpper ghcCommand';
libDir = if isHaLVM then "$out/lib/HaLVM-${ghc.version}"
else "$out/lib/${ghcCommand}-${ghc.version}";
docDir = "$out/share/doc/ghc/html";
packageCfgDir = "${libDir}/package.conf.d";
# CLang is needed on Darwin for -fllvm to work:
# https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/codegens.html#llvm-code-generator-fllvm
llvm = lib.makeBinPath
([ llvmPackages.llvm ]
++ lib.optional stdenv.targetPlatform.isDarwin llvmPackages.clang);

in runCommand "${ghc.name}-with-${package.identifier.name}" {
preferLocalBuild = true;
passthru = {
inherit (ghc) version meta;
baseGhc = ghc;
inherit package;
};
} (
''
. ${makeWrapper}/nix-support/setup-hook
# Start with a ghc...
mkdir -p $out
${lndir}/bin/lndir -silent ${ghc} $out
# ...and replace package database with the one from target package config.
rm -rf ${packageCfgDir}
cp -R --no-preserve=mode ${package.configFiles}/package.conf.d ${packageCfgDir}
# wrap compiler executables with correct env variables
for prg in ${ghcCommand} ${ghcCommand}i ${ghcCommand}-${ghc.version} ${ghcCommand}i-${ghc.version}; do
if [[ -x "${ghc}/bin/$prg" ]]; then
rm -f $out/bin/$prg
makeWrapper ${ghc}/bin/$prg $out/bin/$prg \
--add-flags '"-B$NIX_${ghcCommandCaps}_LIBDIR"' \
--set "NIX_${ghcCommandCaps}" "$out/bin/${ghcCommand}" \
--set "NIX_${ghcCommandCaps}PKG" "$out/bin/${ghcCommand}-pkg" \
--set "NIX_${ghcCommandCaps}_DOCDIR" "${docDir}" \
--set "NIX_${ghcCommandCaps}_LIBDIR" "${libDir}" \
${lib.optionalString (ghc.isGhcjs or false)
''--set NODE_PATH "${ghc.socket-io}/lib/node_modules"''
} \
${lib.optionalString withLLVM ''--prefix "PATH" ":" "${llvm}"''}
fi
done
for prg in runghc runhaskell; do
if [[ -x "${ghc}/bin/$prg" ]]; then
rm -f $out/bin/$prg
makeWrapper ${ghc}/bin/$prg $out/bin/$prg \
--add-flags "-f $out/bin/${ghcCommand}" \
--set "NIX_${ghcCommandCaps}" "$out/bin/${ghcCommand}" \
--set "NIX_${ghcCommandCaps}PKG" "$out/bin/${ghcCommand}-pkg" \
--set "NIX_${ghcCommandCaps}_DOCDIR" "${docDir}" \
--set "NIX_${ghcCommandCaps}_LIBDIR" "${libDir}"
fi
done
for prg in ${ghcCommand}-pkg ${ghcCommand}-pkg-${ghc.version}; do
if [[ -x "${ghc}/bin/$prg" ]]; then
rm -f $out/bin/$prg
makeWrapper ${ghc}/bin/$prg $out/bin/$prg --add-flags "${packageDBFlag}=${packageCfgDir}"
fi
done
# haddock was referring to the base ghc, https://github.com/NixOS/nixpkgs/issues/36976
if [[ -x "${ghc}/bin/haddock" ]]; then
rm -f $out/bin/haddock
makeWrapper ${ghc}/bin/haddock $out/bin/haddock \
--add-flags '"-B$NIX_${ghcCommandCaps}_LIBDIR"' \
--set "NIX_${ghcCommandCaps}_LIBDIR" "${libDir}"
fi
'' + (lib.optionalString (stdenv.targetPlatform.isDarwin && !isGhcjs && !stdenv.targetPlatform.isiOS) ''
# Work around a linker limit in macOS Sierra (see generic-builder.nix):
local packageConfDir="$out/lib/${ghc.name}/package.conf.d";
local dynamicLinksDir="$out/lib/links"
mkdir -p $dynamicLinksDir
# Clean up the old links that may have been (transitively) included by
# symlinkJoin:
rm -f $dynamicLinksDir/*
for d in $(grep dynamic-library-dirs $packageConfDir/*|awk '{print $2}'|sort -u); do
ln -s $d/*.dylib $dynamicLinksDir
done
for f in $packageConfDir/*.conf; do
# Initially, $f is a symlink to a read-only file in one of the inputs
# (as a result of this symlinkJoin derivation).
# Replace it with a copy whose dynamic-library-dirs points to
# $dynamicLinksDir
cp $f $f-tmp
rm $f
sed "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," $f-tmp > $f
rm $f-tmp
done
'') + ''
$out/bin/${ghcCommand}-pkg recache
${# ghcjs will read the ghc_libdir file when resolving plugins.
lib.optionalString (isGhcjs && ghcLibdir != null) ''
mkdir -p "${libDir}"
rm -f "${libDir}/ghc_libdir"
printf '%s' '${ghcLibdir}' > "${libDir}/ghc_libdir"
''}
# fixme: the check doesn't work
$out/bin/${ghcCommand}-pkg check || true
'' + postBuild
)
1 change: 1 addition & 0 deletions test/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let
in {
cabal-simple = callPackage ./cabal-simple { inherit haskell; };
cabal-22 = callPackage ./cabal-22 { inherit haskell; };
with-packages = callPackage ./with-packages { inherit haskell; };
}

## possible test cases
Expand Down
12 changes: 12 additions & 0 deletions test/with-packages/Point.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{-# LANGUAGE TemplateHaskell #-}
module Main where

import Control.Lens

data Point = Point { _x :: Double, _y :: Double }
makeLenses ''Point

main :: IO ()
main = print (point^.x + point^.y)
where
point = Point { _x = 40.0, _y = 2.0 }
2 changes: 2 additions & 0 deletions test/with-packages/Setup.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain
6 changes: 6 additions & 0 deletions test/with-packages/TestWithPackages.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- | Haddock test stuff
module TestWithPackages (hello) where

-- | Standard hello text.
hello :: String
hello = "Hello, world!"
Loading

0 comments on commit e51136b

Please sign in to comment.