nix-shell as #! interpreter for runghc - haskell

I'm trying to use nix-shell as a #! wrapper for runghc, as suggested in the manpage. But it cannot find the libraries. Given the following example, cut-down from the manpage
#! /usr/bin/env nix-shell
#! nix-shell -i runghc -p haskellPackages.HTTP
import Network.HTTP
main = return ()
I get the following result:
[~:0]$ ./nixshelltest
nixshelltest:4:1: error:
Failed to load interface for ‘Network.HTTP’
Use -v to see a list of the files searched for.
[~:1]$
to my mind, that's exactly what nix-shell -p is to avoid.
Am I doing something wrong, missing the point, or is there a bug? This is both on a nixOS 17.03 host, and also a host running nix 17.09 on top of Ubuntu.
Thanks,

The environment that you're using to run the script is missing a step. It's got a GHC and a HTTP package, but the compiler doesn't know about the package.
The way GHC and library packages work in nix might be a little "inside out" from what you're expecting. You actually need to install a compiler that "depends on" all of the libraries you want, rather than simply installing the compiler and the library separately. The reason is that GHC is designed to have library packages added by modifying the file tree where GHC is installed. On a mutable file system with only a single system GHC install you would just modify GHC whenever a library was installed, but nix can't. Nix has to be able to install a frozen GHC that never changes, and potentially many of them.
So what happens instead is that you install a tiny wrapper which depends on the both the underlying "raw" GHC install and all of the libraries that you want to use. The wrapper then acts like an install of GHC that had those libraries registered, without actually needing to duplicate an entire GHC install. When you just install a library package on its own it just sits there inert, without any GHC being able to find it just by it existing.
In fact, the script you've shown here doesn't actually specify that it should have a compiler installed at all; it just asks for the HTTP library. When I tried your script I got command not found: runghc. The runghc is only working on your system because it happened to already be in your path when you ran this (perhaps because you have GHC installed in your profile?), and that GHC wasn't installed with the HTTP package and so can't see it. The nix-shell adding just the library to the environment doesn't help.
What you need to do instead is use this line:
#! nix-shell -i runghc -p "ghc.withPackages (ps: [ ps.HTTP ])"
You're not installing either ghc or HTTP directly; instead the ghc.withPackages function computes a nix package that installs a GHC wrapper that knows about the HTTP Haskell package. Under the hood this depends on a "raw" GHC with no additional libraries, and also on the HTTP library and will cause those to be installed too.
If you use lots of different Haskell environments (possibly via nix-shell scripts like this that each need a different set of libraries), then you will end up with a unique withPackages wrapper installed on your system for each combination of libraries you ever use. But that's okay because the wrappers themselves are tiny, and nix is able to share and reuse the underlying GHCs and library packages between all of those environments.

Related

Why does GHCi understand imports that GHC doesn't?

