How do I stop GHC from recompiling modules when nothing has changed? - haskell

I have the following directory structure:
libgs - The basic Global Script abstract machine and standard library
gsi - A Global Script interpreter
gs2hs - A Global Script to Haskell compiler
Most, but not of course all, of this code is written in Global Script, and translated to Haskell by a Haskell program, hsgs2hs.
As such, the code in gsi and gs2hs both depend on the modules from libgs.
Because of somewhat sloppy code organization on my part, the compiler in gs2hs also depends on the front-end modules (parser, type-checker, etc.) from the gsi directory.
Legal aside: If it matters: my code is freely available online, but is not open-source, and its license does not permit redistribution through Hackage. End legal aside.
I can make this directory structure work by running
ghc --make -i../libgs gsi.hs -o gsi
in the gsi directory, and
ghc --make -i../libgs -i ../gsi gs2hs.hs -o gs2hs
in the gs2hs directory.
This has the problem that, every time I do both builds in sequence, GHC recompiles every single module in the libgs directory, and every shared module in the gsi directory, telling me 'flags changed'.
I figure, ok, I should probably be using packages for re-used code in Haskell, right? So I convert libgs to a package:
Add a libgs.cabal file to that directory, listing all the modules as exposed modules.
Add a libgs/install-all script that runs cabal install --lib --package-env $REPO_ROOT/package.env . and call it before building gsi and gs2hs
Add -package-env $REPO_ROOT/package.env to the GHC flags in the gsi and gs2hs directories.
No joy!
Now, any change to libgs at all - even just adding a new module to it - causes GHC to recompile every module in the gsi directory, telling me the fundamental module GSI.Value has changed. Even though it actually hasn't; the source code for that module and everything it depends on (which isn't much) is unchanged. Just the hash for the package it's coming from has changed.
How do I stop GHC from recompiling the world constantly, and get it to only recompile things when the result can actually be different?

Add a libgs.cabal file to that directory, listing all the modules as exposed modules.
Ok, good.
Add a libgs/install-all script that runs cabal install --lib --package-env $REPO_ROOT/package.env
Don't do that. As a general rule, never use install --lib. It's usually better to let Cabal figure out when to install libraries. The easiest way is to put the executables in the package itself. You can have both a library: section in the .cabal file as well as arbitrarily many executable: gsi and executable: gs2hs ones.
Alternatively, you can keep libgs a package that doesn't care about executables, but have these in their own package each. Then you don't do any builds in the package directories themselves, but instead put a cabal.project file in your main src directory, saying
packages: ./libgs ./gsi ./gs2hs
Then run cabal new-build in that directory. It'll collectively store the require object files in its .dist-newstyle directory.

Related

How can I run GHCi against a compiled package?

I should really know this by now, but I don't. I'm often working on a Cabal-based package and have just run a successful cabal build. Now I want to try some things out in GHCi. If I run cabal repl, then GHC recompiles the whole package into bytecode and runs it in the interpreter. Not what I want at all! If I were just running GHCi directly, I'd use something like -O -fobject-code, but that won't give me the package context. I just want "Give me a repl with the package as it's been compiled, compiling additional things only as necessary." How do I do it?
I don't know the right way, but I do know a workaround that can sometimes be useful. If the thing you care about is a library component, you can ask for a repl for an executable component.
I believe --repl-options -fobject-code kind of does what you want:
cabal repl --repl-options -fobject-code --repl-options -O --builddir dist-repl
This will give you incremental building of compiled code as you work in GHCi. Caveats:
dist-repl is an alternative directory for the -fobject-code build objects. As of cabal 3.6.2.0 at least, trying to reuse the regular output from cabal build leads to some unnecessary rebuilds and other strange behaviour, as reported at cabal issue #3565. That being so, it's better to compromise and use --builddir to keep a separate set of build objects. Note that cabal clean accepts the --builddir option just fine.
Setting the optimisation level explicitly is necessary, as otherwise the default -O0 from cabal repl will override your package setting.

Cabal install tidal ends with warning

