One does not simply use GHCup on macOS M1
source link: https://robertwpearce.com/one-does-not-simply-use-ghcup-on-macos-m1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Summary | Using Haskell through Nix or Docker might be easier paths on macOS, but this article should help if those options aren't available. |
---|---|
Shared | 2023-03-20 |
Intro
The GHCup tool is the official installer for core Haskell tools: cabal, stack, haskell-language-server, and ghc.
I usually use Haskell through Nix (I’m liking devenv.sh, too), and I’ve also used it through Docker, but I was frustrated with build times and wanted to try the official Haskell way.
Unfortunately, I had a rough time trying to use GHCup on a macOS M1 (Ventura 13.2.1), so I documented trying to build a small Haskell project of mine, slugger, with it.
A note about Homebrew
I use Homebrew for installing all sorts of CLI tools and apps for macOS (here’s my personal Brewfile).
While I will use it for something else later in this guide, I could not get ghcup
to work properly when installed via Homebrew, and trying to upgrade GHCup through its interface conflicted with the Homebrew install. Instead, I will use the installer found on the GHCup page.
The library I tried to build
The example library I tried building was my URI slug library, slugger.
Installing GHCup
I like to keep my $HOME
directory clean by having tools adhere to the XDG spec. I read that if I wanted GHCup to use XDG
, I needed to export this variable in the shell where the installer was going to run:
export GHCUP_USE_XDG_DIRS="true"
Here are my XDG environment variables.
Since I always want this to be true, I include that in my .zshenv
dotfile just in case.
Next, I installed GHCup:
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
This is an interactive installer, so there was a bit of output and questions.
Tip: if you run this installer, make sure you read the messages.
Including the GHCup environment
The script asked if it could append something to the end of my .zshrc
file. I prefer to own my environment setup, so I let it do its thing, inspected the file to make sure it looked good, then I changed the sourcing code into a style I prefer:
ghcup_script_path="${XDG_DATA_HOME}/ghcup/env"
[[ -f "${ghcup_script_path}" ]] && source "${ghcup_script_path}"
This adds some Haskell bin-related directories to $PATH
if they aren’t already there.
Running the GHCup terminal user interface
Once this was all done, I opened a new shell window and ran
ghcup tui
TUI is an acronym for “terminal user interface”.
I used the interface to install the recommended tool versions, and this was really easy! Well done, GHCup crew.
Then I went to go see if I could build slugger
.
Building slugger: failure #1 (LLVM)
When I went to the slugger
project directory, I ran cabal v2-build
, and some LLVM errors printed to the screen.
Notably:
Warning: Couldn’t figure out LLVM version! Make sure you have installed LLVM between [9 and 13]
Remember how I said to make sure you read the installer messages? Yeah. I didn’t.
On Darwin M1 you might also need a working llvm installed (e.g. via brew) and have the toolchain exposed in the PATH.
Building slugger: failures #2-4 (also LLVM)
As suggested by the warnings above, I added brew llvm@9
to my Brewfile
, installed it, and tried to cabal v2-build
the slugger
project.
That didn’t work (same sort of issue).
I tried llvm@10
, llvm@11
, and llvm@12
.
None of those worked, either! Would llvm@13
work? Maybe, maybe, maybe…
Building slugger: failure #5 (GHC and LLVM)
It seems none of these will work if ghc
doesn’t know to use LLVM.
I keep a cabal config file in my dotfiles and it had a section, program-default-options
, that contained a ghc-options
key for passing flags to ghc.
Here’s how I told GHC about LLVM:
program-default-options
ghc-options: -fllvm
There’s more information about that on the Haskell GHC Backends doc.
Did that make a difference? Yep!
Building slugger: failure #6 (missing foreign libraries)
Aha! A different error.
cabal-3.6.2.0 Missing dependencies on foreign libraries:
Missing (or bad) C libraries: icuuc, icui18n, icudata
This one stemmed from trying to build a dependency, text-icu, and it seemed I was missing some libraries it expected to find on the OS.
I saw some references on GitHub issues to the icu4c
tool, but I was luckily able to find this archived “Missing dependency on a foreign library” guide that simply told me what to do:
brew install icu4c
If you’re using stack, add this to
~/.stack/config.yaml
:extra-include-dirs: - /usr/local/opt/icu4c/include extra-lib-dirs: - /usr/local/opt/icu4c/lib
Unfortunately, none of this worked out of the box for me for two reasons:
- I’m not using
stack
- Homebrew uses
/opt/homebrew/
for Apple Silicon—not/usr/local/
But those config options looked exactly the same as the recommendation from the build warning above, and that gave me some things to try:
If the libraries are already installed but in a non-standard location then you can use the flags
--extra-include-dirs=
and--extra-lib-dirs=
to specify where they are.
Fixing the missing foreign libraries issue
It turns out that my cabal.conf
file had extra-include-dirs
and extra-lib-dirs
in it, so I didn’t need to pass paths every time I tried to build with cabal.
I don’t regularly edit cabal config files, so I took the stack
YAML config above and tried it:
extra-include-dirs:
- /opt/homebrew/opt/icu4c/include
extra-lib-dirs:
- /opt/homebrew/opt/icu4c/lib
Nope, that didn’t work. I tried indenting the -
to see if the config file liked that.
Nope.
While this config file might, at a glance, resemble YAML, it isn’t—it seems to resemble (or even be) a .cabal
file (email me if you know, please!). Here was a correct way to write them:
extra-include-dirs:
/opt/homebrew/opt/icu4c/include
extra-lib-dirs:
/opt/homebrew/opt/icu4c/lib
Sweet success
With high hopes, I ran cabal v2-build
again, and it worked!
I was successfully able to build my little library and test it out with cabal
.
Personal retrospective on the experience
There are a number of places here where, if I’d have paid closer attention to (admittedly helpful) walls of text, I’d have been led to solutions faster. That is unquestionably my fault!
That said, the errors don’t cover everything you have to do (like the -fllvm
GHC flag), and this overall experience on macOS was rough for me.
I am grateful for all the effort put into GHCup, and I know it takes time and money to make things simple.
For now, even though Nix’s story isn’t one of simplicity, either, I’m going to mostly stick with building Haskell projects that way. However, I’ll keep my options open and periodically try things the GHCup way, as well.
Thanks for reading!
— Robert
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK