shellFor: Refactor for consistency and cross
This makes it work like work-on-multi from Reflex Platform. In particular, rather than making `.env` from `shellFor`, we make `.env` the primitive, and `shellFor` works by combining together the arguments of all the packages to `generic-builder` and taking the `.env` of the resulting mashup-package. There are 2 benefits of this: 1. The dependency logic is deduplicated. generic builder just concatted lists, whereas all the envs until now would sieve apart haskell and system build inputs. Now, they both decide haskell vs system the same way: according to the argument list and without reflection. Consistency is good, especially because it mean that if the build works, the shell is more likely to work. 2. Cross is handled better. For native builds, because the `ghcWithPackages` calls would shadow, we through both the regular component (lib, exe, test, bench) haskell deps and Setup.hs haskell deps in the same `ghcWithPackages` call. But for cross builds we use `buildPackages.ghcWithPackages` to get the setup deps. This ensures everything works correctly.
This commit is contained in:
parent
ba9066abca
commit
7d67db3919
4 changed files with 186 additions and 58 deletions
|
@ -1,5 +1,6 @@
|
|||
{ stdenv, buildPackages, buildHaskellPackages, ghc
|
||||
, jailbreak-cabal, hscolour, cpphs, nodejs, shellFor
|
||||
, jailbreak-cabal, hscolour, cpphs, nodejs
|
||||
, ghcWithHoogle, ghcWithPackages
|
||||
}:
|
||||
|
||||
let
|
||||
|
@ -205,21 +206,28 @@ let
|
|||
optionals doCheck testPkgconfigDepends ++ optionals doBenchmark benchmarkPkgconfigDepends;
|
||||
|
||||
depsBuildBuild = [ nativeGhc ];
|
||||
nativeBuildInputs = [ ghc removeReferencesTo ] ++ optional (allPkgconfigDepends != []) pkgconfig ++
|
||||
setupHaskellDepends ++
|
||||
buildTools ++ libraryToolDepends ++ executableToolDepends ++
|
||||
optionals doCheck testToolDepends ++
|
||||
optionals doBenchmark benchmarkToolDepends;
|
||||
collectedToolDepends =
|
||||
buildTools ++ libraryToolDepends ++ executableToolDepends ++
|
||||
optionals doCheck testToolDepends ++
|
||||
optionals doBenchmark benchmarkToolDepends;
|
||||
nativeBuildInputs =
|
||||
[ ghc removeReferencesTo ] ++ optional (allPkgconfigDepends != []) pkgconfig ++
|
||||
setupHaskellDepends ++ collectedToolDepends;
|
||||
propagatedBuildInputs = buildDepends ++ libraryHaskellDepends ++ executableHaskellDepends ++ libraryFrameworkDepends;
|
||||
otherBuildInputs = extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++ executableFrameworkDepends ++
|
||||
allPkgconfigDepends ++
|
||||
optionals doCheck (testDepends ++ testHaskellDepends ++ testSystemDepends ++ testFrameworkDepends) ++
|
||||
optionals doBenchmark (benchmarkDepends ++ benchmarkHaskellDepends ++ benchmarkSystemDepends ++ benchmarkFrameworkDepends);
|
||||
|
||||
|
||||
allBuildInputs = propagatedBuildInputs ++ otherBuildInputs ++ depsBuildBuild ++ nativeBuildInputs;
|
||||
isHaskellPartition =
|
||||
stdenv.lib.partition isHaskellPkg allBuildInputs;
|
||||
otherBuildInputsHaskell =
|
||||
optionals doCheck (testDepends ++ testHaskellDepends) ++
|
||||
optionals doBenchmark (benchmarkDepends ++ benchmarkHaskellDepends);
|
||||
otherBuildInputsSystem =
|
||||
extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++ executableFrameworkDepends ++
|
||||
allPkgconfigDepends ++
|
||||
optionals doCheck (testSystemDepends ++ testFrameworkDepends) ++
|
||||
optionals doBenchmark (benchmarkSystemDepends ++ benchmarkFrameworkDepends);
|
||||
# TODO next rebuild just define as `otherBuildInputsHaskell ++ otherBuildInputsSystem`
|
||||
otherBuildInputs =
|
||||
extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++ executableFrameworkDepends ++
|
||||
allPkgconfigDepends ++
|
||||
optionals doCheck (testDepends ++ testHaskellDepends ++ testSystemDepends ++ testFrameworkDepends) ++
|
||||
optionals doBenchmark (benchmarkDepends ++ benchmarkHaskellDepends ++ benchmarkSystemDepends ++ benchmarkFrameworkDepends);
|
||||
|
||||
setupCommand = "./Setup";
|
||||
|
||||
|
@ -460,17 +468,61 @@ stdenv.mkDerivation ({
|
|||
runHook postInstall
|
||||
'';
|
||||
|
||||
passthru = passthru // {
|
||||
passthru = passthru // rec {
|
||||
|
||||
inherit pname version;
|
||||
|
||||
compiler = ghc;
|
||||
|
||||
# All this information is intended just for `shellFor`. It should be
|
||||
# considered unstable and indeed we knew how to keep it private we would.
|
||||
getCabalDeps = {
|
||||
inherit
|
||||
buildDepends
|
||||
buildTools
|
||||
executableFrameworkDepends
|
||||
executableHaskellDepends
|
||||
executablePkgconfigDepends
|
||||
executableSystemDepends
|
||||
executableToolDepends
|
||||
extraLibraries
|
||||
libraryFrameworkDepends
|
||||
libraryHaskellDepends
|
||||
libraryPkgconfigDepends
|
||||
librarySystemDepends
|
||||
libraryToolDepends
|
||||
pkgconfigDepends
|
||||
setupHaskellDepends
|
||||
;
|
||||
} // stdenv.lib.optionalAttrs doCheck {
|
||||
inherit
|
||||
testDepends
|
||||
testFrameworkDepends
|
||||
testHaskellDepends
|
||||
testPkgconfigDepends
|
||||
testSystemDepends
|
||||
testToolDepends
|
||||
;
|
||||
} // stdenv.lib.optionalAttrs doBenchmark {
|
||||
inherit
|
||||
benchmarkDepends
|
||||
benchmarkFrameworkDepends
|
||||
benchmarkHaskellDepends
|
||||
benchmarkPkgconfigDepends
|
||||
benchmarkSystemDepends
|
||||
benchmarkToolDepends
|
||||
;
|
||||
};
|
||||
|
||||
getBuildInputs = {
|
||||
# Attributes for the old definition of `shellFor`. Should be removed but
|
||||
# this predates the warning at the top of `getCabalDeps`.
|
||||
getBuildInputs = rec {
|
||||
inherit propagatedBuildInputs otherBuildInputs allPkgconfigDepends;
|
||||
haskellBuildInputs = isHaskellPartition.right;
|
||||
systemBuildInputs = isHaskellPartition.wrong;
|
||||
isHaskellPartition = stdenv.lib.partition
|
||||
isHaskellPkg
|
||||
(propagatedBuildInputs ++ otherBuildInputs ++ depsBuildBuild ++ nativeBuildInputs);
|
||||
};
|
||||
|
||||
isHaskellLibrary = isLibrary;
|
||||
|
@ -483,10 +535,64 @@ stdenv.mkDerivation ({
|
|||
# TODO: fetch the self from the fixpoint instead
|
||||
haddockDir = self: if doHaddock then "${docdir self.doc}/html" else null;
|
||||
|
||||
env = shellFor {
|
||||
packages = p: [ drv ];
|
||||
inherit shellHook;
|
||||
};
|
||||
# Creates a derivation containing all of the necessary dependencies for building the
|
||||
# parent derivation. The attribute set that it takes as input can be viewed as:
|
||||
#
|
||||
# { withHoogle }
|
||||
#
|
||||
# The derivation that it builds contains no outpaths because it is meant for use
|
||||
# as an environment
|
||||
#
|
||||
# # Example use
|
||||
# # Creates a shell with all of the dependencies required to build the "hello" package,
|
||||
# # and with python:
|
||||
#
|
||||
# > nix-shell -E 'with (import <nixpkgs> {}); \
|
||||
# > haskell.packages.ghc865.hello.envFunc { buildInputs = [ python ]; }'
|
||||
envFunc = { withHoogle ? false }:
|
||||
let
|
||||
name = "ghc-shell-for-${drv.name}";
|
||||
|
||||
withPackages = if withHoogle then ghcWithHoogle else ghcWithPackages;
|
||||
|
||||
# We use the `ghcWithPackages` function from `buildHaskellPackages` if we
|
||||
# want a shell for the sake of cross compiling a package. In the native case
|
||||
# we don't use this at all, and instead put the setupDepends in the main
|
||||
# `ghcWithPackages`. This way we don't have two wrapper scripts called `ghc`
|
||||
# shadowing each other on the PATH.
|
||||
ghcEnvForBuild =
|
||||
assert isCross;
|
||||
buildHaskellPackages.ghcWithPackages (_: setupHaskellDepends);
|
||||
|
||||
ghcEnv = withPackages (_:
|
||||
otherBuildInputsHaskell ++
|
||||
propagatedBuildInputs ++
|
||||
stdenv.lib.optionals (!isCross) setupHaskellDepends);
|
||||
|
||||
ghcCommandCaps = stdenv.lib.toUpper ghcCommand';
|
||||
in stdenv.mkDerivation ({
|
||||
inherit name shellHook;
|
||||
|
||||
depsBuildBuild = stdenv.lib.optional isCross ghcEnvForBuild;
|
||||
nativeBuildInputs =
|
||||
[ ghcEnv ] ++ optional (allPkgconfigDepends != []) pkgconfig ++
|
||||
collectedToolDepends;
|
||||
buildInputs =
|
||||
otherBuildInputsSystem;
|
||||
phases = ["installPhase"];
|
||||
installPhase = "echo $nativeBuildInputs $buildInputs > $out";
|
||||
LANG = "en_US.UTF-8";
|
||||
LOCALE_ARCHIVE = stdenv.lib.optionalString (stdenv.hostPlatform.libc == "glibc") "${buildPackages.glibcLocales}/lib/locale/locale-archive";
|
||||
"NIX_${ghcCommandCaps}" = "${ghcEnv}/bin/${ghcCommand}";
|
||||
"NIX_${ghcCommandCaps}PKG" = "${ghcEnv}/bin/${ghcCommand}-pkg";
|
||||
# TODO: is this still valid?
|
||||
"NIX_${ghcCommandCaps}_DOCDIR" = "${ghcEnv}/share/doc/ghc/html";
|
||||
"NIX_${ghcCommandCaps}_LIBDIR" = if ghc.isHaLVM or false
|
||||
then "${ghcEnv}/lib/HaLVM-${ghc.version}"
|
||||
else "${ghcEnv}/lib/${ghcCommand}-${ghc.version}";
|
||||
});
|
||||
|
||||
env = envFunc { };
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -38,12 +38,12 @@ let
|
|||
inherit (stdenv) buildPlatform hostPlatform;
|
||||
|
||||
inherit (stdenv.lib) fix' extends makeOverridable;
|
||||
inherit (haskellLib) overrideCabal getBuildInputs;
|
||||
inherit (haskellLib) overrideCabal;
|
||||
|
||||
mkDerivationImpl = pkgs.callPackage ./generic-builder.nix {
|
||||
inherit stdenv;
|
||||
nodejs = buildPackages.nodejs-slim;
|
||||
inherit (self) buildHaskellPackages ghc shellFor;
|
||||
inherit (self) buildHaskellPackages ghc ghcWithHoogle ghcWithPackages;
|
||||
inherit (self.buildHaskellPackages) jailbreak-cabal;
|
||||
hscolour = overrideCabal self.buildHaskellPackages.hscolour (drv: {
|
||||
isLibrary = false;
|
||||
|
@ -255,6 +255,8 @@ in package-set { inherit pkgs stdenv callPackage; } self // {
|
|||
# packages themselves. Using nix-shell on this derivation will
|
||||
# give you an environment suitable for developing the listed
|
||||
# packages with an incremental tool like cabal-install.
|
||||
# In addition to the "packages" arg and "withHoogle" arg, anything that
|
||||
# can be passed into stdenv.mkDerivation can be included in the input attrset
|
||||
#
|
||||
# # default.nix
|
||||
# with import <nixpkgs> {};
|
||||
|
@ -265,9 +267,11 @@ in package-set { inherit pkgs stdenv callPackage; } self // {
|
|||
# })
|
||||
#
|
||||
# # shell.nix
|
||||
# let pkgs = import <nixpkgs> {} in
|
||||
# (import ./.).shellFor {
|
||||
# packages = p: [p.frontend p.backend p.common];
|
||||
# withHoogle = true;
|
||||
# buildInputs = [ pkgs.python ];
|
||||
# }
|
||||
#
|
||||
# -- cabal.project
|
||||
|
@ -277,49 +281,41 @@ in package-set { inherit pkgs stdenv callPackage; } self // {
|
|||
# common/
|
||||
#
|
||||
# bash$ nix-shell --run "cabal new-build all"
|
||||
# bash$ nix-shell --run "python"
|
||||
shellFor = { packages, withHoogle ? false, ... } @ args:
|
||||
let
|
||||
selected = packages self;
|
||||
combinedPackageFor = packages:
|
||||
let
|
||||
selected = packages self;
|
||||
|
||||
packageInputs = map getBuildInputs selected;
|
||||
pname = if pkgs.lib.length selected == 1
|
||||
then (pkgs.lib.head selected).name
|
||||
else "packages";
|
||||
|
||||
name = if pkgs.lib.length selected == 1
|
||||
then "ghc-shell-for-${(pkgs.lib.head selected).name}"
|
||||
else "ghc-shell-for-packages";
|
||||
# If `packages = [ a b ]` and `a` depends on `b`, don't build `b`,
|
||||
# because cabal will end up ignoring that built version, assuming
|
||||
# new-style commands.
|
||||
combinedPackages = pkgs.lib.filter
|
||||
(input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected);
|
||||
|
||||
# If `packages = [ a b ]` and `a` depends on `b`, don't build `b`,
|
||||
# because cabal will end up ignoring that built version, assuming
|
||||
# new-style commands.
|
||||
haskellInputs = pkgs.lib.filter
|
||||
(input: pkgs.lib.all (p: input.outPath != p.outPath) selected)
|
||||
(pkgs.lib.concatMap (p: p.haskellBuildInputs) packageInputs);
|
||||
systemInputs = pkgs.lib.concatMap (p: p.systemBuildInputs) packageInputs;
|
||||
# Returns an attrset containing a combined list packages' inputs for each
|
||||
# stage of the build process
|
||||
packageInputs = pkgs.lib.zipAttrsWith
|
||||
(_: pkgs.lib.concatMap combinedPackages)
|
||||
(map (p: p.getCabalDeps) selected);
|
||||
|
||||
withPackages = if withHoogle then self.ghcWithHoogle else self.ghcWithPackages;
|
||||
ghcEnv = withPackages (p: haskellInputs);
|
||||
nativeBuildInputs = pkgs.lib.concatMap (p: p.nativeBuildInputs) selected;
|
||||
genericBuilderArgs = {
|
||||
inherit pname;
|
||||
version = "0";
|
||||
license = null;
|
||||
} // packageInputs;
|
||||
|
||||
ghcCommand' = if ghc.isGhcjs or false then "ghcjs" else "ghc";
|
||||
ghcCommand = "${ghc.targetPrefix}${ghcCommand'}";
|
||||
ghcCommandCaps= pkgs.lib.toUpper ghcCommand';
|
||||
in self.mkDerivation genericBuilderArgs;
|
||||
|
||||
mkDrvArgs = builtins.removeAttrs args ["packages" "withHoogle"];
|
||||
in pkgs.stdenv.mkDerivation (mkDrvArgs // {
|
||||
name = mkDrvArgs.name or name;
|
||||
|
||||
buildInputs = systemInputs ++ mkDrvArgs.buildInputs or [];
|
||||
nativeBuildInputs = [ ghcEnv ] ++ nativeBuildInputs ++ mkDrvArgs.nativeBuildInputs or [];
|
||||
phases = ["installPhase"];
|
||||
installPhase = "echo $nativeBuildInputs $buildInputs > $out";
|
||||
LANG = "en_US.UTF-8";
|
||||
LOCALE_ARCHIVE = pkgs.lib.optionalString (stdenv.hostPlatform.libc == "glibc") "${buildPackages.glibcLocales}/lib/locale/locale-archive";
|
||||
"NIX_${ghcCommandCaps}" = "${ghcEnv}/bin/${ghcCommand}";
|
||||
"NIX_${ghcCommandCaps}PKG" = "${ghcEnv}/bin/${ghcCommand}-pkg";
|
||||
# TODO: is this still valid?
|
||||
"NIX_${ghcCommandCaps}_DOCDIR" = "${ghcEnv}/share/doc/ghc/html";
|
||||
"NIX_${ghcCommandCaps}_LIBDIR" = if ghc.isHaLVM or false
|
||||
then "${ghcEnv}/lib/HaLVM-${ghc.version}"
|
||||
else "${ghcEnv}/lib/${ghcCommand}-${ghc.version}";
|
||||
envFuncArgs = builtins.removeAttrs args [ "packages" ];
|
||||
in (combinedPackageFor packages).env.overrideAttrs (old: envFuncArgs // {
|
||||
nativeBuildInputs = old.nativeBuildInputs ++ envFuncArgs.nativeBuildInputs or [];
|
||||
buildInputs = old.buildInputs ++ envFuncArgs.buildInputs or [];
|
||||
});
|
||||
|
||||
ghc = ghc // {
|
||||
|
|
|
@ -22,6 +22,8 @@ with pkgs;
|
|||
cc-wrapper-libcxx-7 = callPackage ./cc-wrapper { stdenv = llvmPackages_7.libcxxStdenv; };
|
||||
stdenv-inputs = callPackage ./stdenv-inputs { };
|
||||
|
||||
haskell-shellFor = callPackage ./haskell-shellFor { };
|
||||
|
||||
cc-multilib-gcc = callPackage ./cc-wrapper/multilib.nix { stdenv = gccMultiStdenv; };
|
||||
cc-multilib-clang = callPackage ./cc-wrapper/multilib.nix { stdenv = clangMultiStdenv; };
|
||||
|
||||
|
|
24
pkgs/test/haskell-shellFor/default.nix
Normal file
24
pkgs/test/haskell-shellFor/default.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
{ stdenv, haskellPackages, cabal-install }:
|
||||
|
||||
haskellPackages.shellFor {
|
||||
packages = p: [ p.database-id-class p.constraints-extras ];
|
||||
nativeBuildInputs = [ cabal-install ];
|
||||
phases = [ "unpackPhase" "buildPhase" "installPhase" ];
|
||||
unpackPhase = ''
|
||||
sourceRoot=$(pwd)/scratch
|
||||
mkdir -p "$sourceRoot"
|
||||
cd "$sourceRoot"
|
||||
tar -xf ${haskellPackages.database-id-class.src}
|
||||
tar -xf ${haskellPackages.constraints-extras.src}
|
||||
cp ${builtins.toFile "cabal.project" "packages: database-id-class* constraints-extras*"} cabal.project
|
||||
'';
|
||||
buildPhase = ''
|
||||
export HOME=$(mktemp -d)
|
||||
mkdir -p $HOME/.cabal
|
||||
touch $HOME/.cabal/config
|
||||
cabal v2-build --offline --verbose database-id-class constraints-extras --ghc-options="-O0 -j$NIX_BUILD_CORES"
|
||||
'';
|
||||
installPhase = ''
|
||||
touch $out
|
||||
'';
|
||||
}
|
Loading…
Reference in a new issue