I'm trying install tidal in command line this way:
cabal install tidal
but it ends with this message:
Warning: You asked to install executables, but there are no executables in
target: tidal. Perhaps you want to use --lib to install libraries instead.
Return of:
cabal install tidal --lib
is:
Resolving dependencies...
Up to date
If I check ghk-pkg list, there is no package tidal
...
Have somebody similar problem or what I'm doing wrong?
My environment is:
Windows 10 Education
Haskell 8.4.3
Cabal 3.2.0.0
Ghc 8.10.1
Thank you for help.
Like Stack for a longer time, Cabal-install does now (as of 3.2) not really install libraries anymore – in the sense of, change the computer's state so that GHC can access the library on it†. Both tools only install executables now. It used to do that for libraries too, but that was stopped with the now default Nix-style builds.
Now (and, really, also already before), the way to use a library is instead to just depend on it, and let Cabal figure out behind the scenes if it needs to be installed. I.e., you add a .cabal file to your .hs source file with build-depends: tidal in it. Then when you say cabal install ., it will first download and install the library before then using it for building your own executable.
†Of course both Stack and Cabal do technically speaking install libraries, just they don't globally register them. I.e., cabal knows where it has installed the library, but you're not really supposed to know about that. It's in the spirit of continuous integration: if your code builds now with the particular state of libraries you happen to have installed, that's not very reliable. If it builds with just those libraries that are explicitly listed in a project file, the chances are much better that future-you (or somebody else) will still be able to use your code on another computer without hours of figuring out what libraries to install first.
cabal install --lib tidal doesn't install the library binaries in a location managed by ghc-pkg. The binaries remain in the Cabal "store".
What it does is to create a plaintext GHC package environment file that is picked up by standalone invocations of ghc and ghci and tells them where to look for the extra libraries.
By default (as mentioned in the docs) this package environment file will be created at ~/.ghc/$ARCH-$OS-$GHCVER/environments/default and will be picked by ghc and ghci invocations made anywhere.
We can also supply an extra --package-env parameter to create the environment file in a local folder, which will only affect ghc and ghci invocations made in that folder. For example:
cabal install --lib --package-env . tidal
cabal projects themselves ignore environment files, as their package environments are constructed from the build-depends section of the cabal file for the sake of reproducibility. But environment files are useful for not having to create a cabal project in the first place, if you only need it for playing with the library in ghci, or if you are compiling simple programs using ghc only.

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.

Force GHC using local files

I'm making some "experiments" on a haskell module and I have a problem with a source file I wish to modify.
I have many reasons to think that GHC seek the installed (with cabal) library on my system and not the local sources files.
I deleted the *.o files locally and the other source files in this module are not rebuild by GHC.
Can I force GHC to use the local sources files of a module or ignore an installed module in particular?
Yes, use ghc -hide-package evil-package. Or you can hide the package temporarily with ghc-pkg hide evil-package, and then undo it later with ghc-pkg expose evil-package.

How to stop GHC from generating intermediate files?

When compiling a haskell source file via ghc --make foo.hs GHC always leaves behind a variety of intermediate files other than foo.exe. These are foo.hi and foo.o.
I often end up having to delete the .hi and .o files to avoid cluttering up the folders.
Is there a command line option for GHC not to leave behind its intermediate files? (When asked on #haskell, the best answer I got was ghc --make foo.hs && rm foo.hi foo.o.
I've gone through the GHC docs a bit, and there doesn't seem to be a built-in way to remove the temporary files automatically -- after all, GHC needs those intermediate files to build the final executable, and their presence speeds up overall compilation when GHC knows it doesn't have to recompile a module.
However, you might find that setting the -outputdir option will help you out; that will place all of your object files (.o), interface files (.hi), and FFI stub files in the specified directory. It's still "clutter," but at least it's not in your working directory anymore.
GHC now has the options no-keep-hi-files and no-keep-o-files. See here for more information.
My usual workflow is to use cabal rather than ghc directly. This sets the outputdir option into an appropriate build folder and can do things like build haddock documentation for you. All you need is to define the .cabal file for your project and then say cabal install or cabal build instead of run ghc directly. Since you need to follow this process in the end if you ever want to share your work on hackage, it is a good practice to get into and it helps manage package dependencies as well.
You can set the -hidir to /dev/null, I think, sending them there. Also, the -fno-code option in general turns off a lot of output. You might just want to use Cabal.
Turns out that using -hidir/-odir/-outputdir is no good; /dev/null is a file, and not a directory. See http://www.haskell.org/pipermail/xmonad/2010-May/010182.html
2 cents to improve the workflow a bit:
We can put the following alias into the .bashrc (or similar) config
file:
alias hsc='_hsc(){ ghc -no-keep-hi-files -no-keep-o-files "$#";}; _hsc'
And then just call
$ hsc compose.hs
[1 of 1] Compiling Main ( compose.hs, compose.o )
Linking compose ...
$ ls
compose compose.hs

Resources