Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a Makefile with cabal?

I'm working on a toy project as a way to move my enjoyment of Haskell from theoretical to the practical, make myself more comfortable with cabal, HUnit, etc.

I just added a Makefile to my project:

test: dist/build
  cabal-dev test

dist/build: dist/setup-config src/*.hs tests/*.hs
  cabal-dev build
  touch dist/build

dist/setup-config: ToyProject.cabal
  cabal-dev configure --enable-tests

Because:

  • cabal-dev install --enable-tests seemed like overkill (and was warning me about re-installs)
  • cabal-dev configure --enable-tests && cabal-dev build && cabal-dev test was doing unnecessary work, and keeping state about whether I needed to reconfigure was boring
  • and both were a lot of typing

I'm concerned that I might be recreating functionality with Make that cabal or cabal-dev already gives me, but I'm not familiar enough with either to know whether that is true, and if it is, how I'd do it.

Is a Makefile appropriate here, or is there a more direct way to do this just using cabal/cabal-dev?

like image 994
rampion Avatar asked Sep 06 '13 03:09

rampion


1 Answers

Below is a simplified version of a Makefile I use with a Cabal package I'm developing in parallel with another Haskell program that depends on the Cabal package (I often edit them in parallel and so I have the Cabal package as a build dependency of the program, using another Makefile :P). The goals are:

  1. Only run cabal if some source files have actually changed. I use this Makefile on a very slow netbook where the Cabal dependency resolution step takes 10's of seconds, so I want to avoid running cabal install at all if possible.

  2. Have independent debugging and regular builds in separate build dirs. By default, if you do a Cabal build with profiling (--ghc-options=-fprof-auto), and then one without profiling, Cabal will start over, recompiling all your files from scratch. Putting the builds in separate build dirs avoids this issue.

I'm not sure (2) is of interest to you, but (1) probably is, and I see that you only touch the build dir on success, not failure, and I expect that won't work correctly.

Here is the Makefile:

cabal-install: dist

cabal-install-debug: prof-dist

# You will need to extend this if your cabal build depends on non
# haskell files (here '.lhs' and '.hs' files).
SOURCE = $(shell find src -name '*.lhs' -o -name '*.hs')

# If 'cabal install' fails in building or installing, the
# timestamp on the build dir -- 'dist' or 'prof-dist', stored in
# the make target variable '$@' here -- may still be updated.  So,
# we set the timestamp on the build dir to a long time in the past
# with 'touch --date "@0" $@' in case cabal fails.
CABAL_INSTALL = \
  cabal install $(CABAL_OPTIONS) \
  || { touch --date "@0" $@ ; \
       exit 42 ; }

dist: $(SOURCE)
    $(CABAL_INSTALL)

# Build in a non-default dir, so that we can have debug and non-debug
# versions compiled at the same time.
#
# Added '--disable-optimization' because '-O' messes with
# 'Debug.Trace.trace' and other 'unsafePerformIO' hacks.
prof-dist: CABAL_OPTIONS += --ghc-options="-fprof-auto" --builddir=prof-dist --disable-optimization
prof-dist: $(SOURCE)
    $(CABAL_INSTALL)
like image 197
ntc2 Avatar answered Oct 04 '22 19:10

ntc2