Is there a way to set up my cabal project in such a way that I donât have to rebuild my whenever I want to run at full speed or in profiling mode? Ideally, I would like there to be two executables that build/update what has changed since for that executable since it last run; it seems I have to always rebuild the library if I want to build the other executableâŚ
Currently, I have one cabal file, with one library and two executables. The main flags and ghc options are controlled in cabal.project.* files.
I think if you use the profiling: directives in your cabal.project.* file, cabal should put the two different modes in two different directories enabling separate caching. If you do change the ghc-options then cabal does not do separate caching.
Ah, thanks! Thatâs a bit of pain, howeverâŚ
Say I have cabal.project.local:
import: ./cabal.project
ignore-project: False
optimization: 2
package package1
ghc-options: "-with-rtsopts=-N -s"
and cabal.project.prof:
import: ./cabal.project
ignore-project: False
optimization: 2
package package1
profiling: true
profiling-detail: late
ghc-options: -finfo-table-map -fdistinct-constructor-tables -fno-prof-count-entries "-with-rtsopts= -l -pj -hc -N -s"
program-options
ghc-options:
-dsuppress-all
-dsuppress-uniques
-ddump-deriv
-ddump-simpl
-ddump-stg-final
-ddump-to-file
-dumpdir=dumps
To my mind, the most sensible thing to do is to put the ghc-options as pragmas in the files themselves? How would I ensure that I only dump the core and stg whenever I do profiling runs?
Am I understanding it correctly that youâre compiling one of your dependencies with profiling, but not the main executable? Why is there no profiling: true at the top level in cabal.project.prof?
I want to compile the whole package(?). My understanding is the package contains my libraries and executables and I would like away to apply different settings to different executables.
Lack of knowledge, I didnât know it was supposed to go there
(itâs been a journey even using cabal.project files!! I used to stick everything in project.cabal until it stopped working for profiling and info tables didnât get filled properly!)
To be clear, I want to have the following kind of behaviour (if it is possible):
$ cabal run --project-file=cabal.project.local exe1 # Builds and runs exe1
then later on, when I do
$ cabal run --project-file=cabal.project.prof exe1-profile # Builds and runs exe1-profile
but it does so without requiring a full rebuild of the library and executable because I ran the exe1 build some time in between: rather it increments on the last time I ran exe1-profile.
I use ghci on Windows. Its most annoying (lack of) feature, that slows me down the worst, is I canât use cutânâpaste keys. Instead I have to dive around the menu.
Nice article! I appreciate the build optimization section!
One of the nicest tricks I know for working effectively with ghci (or cabal repl) is defining this custom âcommandâ:
$ cat ~/.ghci | fgrep 'def x '
:def x \expr -> return $ unlines [":!clear", ":reload", expr]
This way, when iterating on a particular bit of code, you can :
- editor: change a bit of code
- ghci: press the up arrow then enter to observe the change
- editor: change a bit of code
- ghci: press the up arrow then enter to observe the change
- and so onâŚ
This requires an initial setup where you load the required modules properly and initiate a proper function call with the required args, say:
ghci> myFunc 1 2 3
Then you can simply prefix the REPL expression with :x to initiate the âREPL driven development loopâ described above, so :
ghci> :x myFunc 1 2 3
Itâs been working very well for me. âXâ as in âscratch that, letâs try againâ ![]()
Iâm happy to see that the blog post generated real-world impact!
Dominik has reported build speedups of ~2.2x. Very nice!
Speaking of these things, some 15 years ago I set up :L to load the currently edited file. I just have vim write that to a known place on BufEnter. Then the other major productivity bonus is to alias :R to :r, because I canât get my finger off the shift key fast enough half the time. I also have :load add a :module +D, which puts some pretty-printing functions into scope.
Since I run tests from ghci and use a framework where they are plain IO () actions, the usual loop is to edit, and then up arrow return the test_whatever function. I have a :K alias that does a reload and rerun all at once. Itâs from a DSL that produces text output and has the side-effect of saving the output to a file. So I can do :K to reload and rerun, and then diff to show the diffs with the output of the last time I ran it. I could have the editor send these over automatically on save ala ghcid via tslime.vim which sends text to other tmux windows, but so far havenât gotten around to it.
For functions under test, I have a habit of just assigning them in the file as eg t0 = f 1 2 3, since this is persistent across reloads. Then if they are still interesting I might copy paste them into a test.