Skip to content
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

Per component building, and hackage DB #6

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 cabal.project
Original file line number Diff line number Diff line change
@@ -1 +1 @@
packages: . hackage-db
packages: . hackage-db
10 changes: 8 additions & 2 deletions nix/cabal-licenses.nix
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
stdenv: with stdenv.lib.licenses;
{ BSD-3-Clause = bsd3; }
lib: with lib.licenses;
{
BSD-2-Clause = bsd2;
BSD-3-Clause = bsd3;
MIT = mit;
ISC = isc;
LicenseRef-LGPL = "LGPL";
}
10 changes: 5 additions & 5 deletions nix/cabal-os-arch-comp.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@
# OSs
isLinux = false;
isWindows = false;
isOSX = false;
isFreeBSD = false;
isOsx = false;
isFreebsd = false;
isOpenBSD = false;
isNetBSD = false;
isDragonFly = false;
isSolaris = false;
isAIX = false;
isAix = false;
isHPUX = false;
isIRIX = false;
isHaLVM = false;
isHalvm = false;
isHurd = false;
isIOS = false;
isIos = false;
isAndroid = false;
isGhcjs = false;
};
Expand Down
108 changes: 108 additions & 0 deletions nix/comp-builder.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{ stdenv, ghc, lib, pkgconfig, writeText, haskellLib }:

{ componentId
, component
, package
, name
, setup
, src
, flags
, cabalFile
}:

let
flagsAndConfig = field: xs: {
flags = map (x: "--${field}=${x}") xs;
config = lib.optional (xs != []) "${field}: ${lib.concatStringsSep " " xs}";
};

allFlagsAndConfigs = {
packageDbs =
let
makePairs = map (p: rec { key="${val}"; val=p.components.${p.identifier.name}; });
closure = builtins.genericClosure {
startSet = makePairs component.depends;
operator = {val,...}: makePairs val.config.depends;
};
flatDepends = map ({val,...}: val) closure;
in flagsAndConfig "package-db" (map (p: "${p}/package.conf.d") flatDepends);

extraLibDirs = flagsAndConfig "extra-lib-dirs" (map (p: "${p}/lib") component.libs);
extraIncludeDirs = flagsAndConfig "extra-include-dirs" (map (p: "${lib.getDev p}/include") component.libs);
extraFameworks = flagsAndConfig "extra-framework-dirs" (map (p: "${p}/Library/Frameworks") component.frameworks);
userFlags = {
flags = [("--flags=\"" + lib.concatStringsSep " " (lib.mapAttrsToList (fname: val: lib.optionalString (!val) "-" + fname) flags) + "\"")];
config = [];
};
};

finalConfigureFlags = lib.concatStringsSep " " (
[ "--prefix=$out" "${componentId.ctype}:${componentId.cname}" ]
++ builtins.concatLists (lib.mapAttrsToList (_: x: x.flags) allFlagsAndConfigs)
++ component.configureFlags
);
in stdenv.mkDerivation {
name = "${name}-${componentId.ctype}-${componentId.cname}";

inherit src;

doCheck = componentId.ctype == "test";

passthru = {
inherit (package) identifier;
config = component;
};

meta = {
homepage = package.homepage;
description = package.synopsis;
license = (import ./cabal-licenses.nix lib).${package.license};
};

CABAL_CONFIG = writeText
"package-db-cabal.config"
(lib.concatStringsSep "\n" (builtins.concatLists (lib.mapAttrsToList (_: x: x.config) allFlagsAndConfigs)));

enableParallelBuilding = true;

buildInputs = component.libs
++ component.pkgconfig;

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;

SETUP_HS = setup + /bin/Setup;

# Phases
prePatch = lib.optionalString (cabalFile != null) ''
cat ${cabalFile} > ${package.identifier.name}.cabal
'';

configurePhase = ''
echo Configure flags:
printf "%q " ${finalConfigureFlags}
echo
$SETUP_HS configure ${finalConfigureFlags}
'';

buildPhase = ''
$SETUP_HS build -j$NIX_BUILD_CORES
'';

checkPhase = ''
$SETUP_HS test
'';

installPhase = ''
$SETUP_HS copy
${lib.optionalString (haskellLib.isLibrary componentId) ''
$SETUP_HS register --gen-pkg-config=${name}.conf
ghc-pkg init $out/package.conf.d
ghc-pkg ${lib.concatStringsSep " " allFlagsAndConfigs.packageDbs.flags} -f $out/package.conf.d register ${name}.conf
''}
'';
}
13 changes: 13 additions & 0 deletions nix/compat-driver.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{ pkgs, lib, haskellLib, ghc, weakCallPackage, plan }:

lib.fix (hsPkgs: pkgs.callPackage "${pkgs.path}/pkgs/development/haskell-modules" {
haskellLib = pkgs.haskell.lib;
inherit ghc;
buildHaskellPackages = hsPkgs;
compilerConfig = _: _: lib.mapAttrs (_: _: null) (builtins.removeAttrs plan.compiler.packages ["ghc"]);
initialPackages = args: self:
lib.mapAttrs
(_: pkg: self.callPackage ./driver.nix { expr = lib.fix pkg; inherit lib; })
(plan.packages self);
packageSetConfig = plan.overlay pkgs;
})
11 changes: 11 additions & 0 deletions nix/component-driver.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{ pkgs, lib, haskellLib, ghc, weakCallPackage, plan }:
let
new-builder = weakCallPackage pkgs ./new-builder.nix {
inherit haskellLib ghc weakCallPackage;
};
in
lib.fix (self:
{ buildPackages = self; }
// lib.mapAttrs (_: _: null) plan.compiler.packages
// lib.mapAttrs (_: pkg: new-builder (lib.fix pkg)) (plan.packages self)
)
121 changes: 121 additions & 0 deletions nix/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{ pkgs ? import <nixpkgs> {}
, planFunc ? import ../stackage/lts-11.4.nix
, hackage ? import ../hackage
, driver ? ./component-driver.nix
}:

(pkgs.lib.fix (self: with self; {
inherit (pkgs) lib stdenv;
ghc = pkgs.haskell.compiler.${plan.compiler.nix-name};

# Avoid pkgs.callPackage for now. It does a lot of nonsense with OOP
# style programming that we should avoid until we know we want it.
weakCallPackage = scope: f: args:
let f' = if lib.isFunction f then f else import f;
args' = scope // args;
in f' (builtins.intersectAttrs (builtins.functionArgs f') args');

haskellLib = import ./lib.nix { inherit lib haskellLib; };

hackageConfigs =
lib.flip lib.mapAttrs hackage
(pname: lib.mapAttrs
(vnum: version: version // {
revisions =
let
rev2Config = rev: {
inherit (version) sha256;
inherit (rev) outPath;
revision = rev.revNum;
revisionSha256 = rev.sha256;
};
f = rev: acc: acc // {
# If there's a collision (e.g. a revision was
# reverted), pick the one with the smaller
# revNum. They're identical, but if the smaller one is
# r0 then we don't have to download a cabal file.
${rev.sha256} = if lib.hasAttr rev.sha256 acc && acc.${rev.sha256}.revNum < rev.revNum
then acc.${rev.sha256}
else rev;
};
contentAddressedRevs = lib.foldr f {} (builtins.attrValues version.revisions);
in lib.mapAttrs (_: rev2Config) (version.revisions // contentAddressedRevs);
}));
plan =
let p = planFunc hackageConfigs;
in p // {
packages = hsPkgs:
let
args = {
inherit hsPkgs compiler system pkgconfPkgs;
pkgs = adjustedPkgs;
};
in lib.mapAttrs (pname: { revision, flags ? {} }: self:
# Set the flags with the rhs of the recursiveUpdate, but
# pass the final choice in flags using open recursion.
lib.recursiveUpdate (import revision (args // { inherit (self) flags; })) {
inherit flags;
inherit (revision) sha256 revision revisionSha256;
}
)
p.packages;
};

cabal = import ./cabal-os-arch-comp.nix;

compiler = cabal.compiler // {
isGhc = true;
version = lib.mapAttrs (_: f: v: f (builtins.compareVersions plan.compiler.version v)) {
eq = c: c == 0;
gt = c: c > 0;
ge = c: c >= 0;
lt = c: c < 0;
le = c: c <= 0;
};
};
system = let
hostMap = import ./host-map.nix pkgs.stdenv;
in cabal.os // { "is${hostMap.os}" = true; }
// cabal.arch // { "is${hostMap.arch}" = true; };

adjustedPkgs = pkgs // {
pthread = null;
"stdc++" = null;
ssl = pkgs.openssl.dev;
crypto = pkgs.openssl.dev;
z = pkgs.zlib;
GL = pkgs.libGL;
GLU = pkgs.libGLU;
alut = pkgs.freealut;
X11 = pkgs.xorg.libX11;
Xrandr = pkgs.xorg.libXrandr;
Xext = pkgs.xorg.libXext;
Xi = pkgs.xorg.libXi;
Xxf86vm = pkgs.xorg.libXxf86vm;
Xcursor = pkgs.xorg.libXcursor;
Xinerama = pkgs.xorg.libXinerama;
mysqlclient = pkgs.mysql;
Imlib2 = pkgs.imlib2;
asound = pkgs.alsaLib;
ffi = null;
};
pkgconfPkgs = pkgs // {
cairo-pdf = pkgs.cairo;
cairo-ps = pkgs.cairo;
cairo-svg = pkgs.cairo;
xft = pkgs.xorg.libXft;
xau = pkgs.xorg.libXau;
libR = pkgs.R;
fftw3f = pkgs.fftwFloat;
fftw3 = pkgs.fftw;
};