I'm pretty new to Haskell, and I think I have a fundamental misunderstanding somewhere. When I'm in GHCi (using the ghci command), I can type import System.Random, and it works. I can then generate random numbers.
Next, I make a file called test.hs that contains nothing but one line: import System.Random. I then call the command ghc test.hs and get the following error message:
test.hs:1:1: error:
Could not find module ‘System.Random’
There are files missing in the ‘random-1.1’ package,
try running 'ghc-pkg check'.
Use -v to see a list of the files searched for.
|
1 | import System.Random
| ^^^^^^^^^^^^^^^^^^^^
However, if I go back to GHCi, I can type :load test.hs. This works, and allows me to generate random numbers.
When I run ghc-pkg check, I get only warnings about missing haddock interface files: https://pastebin.com/6a9f0nYZ. From what I understand, this isn't related to the current issue.
Also, when I run ghc-pkg list, random-1.1 is in the list, so random should be installed.
A couple of questions:
Why would GHC and GHCi have access to different imports? Why is the system set up that way? Maybe I just don't understand the relationship between GHC and GHCi.
According to the error message, there are "files missing." How can I figure out which files?
How can I make it so that I can compile Haskell files that use System.Random?
Edit: Both GHC and GHCi are the same version.
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.6.4
$ ghci --version
The Glorious Glasgow Haskell Compilation System, version 8.6.4
Edit: Both ghc and ghci are in /usr/bin/
$ which ghc
/usr/bin/ghc
$ which ghci
/usr/bin/ghci
Update: It looks like this is an Arch Linux peculiarity, not a corrupt package. I've updated my answer accordingly.
GHCi loads "dynamic" versions of modules. By default, GHC links with "static" versions of modules. In particular, when you run:
> import System.Random
under GHCi, it tries to access the file Random.dyn_hi to get interface information for the module. In contrast, when you compile a file with that import statement in it, GHC tries to access the file Random.hi.
You can verify this is the issue by running ghc-pkg field random import-dirs and peeking in the resulting directory. There should be a System subdirectory that usually has two files in it: System.hi and System.dyn_hi. If the former is missing, that's your problem.
Now, it looks like you're probably using Arch Linux. As documented on the Arch Haskell wiki page in the section "Problems with linking", the Arch Haskell community packages (including haskell-random) intentionally omit the static versions of interface files and libraries.
There are several of workarounds given there:
You can use dynamic linking when compiling with GHC. When using GHC directly, this just means passing the -dynamic flag. For Cabal-based projects, instructions are given on that page for modifying your ~/.cabal/config to use dynamic linking for all projects.
You can install the ghc-static and ghc-pristine packages and set up your path and/or Cabal to use the compiler in /usr/share/ghc-pristine/bin/ghc which will maintain its own separate package database that won't interfere with globally installed Haskell community packages, like haskell-random.
You can install ghc-static to get static versions of the base libraries and then run cabal install --force-reinstalls somepackage for all the non-base packages you need. Note that the Wiki notes that this can be tedious and complicated, since you have to manually determine all the package dependencies.
Now, it looks like you already had ghc-static installed, or when invoking GHC you would have also gotten an error about missing files in the base package. You ran cabal install --force-reinstalls random, though as #dfeuer notes, it might have been safer to run:
$ cabal install --force-reinstalls random-1.1
to ensure that the same version was reinstalled.
Anyway, this installed an additional copy of random in your user-specific package directory. If you run:
$ ghc-pkg list
you'll see that random-1.1 is listed both under the global database and user database:
/usr/lib/ghc-8.6.4/package.conf.d
...
random-1.1
...
/home/xxxx/.ghc/x86_64-linux-8.6.4/package.conf.d
random-1.1
and if you run:
$ ghc-pkg describe random
you'll see that it lists the two separate installed versions which is why you now get duplicate fields with ghc-pkg field random import-dirs.
There shouldn't be anything wrong with this. Your user database will take precedence over the global database, so your newly installed version of random will be used when you run GHCi or GHC.
Note that, if you change your mind and want to back out this reinstallation (and maybe try one of the other solutions suggested on the Wiki), you should be able to run:
$ ghc-pkg unregister --user random
Technically, this won't actually remove the package (as the compiled version will still be there under ~/.cabal/lib), but it should otherwise put things back the way they were.

Two cabal packages in a cabal.project and nix-building both (a lib and its example prgs)

