This document describes my setup for working with Haskell on Windows. Notice I don't know much of the intricacies of Windows but it seems I was able to make it work.
It will contain also personal recommendations.
Install git using scoop
in PowerShell:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
scoop install git
Notice we are installing Git for Windows with this. It seems like it has worked properly for me until now. Some configs:
➜ cat .gitconfig
[user]
name = ...
email = ...
[core]
autocrlf = input
sshCommand = C:/Windows/System32/OpenSSH/ssh.exe
[gc]
auto = 256
[alias]
from-linux = "!__git_rm_symlinks() {\n case \"$1\" in (-h)\n printf 'usage: git from-linux [symlink] [symlink] [...]\\n'\n return 0\n esac\n ppid=$$\n case $# in\n (0) git ls-files -s | grep -E '^120000' | cut -f2 ;;\n (*) printf '%s\\n' \"$@\" ;;\n esac | while IFS= read -r symlink; do\n case \"$symlink\" in\n (*/*) symdir=${symlink%/*} ;;\n (*) symdir=. ;;\n esac\n git checkout -- \"$symlink\"\n src=\"${symdir}/$(cat \"$symlink\")\"\n posix_to_dos_sed='s_^/\\([A-Za-z]\\)_\\1:_;s_/_\\\\\\\\_g'\n doslnk=$(printf '%s\\n' \"$symlink\" | sed \"$posix_to_dos_sed\")\n dossrc=$(printf '%s\\n' \"$src\" | sed \"$posix_to_dos_sed\")\n if [ -f \"$src\" ]; then\n rm -f \"$symlink\"\n cmd //C mklink //H \"$doslnk\" \"$dossrc\"\n elif [ -d \"$src\" ]; then\n rm -f \"$symlink\"\n cmd //C mklink //J \"$doslnk\" \"$dossrc\"\n else\n printf 'error: git-rm-symlink: Not a valid source\\n' >&2\n printf '%s =/=> %s (%s =/=> %s)...\\n' \\\n \"$symlink\" \"$src\" \"$doslnk\" \"$dossrc\" >&2\n false\n fi || printf 'ESC[%d]: %d\\n' \"$ppid\" \"$?\"\n git update-index --assume-unchanged \"$symlink\"\n done | awk '\n BEGIN { status_code = 0 }\n /^ESC\\['\"$ppid\"'\\]: / { status_code = $2 ; next }\n { print }\n END { exit status_code }\n '\n}\n__git_rm_symlinks"
to-linux = "!__git_checkout_symlinks() {\n case \"$1\" in (-h)\n printf 'usage: git to-linux [symlink] [symlink] [...]\\n'\n return 0\n esac\n case $# in\n (0) git ls-files -s | grep -E '^120000' | cut -f2 ;;\n (*) printf '%s\\n' \"$@\" ;;\n esac | while IFS= read -r symlink; do\n git update-index --no-assume-unchanged \"$symlink\"\n rmdir \"$symlink\" >/dev/null 2>&1\n git checkout -- \"$symlink\"\n printf 'Restored git symlink: %s -> %s\\n' \"$symlink\" \"$(cat \"$symlink\")\"\n done\n}\n__git_checkout_symlinks"
[gpg]
program = C:/Program Files (x86)/GnuPG/bin/gpg.exe
[commit]
gpgsign = true
[core]
editor = nano
symlinks = true
Use git from-linux
and git to-linux
to remove and restore symbolic links.
For gpg
, I use Gpg4win
, and for ssh
I use Window's OpenSSH
The developer environment will be based on MSYS2. Download the installer from their webpage and follow the wizard steps.
Run pacman -Syuu
twice, to update the runtime and to update the packages.
Add this line to your /etc/nsswitch.conf
file from inside MSYS2.
db_home: <whatever-was-here> windows
and restart the terminal.
GHC ships a minimal toolchain in order to not depend on the user's toolchain (currently 0.8 here, check here for the version on your particular GHC).
Before GHC 9.4, the base environment was GCC-based, thus the best way to align things was to use a MINGW64
environment.
After GHC 9.4, the base environment is clang-based. So I would suggest using the CLANG64
environment.
Add the argument -use-full-path
to the msys2_shell.cmd
(right-click on the icon that appears when you search for MSYS2 in your start menu, and modify the shortcut).
Some packages that I find very useful to have around:
mingw-w64-clang-x86_64-pkgconf
: there is amsys2/pkgconf
package but it doesn't understand Windows paths inPKG_CONFIG_PATH
... Sadly this means that if you change environments you will have to switch thepkg-config
package (or install a new one).base-devel
: generally usefulmingw-w64-clang-x86_64-zlib
mingw-w64-clang-x86_64-fd
: useful replace forfind
.mingw-w64-clang-x86_64-jq
mingw-w64-clang-x86_64-emacs
: just in case.mingw-w64-clang-x86_64-openssl
mingw-w64-clang-x86_64-python
andmingw-w64-clang-x86_64-python-pip
.
or in one line:
pacman --noconfirm -S base-devel mingw-w64-clang-x86_64-pkgconf mingw-w64-clang-x86_64-zlib mingw-w64-clang-x86_64-fd mingw-w64-clang-x86_64-jq mingw-w64-clang-x86_64-emacs mingw-w64-clang-x86_64-python mingw-w64-clang-x86_64-python-pip
I tend to work with zsh
so I would suggest installing pacman -S zsh
.
I would suggest installing the Windows Terminal and FiraCode Nerd font
(make sure to install it for all users or Windows Terminal will complain).
There you can configure the different profiles you want to use. My settings are as follows (notice the -use-full-path
and the -shell zsh
):
"defaultProfile": "<some profile guid from below>",
"profiles":
{
"defaults": {},
"list":
[
{
"colorScheme": "One Half Dark",
"commandline": "C:\\msys64\\msys2_shell.cmd -defterm -here -no-start -clang64 -shell zsh -use-full-path",
"font":
{
"face": "FiraCode Nerd Font"
},
"guid": "...",
"hidden": false,
"icon": "C:\\msys64\\clang64.ico",
"name": "MSYS2 / CLANG64",
"startingDirectory": "C:/Users/Javier"
},
{
"colorScheme": "Solarized Dark",
"commandline": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
"font":
{
"face": "FiraCode Nerd Font"
},
"guid": "...",
"hidden": false,
"name": "Windows PowerShell"
}
]
},
"schemes":
[
{
"background": "#282C34",
"black": "#282C34",
"blue": "#61AFEF",
"brightBlack": "#5A6374",
"brightBlue": "#61AFEF",
"brightCyan": "#56B6C2",
"brightGreen": "#98C379",
"brightPurple": "#C678DD",
"brightRed": "#E06C75",
"brightWhite": "#DCDFE4",
"brightYellow": "#E5C07B",
"cursorColor": "#FFFFFF",
"cyan": "#56B6C2",
"foreground": "#DCDFE4",
"green": "#98C379",
"name": "One Half Dark",
"purple": "#C678DD",
"red": "#E06C75",
"selectionBackground": "#FFFFFF",
"white": "#DCDFE4",
"yellow": "#E5C07B"
},
{
"background": "#002B36",
"black": "#002B36",
"blue": "#268BD2",
"brightBlack": "#073642",
"brightBlue": "#839496",
"brightCyan": "#93A1A1",
"brightGreen": "#586E75",
"brightPurple": "#6C71C4",
"brightRed": "#CB4B16",
"brightWhite": "#FDF6E3",
"brightYellow": "#657B83",
"cursorColor": "#FFFFFF",
"cyan": "#2AA198",
"foreground": "#839496",
"green": "#859900",
"name": "Solarized Dark",
"purple": "#D33682",
"red": "#DC322F",
"selectionBackground": "#FFFFFF",
"white": "#EEE8D5",
"yellow": "#B58900"
}
]
I also use starship and install it from pacman
with mingw-w64-clang-x86_64-starship
.
Install the latest PowerShell with winget
:
winget install --id Microsoft.WindowsTerminal --source winget
Point your Windows Terminal profile to C:\Program Files\PowerShell\7\pwsh.exe
.
Add this to your profile (~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
) to enable which
and also movement like emacs on the command line:
New-Alias which get-command
Import-Module PSReadLine
Set-PSReadLineOption -EditMode Emacs
If you want starship
, then you can install it with winget
:
winget install --id Starship.Starship
and then add this to your profile:
Invoke-Expression (&starship init powershell)
Set the following user variables:
Var | Value |
---|---|
GHCUP_INSTALL_BASE_PREFIX |
C:\ |
GHCUP_MSYS2 |
C:\msys64 |
Path |
Add C:\ghcup\bin and C:\Users\Javier\AppData\Roaming\cabal\bin |
Then go to GHCup and get the POSIX install command:
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
And run it in an MSYS2 shell. Restart the terminal and run ghcup tui
to select the latest versions of cabal
, stack
and the desired ghc
.
GHCup will have modified your cabal global config like this:
extra-include-dirs: C:\msys64\clang64\include
extra-lib-dirs: C:\msys64\clang64\lib
extra-prog-path: C:\ghcup\bin,
C:\Users\<USER>\AppData\Roaming\cabal\bin,
C:\msys64\clang64\bin,
C:\msys64\usr\bin
This might lead to problems sometimes, see this.
I would say leave it if using CLANG64
and GHC >= 9.4
, but if using MINGW64
you might need to comment the include and lib dirs.
I use this function to switch environments if needed:
cabal-msys2-env () {
sed -i "s/\(ucrt\|mingw\|clang\)/$1/g" $(cygpath -u "$(cabal --help | tail -n1 | sed 's_\r__g' | tr -d ' ')")
}
NOTE: while this is fixed, I suggest adding the following to your cabal config
program-default-options ghc-options: -optc-Wno-pragma-pack -optc-Wno-macro-redefined -optc-Wno-missing-declarations
Install VSCode from its webpage and install the Haskell extension and the Haskell syntax extension.
Now configure the settings.json
as follows:
{
"terminal.integrated.profiles.windows": {
...
"MSYS2 CLANG64": {
"path": "C:\\msys64\\msys2_shell.cmd",
"args": [
"-defterm", "-here", "-no-start", "-clang64", "-shell", "zsh", "-use-full-path"
]
}
},
...
"terminal.integrated.defaultProfile.windows": "MSYS2 CLANG64",
...
"haskell.serverEnvironment": {
"PATH": "C:/msys64/clang64/bin;C:/msys64/usr/local/bin;C:/msys64/usr/bin;$PATH"
, "PKG_CONFIG_PATH": "C:/msys64/clang64/lib/pkgconfig;C:/msys64/clang64/share/pkgconfig"
, "LD_LIBRARY_PATH": "$LD_LIBRARY_PATH"
, "XDG_DATA_DIRS": "C:/msys64/clang64/share;C:/msys64/usr/share"
, "PKG_CONFIG_SYSTEM_INCLUDE_PATH": "C:/msys64/clang64/include"
, "PKG_CONFIG_SYSTEM_LIBRARY_PATH": "C:/msys64/clang64/lib"
, "LANG": "en_US.UTF-8"
, "MINGW_PREFIX": "C:/msys64/clang64"
, "WD": "C:/msys64/usr/bin/"
},
}