# hsPkgs = adjustedPkgs
# // { buildPackages = hsPkgs; }
# // lib.mapAttrs (_: _: null) (plan.compiler.packages // { hsc2hs = "0.68.2"; })
# // lib.mapAttrs (_: driver) configs;

hsPkgs = weakCallPackage pkgs driver {
inherit haskellLib ghc weakCallPackage plan;
};
}))
53 changes: 17 additions & 36 deletions nix/driver.nix
Original file line number Diff line number Diff line change
@@ -1,50 +1,31 @@
{ cabalexpr, pkgs, compiler ? "Ghc", os ? "OSX", arch ? "X86_64" }:
assert (builtins.elem compiler ["Ghc" "Ghcjs" "Nhc" "Yhc" "Hugs" "Hbc" "Helium" "Jhc" "Lhc" "Uhc" "Eta"]);
assert (builtins.elem os ["Linux" "Windows" "OSX" "FreeBSD" "OpenBSD" "NetBSD" "DragonFly" "Solaris" "AIX" "HPUX" "IRIX" "HaLVM" "Hurd" "IOS" "Android" "Ghcjs"]);
assert (builtins.elem arch ["I386" "X86_64" "PPC" "PPC64" "Sparc" "Arm" "Aarch64" "Mips" "SH" "IA64" "S390" "Alpha" "Hppa" "Rs6000" "M68k" "Vax" "JavaScript"]);
{ lib, expr, mkDerivation }:

with rec {
let
# utilities
collectAttr = a: s: pkgs.lib.lists.fold (e: acc: (if e ? ${a} then e.${a} else []) ++ acc) [] (builtins.attrValues s);

# resolver for cabal license to nix license
resolve = { license = stdenv: license: (import ./cabal-licenses.nix stdenv).${license}; };

# cabal os, arch and compilers.
cabal = import ./cabal-os-arch-comp.nix;

expr = cabalexpr {
# null out self references. Tests, Benchmarks, ...
# naturally depend on the package (as a library), but
# we don't want those dependencies.
hsPkgs = pkgs.haskellPackages // { ${pname} = null; };
pkgs = pkgs;
compiler = cabal.compiler // { "is${compiler}" = true; };
system = cabal.os // { "is${os}" = true; }
// cabal.arch // { "is${arch}" = true; };
};

collectAttr = a: s: lib.lists.fold (e: acc: (if e ? ${a} then e.${a} else []) ++ acc) [] (builtins.attrValues s);
pname = expr.package.identifier.name;
};
{ mkDerivation, stdenv }:
mkDerivation {

in mkDerivation ({
inherit pname;
version = expr.package.identifier.version;
sha256 = null;

isLibrary = builtins.hasAttr pname expr.components;
isExecutable = builtins.hasAttr "exes" expr.components;

homepage = expr.package.homepage;
description = expr.package.synopsis;
license = resolve.license stdenv expr.package.license;
} // pkgs.lib.optionalAttrs (builtins.hasAttr pname expr.components) {
libraryHaskeallDepends = expr.components.${pname}.depends;
} // pkgs.lib.optionalAttrs (builtins.hasAttr "exes" expr.components) {
license = (import ./cabal-licenses.nix lib).${expr.package.license};

} // lib.optionalAttrs (expr ? revision) {
revision = "${toString expr.revision}";
editedCabalFile = expr.revisionSha256;
} // lib.optionalAttrs (expr ? sha256) {
inherit (expr) sha256;
} // lib.optionalAttrs (builtins.hasAttr pname expr.components) {
libraryHaskellDepends = expr.components.${pname}.depends;
} // lib.optionalAttrs (builtins.hasAttr "exes" expr.components) {
executableHaskellDepends = collectAttr "depends" expr.components.exes;
} // pkgs.lib.optionalAttrs (builtins.hasAttr "tests" expr.components) {
} // lib.optionalAttrs (builtins.hasAttr "tests" expr.components) {
testHaskellDepends = collectAttr "depends" expr.components.tests;
} // pkgs.lib.optionalAttrs (builtins.hasAttr "benchmarks" expr.components) {
} // lib.optionalAttrs (builtins.hasAttr "benchmarks" expr.components) {
benchmarkHaskellDepends = collectAttr "depends" expr.components.benchmarks;
}
})
Loading