We’re using llvm-hs at work. However, there is a problem building it on MacOSX.
Through some trail-and-error, I was able to get it building. It was somewhat complicated, so I wanted to create a post here that explained how to do it.
From what I could tell, llvm-hs needs to be built in an environment with the LLVM version it expects (so llvm-hs-8.0.0 needs LLVM-8 available). However, GHC is built with clang-7 (from LLVM-7), and this seems to mess up llvm-hs-8.0.0 when building.
In order to work around this, I had to recompile GHC with using clang-8. Because GHC is a compiler and interacts with the cross-compilation infrastructure, it was somewhat complicated figuring out how to do this.
Here’s the derivation I came up with to get llvm-hs-8.0.0 building on MacOSX:
# This derivation shows how to build llvm-hs-8.0.0 on MacOSX with a recent
# version of nixpkgs.
#
# llvm-hs is needs to be built in an environment with a specific version of
# llvm and related tools. For instance, llvm-hs-8.0.0 needs to be built in
# an environment with LLVM-8 and clang-8.
#
# The difficulty of doing this is that the stdenv on darwin uses clang-7 by
# default, and GHC is built using this stdenv. GHC sets the C compiled used to
# build it as a propagatedBuildInput. This interferes with building llvm-hs.
#
# The trick is to rebuild GHC with the same version of clang provided by the
# version of LLVM used by llvm-hs. In our case, llvm-hs-8.0.0 requires LLVM-8,
# so we rebuilt GHC with clang-8 from the LLVM-8 stdenv.
let
nixpkgs = builtins.fetchTarball {
# nixpkgs master as of 2019/11/12.
url = "https://github.com/NixOS/nixpkgs/archive/2041381185a3794b658928506faf575091fa3088.tar.gz";
sha256 = "1dqznlhxf6w23db8apvjp98b9xdw9vqzqq4pii6i6b134v9nmkxr";
};
in
with import nixpkgs {};
with haskell.lib;
let
# This is the stdenv we will be using everywhere below. You can see it comes
# from LLVM-8, which means it uses clang-8.
#
# Note that I don't just create an overlay setting the default stdenv to be
# llvmPackages_8.stdenv because I don't want to recompile absolutely
# everything, just Haskell-related things.
stdenvUsingLLVM8 = llvmPackages_8.stdenv;
# This is GHC rebuilt with LLVM-8.
#
# This is somewhat complicated because GHC can be setup to cross compile
# binaries, which means it has to be careful whether it gets things from
# buildPackages, targetPackages, or pkgsBuildTarget.
#
# By default, GHC sets the C compiler (clang in our case) as a
# propagatedBuildInput. If we don't override clang to come from llvm-8, then
# this messes up llvm-hs when we try to compile it. It is really important
# that GHC only see clang-8.
#
# I don't really know that much about cross-compilation in nixpkgs, so there
# may be an easier way to write this.
ghcUsingLLVM8 = haskell.compiler.ghc865.override {
stdenv = stdenvUsingLLVM8;
llvmPackages = llvmPackages_8;
buildLlvmPackages = buildPackages.llvmPackages_8;
targetPackages = targetPackages.extend (self: super: {
stdenv = stdenvUsingLLVM8;
});
pkgsBuildTarget = pkgsBuildTarget.extend (self: super: {
targetPackages = super.targetPackages.extend (sself: ssuper: {
stdenv = stdenvUsingLLVM8;
});
});
};
# This is a Haskell package set using our GHC from above and a stdenv with
# only references to clang-8.
haskellPackagesUsingLLVM8 = haskellPackages.override {
# buildHaskellPackages needs to be overridden. If you don't override it, it becomes an environment
# with a version of GHC that is not built with clang-8.
#
# Originally I tried to override it like the following. This also seemed to work:
#
# buildHaskellPackages = buildPackages.haskell.packages.ghc865.override {
# stdenv = stdenvUsingLLVM8;
# ghc = ghcUsingLLVM8;
# };
#
# However, through trial-and-error I also figured out that it could just be this recursive package set.
# I'm not sure why this works and doesn't go into an infinite recursion error.
buildHaskellPackages = haskellPackagesUsingLLVM8;
stdenv = stdenvUsingLLVM8;
ghc = ghcUsingLLVM8;
};
llvm-hs =
let
drv = haskellPackagesUsingLLVM8.llvm-hs.override {
# The llvm-config executable is a build tool (similar to pkg-config but
# specific for LLVM). It comes from the llvm_8 package.
llvm-config = llvm_8;
};
in
overrideCabal drv (oldArgs: {
# Apparently something links to libxml2. Not sure what requires this.
librarySystemDepends = (oldArgs.librarySystemDepends or []) ++ [
libxml2
];
# llvm-hs has a Cabal flag called `shared-llvm`. This is enabled by
# default, and it tries to link to dynamic shared libraries provided by
# LLVM instead of static libraries.
#
# However, apparently the LLVM maintainers don't like the shared-library
# approach, so they recommend only using static libraries. LLVM-8 in
# nixpkgs only provides static libraries, so the `shared-llvm` flag must
# be disabled.
configureFlags = (oldArgs.configureFlags or []) ++ [
"-f-shared-llvm"
];
# llvm-hs tests fail on MacOSX. I'm not sure if this has been reported
# upstream.
doCheck = false;
});
in
llvm-hs
I just wanted to leave this in a google-able location so that other people might be able to find it if they are having trouble getting it working.