I have a haskell lib X having the following file-structure:
X/src/libxfiles.hs
X/default.nix
X/shell.nix
X/libx.nix
X/libx.cabal
X/cabal.project
and
X/example/lib/exampleCommon.hs
X/example/ex1/main.hs
X/example/ex2/main.hs
X/example/example.cabal
cabal.project points to libx.cabal and to example/example.cabal and cabal new-build all works inside nix-shell. The required haskell packages for libx library and example-programs are introduced in the libx.nix -file.
Why this structure? Since the example programs have 99% common code and only a couple of lines that are ui-dependend (webkit2gtk and such). Earlier version had flags and CPP inside the main of example and nix-build produced the lib and example prg in result-directory. On this new setup, the cabal new-build works ok when the compiler is ghc and my aim is to produce programs with ghc.
Earlier nix-build build the lib and one of its example programs with ghcjs.
At the moment, nix-build makes only the lib. Is it somehow possible to tell in the libx.nix or in the default.nix to also build one of the example-programs, say ex1? That is, to tell for the build process to cabal new-build ex1 and then install the result just like it installs the lib.
To use ghcjs, to build the ex1 that is targeted to ghcjs, it is possible to nix-shell --argstr "compiler" "ghcjs" and then cabal new-configure --ghcjs etc. (So, outside nix-shell, the nix-build -command is not doing all I'd like it do.)
Is it required to make a nix-files into the example-directory? Somehow this does not sound that appealing as the required modules are already given in libx.nix. That is, should I do something like in the answer to how to get cabal and nix work together?
Or is there a haskell package in github having similar structure that could work as an example?
Ideal solution would be that nix-build would build the lib and all its example programs. That would require the use of different building tools (ghc and ghcjs). Thus, maybe the question is, how to make a nix-setup for a multipackage cabal-project where individual packages need different environments including the compilation tools. On ideal world, the default nix-build would build all, and user could parameterize the nix-build to build the chosen example or examples etc.
It appears that the discussions in
cabal issue 4646 and in cabal2nix issue 286 are possibly related.

Installing GLUT fails due to missing foreign library - ghc7.8

I tried cabal install GLUT which gave the following:
Setup: Missing dependency on a foreign library:
* Missing C library: glut32
This problem can usually be solved by installing the system package that
provides this library (you may need the "-dev" version). If the library is
already installed but in a non-standard location then you can use the flags
--extra-include-dirs= and --extra-lib-dirs= to specify where it is.
So I thought, ok, lets get the sources and point cabal to the directories. I first tried freeglut and then the following
cabal install GLUT --extra-include-dirs="<path to freeglut>\include"
--extra-lib-dirs="<path to freeglut>\src"
Same thing, so I thought maybe it doesn't work with freeglut, and got glut:
cabal install GLUT --extra-include-dirs="<path to glut>\include"
--extra-lib-dirs="<path to glut>\lib"
When this doesn't work, I try to download the source and cabal install inside the directory, then runghc Setup configure. Then thinking that there is some parse error of the paths, I try every possible way of writing a file path known to man; quotes, no quotes, backslashes, double backslashes, forward slashes, and every combination of the above. I even placed all the files on my PATH in hopes it would find them. All other options exhausted, I proceeded to sacrifice a goat to satan, but still no dice.
The question is, what do I have to do to convince ghc to find this library? (this is windows 7)
You need to make the libglut32.a import library accessible to the compiler (see this answer for information about what import libraries are). Just copy it under $GHCDIR/mingw/lib. Alternatively, try the Haskell Platform installer, which ships with a pre-compiled version of the GLUT bindings.

Using GHC API from GHC compiled from source

I'd like to, within a client program, use the GHC API from a
modified version of GHC. Its easy enough to get the GHC sources
and build and install the modified GHC, but installing the
modified GHC causes all older (Cabal) dependencies to break.
Consequently, I cannot use the GHC API as ghc -v says:
package ghc-7.0.3-... is unusable due to missing or recursive dependencies:...
Any clues on how to proceed?
Thanks!,
Ranjit.
You must build a custom version of GHC first, then install all the packages you need. That way, everything will be linked against your custom GHC, and you will be fine.

Why is package hidden by default? And how can I "unhide" it?

I'm running Ubuntu 10.10, and I have the transformers module installed via the Ubuntu package libghc6-transformers-dev. For some reason, this package is hidden by default:
ghc --make -i./src/ src/fastcgi.hs -o myapp.fcgi
src/MyApp/Webapp.hs:6:7:
Could not find module `Control.Monad.IO.Class':
It is a member of the hidden package `transformers-0.2.1.0'.
Use -v to see a list of the files searched for.
So, my first question is, "why?". And my second question is, what is the proper way to "unhide" this module (without needing to explicitly specify the module via command-line)? And is that a good/bad idea to do?
Note, I am able to get ghc to compile by passing the package name explicitly, like so:
ghc --make -package transformers -i./src/ src/fastcgi.hs -o myapp.fcgi
Use the ghc-pkg tool from the command line:
ghc-pkg expose transformers
Why it was hidden by default I don't know. It may be something to take up with the Ubuntu package maintainers.
Also,
ghc-pkg help
will tell you a lot more about this program.
When building with Stack, add transformers as a dependency to your package.yml file:
dependencies:
- base >= 4.7 && < 5
- transformers
This fixed the problem permanently for me.

Resources