Fixing a broken cabal update
Struggling with build tools is maybe my least favorite part of programming, although working with encodings and environment variables is a close second. Today’s blog post involves all three!
Not really, though,The machine this was a problem on was running Ubuntu 18.04.5 LTS (“Bionic Beaver”). Oooh, someone needs to update their Ubuntu. That someone would be me. because Nix has relieved me from thinking about the environment variables.
Anyway, our story begins with an attempt to run
cabal update. I was getting this error message:
$ cabal update Downloading the latest package list from hackage.haskell.orgoutput of /usr/bin/wget: hGetContents: invalid argument (invalid byte sequence)
I checked my version of
cabal-install; it was the latest version (184.108.40.206).
I searched for the error message. A couple of Stack Overflow posts and GitHub issues were about similar (but not exactly the same) issues and seemed helpful, but attempts to do as they suggested did not resolve the issue.
So we tried to figure out what to do. The parenthetical about an “invalid byte sequence” and some of the comments on those posts I read suggested it was probably due to an environmental variable on this machine, perhaps related to the encoding.
But because I am a Nix userI use NixOS on my laptops, but my desktop runs Ubuntu with the Nix package manager. It can be confusing since the word “Nix” often refers to different things., a Nix-based solution presented itself.
I could run the
cabal update command within a Nix shell, and Nix will set the environment variables appropriately for the program and command it is running within the Nix shell where it is running the command, but without affecting the settings of any environment variables outside this context.
So, the solution to my problem was this command.
$ nix-shell --pure -p cabal-install --run "cabal update"
Let’s break that down:
--pureflag clears the Nix shell of any environment variables that might be set in my shell. Normally, when you invoke
nix-shell, if there is a
shell.nixfile in the current working directory, Nix builds the necessary dependencies and prepares the environment for those packages, inheriting some environment variables from the parent shell. Opening a Nix shell with the
--pureflag clears the Nix shell of all inherited environment variables. It purifies the Nix environment, so to speak.
-pflag is the short version of
--packagesand is followed by its argument. In this case the package we want in our Nix shell is
--runflag runs the specified command (given in quotes following the flag) and then exits the shell. Usually a Nix shell is an interactive shell so you can open it and run different commands within the shell, but invoking the shell this way opens a noninteractive shell that just runs this command.
As I said, the Nix shell sets the environment variables within the shell appropriately for whatever packages it’s dealing with (and, again, this is without affecting their settings globally). You can look at the environments outside and inside the Nix shell, if you like, to see the changes.
For example, on this machine, if I run
env in my normal Bash shell, I get some output that looks like this (although I’m leaving a lot out here for the sake of brevity).
$ env CLUTTER_IM_MODULE=xim LS_COLORS= < lotta letters and numbers in here > LESSCLOSE=/usr/bin/lesspipe %s %s XDG_MENU_PREFIX=gnome- LANG=en_US.UTF-8 DISPLAY=:1< ... and so on ... >
But if I run
env in a Nix shell instead, it looks considerably different. For example, this is the
env for a Nix shell with the
$ nix-shell --pure -p cabal-install [nix-shell:~/twitter]$ env HOST_PATH= < a big path > LS_COLORS= < lotta letters and numbers, again > LESSCLOSE=/usr/bin/lesspipe %s %s propagatedBuildInputs= stdenv=/nix/store/333six1faw9bhccsx9qw5718k6b1wiq2-stdenv-linux __ETC_PROFILE_SOURCED=1 DISPLAY=:1 out=/nix/store/k1h4df0jswivi8a9yfh9nds2vvssai7g-shell CONFIG_SHELL=/nix/store/2jysm3dfsgby5sw5jgj43qjrb5v79ms9-bash-4.4-p23/bin/bash buildInputs=/nix/store/1jpb9qg7ah9s2gj160y1vn7iwn0r6gy8-cabal-install-220.127.116.11 builder=/nix/store/2jysm3dfsgby5sw5jgj43qjrb5v79ms9-bash-4.4-p23/bin/bash< ... and so on ... >
And if I
grep for that
LANG variable that showed up in my Bash shell environment, it’s no longer there in the Nix shell environment, which is configured specifically for this package and its needs.
Now, I don’t know that it’s the
LANG variable that’s causing this problem with my
cabal update but I do know that running the
update command within this Nix shell fixed my problem.
Perhaps someday this post will help someone else figure out how to use a Nix shell to fix this problem or a similar problem or even write something new about their own problems. That’s the beauty of the internet.