When does cabal recompile a module which contains Template Haskell? - haskell

I understand that cabal will recompile a module if the interface of any of its dependencies has changed. It seems that this simple rule does not hold if the module contains Template Haskell. In that case, even just adding a trailing newline character to a file in the module's (transitive) dependencies will cause the cabal to recompile the file.
Minimal example:
file: Foo.hs
module Foo where
foo = "foo"
file: FooTH.hs
{-# LANGUAGE TemplateHaskell #-}
module FooTH where
import Data.Bifunctor.TH
import Foo
data FooPair a b = FooPair a b
$(deriveBifunctor ''FooPair)
file: MCVE.cabal
name: MCVE
version: 0.1.0.0
synopsis: MCVE
license: MIT
license-file: LICENSE
author: tarleb
maintainer: tarleb#example.com
build-type: Simple
extra-source-files: CHANGELOG.md
cabal-version: >=1.10
library
exposed-modules: Foo
, FooTH
build-depends: base >=4.8 && <4.13
, bifunctors
default-language: Haskell2010
Adding a newline to Foo.hs, e.g. by running echo "\n" >> Foo.hs, will cause recompilation of module FooTH. This ceases to happen if the TH line in FooTH is commented out.
What is the reason for this, and is there a way to avoid this unnecessary recompilation?

There's a feature in Template Haskell called addDependentFile, which adds metadata to the .hi file indicating that the source file in question depends on another file as well. To my knowledge, Cabal will always ask GHC to try to build, though it may have more intelligent logic. Stack tries to bypass that process, and has logic to parse the addDependentFile information out.

Related

Set up Haskell Project and run tests

I'm trying to set up a Haskell project (library) with tests that I can use to work through The Haskell Road to Logic, Maths, and Programming. There are three parts that I'd like to have:
The code that comes with the book, in a subdirectory
The code I write for exercises in the book; one file per chapter
The code I write for tests; one file per chapter
I have attempted a project setup here, but am getting the following cabal error:
Resolving dependencies...
Configuring haskell-road-0.1.0.0...
Building haskell-road-0.1.0.0...
Failed to install haskell-road-0.1.0.0
Build log ( /Users/stuart/.cabal/logs/haskell-road-0.1.0.0.log ):
cabal: Entering directory '.'
Configuring haskell-road-0.1.0.0...
Building haskell-road-0.1.0.0...
Preprocessing library haskell-road-0.1.0.0...
src/Chapter1.hs:1:1:
File name does not match module name:
Saw: ‘Main’
Expected: ‘Chapter1’
cabal: Leaving directory '.'
cabal: Error: some packages failed to install:
haskell-road-0.1.0.0 failed during the building phase. The exception was:
ExitFailure 1
I'd like to be able to run $ cabal test and have all of the tests run, and have the import paths work. Any help is appreciated. I think there are probably issues with the test structure, but I've had trouble finding definitive guides on the actual setup.
EDIT: More details
src/
Chapter1.hs
Book/
GS.hs
etc....
test/
Chapter1Test.hs
MainTestSuite.hs
TestHelper.hs
haskell-book.hs:
-- Initial haskell-road.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: haskell-road
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
-- synopsis:
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: MIT
-- The file containing the license text.
license-file: LICENSE
-- The package author(s).
author: Stuart Terrett
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: shterrett#gmail.com
-- A copyright notice.
-- copyright:
-- category:
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
library
-- Modules exported by the library.
exposed-modules: Chapter1, Book.COR, Book.DB, Book.FAIS, Book.FCT, Book.GS, Book.Hierarchy, Book.IAR, Book.Nats, Book.POL, Book.Polynomials, Book.PowerSeries, Book.Query, Book.REL, Book.SetEq, Book.SetOrd, Book.STAL, Book.TAMO, Book.TUOLP, Book.WWN
-- Modules included in this library but not exported.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
other-extensions: FlexibleInstances
-- Other library packages from which modules are imported.
build-depends: base, random >=1.1 && <1.2, HUnit >=1.3 && <1.4
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
test-suite haskell-road-tests
type: exitcode-stdio-1.0
hs-source-dirs: tests, src
main-is: MainTestSuite.hs
build-depends: base,
HUnit,
QuickCheck,
test-framework,
test-framework-hunit,
test-framework-quickcheck2
MainTestSuite.hs
import Chapter1Test
exitProperly :: IO Counts -> IO ()
exitProperly m = do
counts <- m
exitWith $ if failures counts /= 0 || errors counts /= 0 then ExitFailure 1 else ExitSuccess
allTests::[Test]
allTests = [Chapter1Test.itRuns]
main :: IO ()
main = exitProperly (runTestTT (TestList allTests))
Diff of all changes:
http://lpaste.net/5997592404872396800
Specific changes you need to make:
In Chapter1.hs make sure module Chapter1 appears before the import statement:
module Chapter1 where
import ...
In each of the Book modules you need to add the prefix Book. to
each of the module statements, e.g. in Book/COR.hs:
change: module COR
to: module Book.COR
Also, any import statement will also need the Book. prefix, i.e. in Book/STAL.hs:
change: import DB
to: import Book.DB
(It might easier just to leave the book's modules at the top-level of the module name space.)
To fix this compilation error:
src/Book/IAR.hs:131:7:
No instance for (Foldable t3) arising from a use of ‘foldr’
just add {-# LANGUAGE NoMonomorphismRestriction #-} to the top of Book/IAR.hs (it should be the very first line.)
To fix this compilation error:
src/Book/FAIS.hs:14:4: Parse error in pattern: n + 1
change: f (n+1) = True : f n to f n = True : f (n-1).
This is called an n+k pattern and more info about it (and why it has been deprecated) is available here: What are "n+k patterns" and why are they banned from Haskell 2010?
In the test-suite section you have:
hs-source-dirs: tests, src
To use the code in the src directory you tests should depend on the haskell-road library instead of compiling the source code. That is, use these lines in the test-suite section:
hs-source-dirs: tests
build-depends: base, haskell-road, HUnit, ...
File test/Chapter1Test.hs needs a module statement:
module Chapter1Test where
and also fix this import statement:
-import TestHelper.testCase
+import TestHelper (testCase)
File test/MainTestSuite.hs needs these import statements:
import System.Exit
import Test.HUnit
File test/testHelper.hs needs to be renamed to test/TestHelper.hs
and also needs this import statement:
import Test.HUnit
Cabal has
developing packages link and cabal file content structure is described there.
By looking the error message, it seems that your haskell library source file Chapter1 starts with module Main where. It should contain module Chapter1 where, as the error message says.
Libraries should not contain main while the test-executables should, which is why you state in the cabal file the test executables with main-is.
Hope this helps! (I didn't look at the github sources, just the error message.)

Shared cabal "build-depends" (Haskell) [duplicate]

Here's a .cabal file:
Name: myprogram
Version: 0.1
-- blah blah blah
Cabal-version: >=1.9.2
Executable myprogram
HS-source-dirs: src
Main-is: Main.hs
Build-depends: attoparsec == 0.10.*,
base == 4.3.*,
-- long long list of packages
Test-Suite test
HS-source-dirs: test, src
Type: exitcode-stdio-1.0
Main-is: Main.hs
Build-depends: attoparsec == 0.10.*,
base == 4.3.*,
-- long long list of packages
QuickCheck == 2.4.*
Is there any way I can replace the long list of build-depends packages for the test suite with "same as for the executable, plus QuickCheck"?
Edit: version information.
cabal-dev 0.9
cabal-install 0.10.2
Cabal library 1.10.2.0
GHC 7.0.4
Haskell Platform 2011.4.0.0
NOTE: superseded by phadej's answer suggesting common stanzas.
Is there any way I can replace the long list of build-depends packages for the test suite with "same as for the executable, plus QuickCheck"?
Not that I know of. However, there is a way to only mention the list of build-depends packages once, by structuring your project into three targets:
a library that contains all your code, and needs the long build-depends list.
an executable that consists of only one file, and depends on base and the library from above.
a test-suite that depends on the library from above, and the testing packages you are using.
Maybe this approach is what indygemma's answer proposes, but the Cabal file proposed there will not quite achieve it, as Norman Ramsey points out in a comment. Here's the main points of what you need in a Cabal file. For a full example that works for me, you can look at this Cabal file.
name: my-program
version: ...
library
hs-source-dirs: src-lib
build-depends: base, containers, ...
exposed-modules: My.Program.Main, ...
executable my-program
hs-source-dirs: src-exec
main-is: my-program.hs
Build-depends: base, my-program
test-suite tests
type: exitcode-stdio-1.0
hs-source-dirs: src-test
main-is: tests.hs
other-modules: ...
build-depends: base, my-program, test-framework, ...
Important points:
There are three separate source directories for the three targets. This is necessary to stop GHC from recompiling library files when building the other targets.
All of the application code is in the library. The executable is just a wrapper, like this:
import My.Program.Main (realMain)
main = realMain
The library exposes all modules that are necessary for testing.
The last point highlights the drawback of this approach: You end up having to expose internal modules. The main benefit of this approach is that you have less duplication in the Cabal file, and maybe more importantly, less duplication in the build process: The library code will be built only once, and then linked into both the executable and the test-suite.
Since version 2.2 Cabal supports common stanzas, to dedup build info fields:
https://cabal.readthedocs.io/en/latest/developing-packages.html#common-stanzas
cabal-version: 2.2
name: myprogram
version: 0.1
-- blah blah blah
common deps
build-depends: base ^>= 4.11,
-- long long list of packages
ghc-options: -Wall
library
import: deps
exposed-modules: Foo
test-suite tests
import: deps
type: exitcode-stdio-1.0
main-is: Tests.hs
build-depends: foo
You could also consider using hpack instead of writing the .cabal file by hand:
In hpack's package.yaml format, you can specify a common dependencies field whose entries are added to every components' build-depends field when generating the .cabal file.
For example, see hpack's own package.yaml and the generated hpack.cabal.
To start using hpack with an existing package, you can use hpack-convert which will generate the package.yaml from an existing .cabal file.
To create a new package that uses hpack, you can use stack's simple-hpack template like so: stack new mypkg simple-hpack.
If you use stack for development, you don't have to call hpack manually to regenerate the .cabal file from an updated package.yaml – stack will do that automatically.
No easy way:
you can use m4 and specify your dependencies once, but then you will need to reprocess your Cabal file through m4 whenever you change it.
you can move the code you are testing out to a library, and then specify the library in your Build-depends for the test. That requires you to install a library even just to run the test.
You can just not put the test in the cabal file at all. Build it with ghc --make, which will pull in dependencies. But then you lose cabal integration.
There is an optional library section for .cabal files, which solves your problem.
name: myprogram
version: 0.1
-- blah blah blah
cabal-version: >=1.9.2
library
build-depends: attoparsec == 0.10.*
, base == 4.3.*
-- long long list of packages
executable myprogram
hs-source-dirs: src
main-is: Main.hs
test-suite test
hs-source-dirs: test, src
type: exitcode-stdio-1.0
main-is: Main.hs
build-depends: QuickCheck == 2.4.*

plugins package unknown symbol when using cabal

I'm messing around with the plugins package however I bumped into a problem.
Here's the code:
Util/Header.hs
module Util.Header(PT(..)) where
data PT a = PT a deriving Show
Plug.hs
module Plug(helloPlugin) where
import Util.Header
helloPlugin :: PT Int
helloPlugin = PT 1
Main.hs
module Main where
import Util.Header
import System.Plugins
main :: IO ()
main = do
mv <- load "Plug.o" ["."] [] "helloPlugin"
case mv of
LoadFailure msg -> print msg
LoadSuccess _ v -> print $ show (v :: PT Int)
This all works fine then compiling with ghc. Building with Cabal works fine as well, but when I run the executable I get this error:
plugintest: /home/kevin/.cabal/lib/plugins-1.5.4.0/ghc-7.6.3/HSplugins-1.5.4.0.o: unknown symbol `ghczm7zi6zi3_ErrUtils_zdsinsertzuzdsgo5_info'
plugintest: user error (resolvedObjs failed.)
My very minimalistic cabal file:
name: plugintest
version: 0.1.0.0
license-file: LICENSE
build-type: Simple
cabal-version: >=1.8
library
hs-source-dirs: src
exposed-modules: Util.Header
build-depends: base ==4.6.*, plugins ==1.5.*
executable plugintest
main-is: Main.hs
build-depends: base ==4.6.*, plugins ==1.5.*, plugintest == 0.1.0.0
hs-source-dirs: src
Now I assume the problem is that it can't find the "ErrUtils" module which is part of the ghc package installed in /usr/lib/ghc-7.x.x.
Since it's using cabal it'll use the $HOME/.cabal/lib/ instead.
Now I obviously wouldn't want to use /usr/lib if I wanted to make it distributable. Sadly I'm not very familiar with how packages are managed nor am I familiar with the plugins package.
I have a feeling this is extremly nooby but I wasn't able to find a solution myself.
So a few questions:
How can I get my dependencies to work in a way to make this distributable?
It seems I'll need to know beforehand what my Plugin.o files will depend on before actually being able to use them (If I understand correctly).
Is there a way to package a .o files that I wouldn't have to worry about this problem? (Sorry if this question is too vague, feel free to ignore)
Thanks in advance!
Ok, so I had the exact same problem.
Here is a workaround I found
Change the load call to
load "Plug.o" [".","dist/build/plugintest/plugintest-tmp"] [] "testplugin"
Make sure you compile the thing with -c or by using the "make" library from plugins.
Quite annoyed by this... The error suggests it is having issues linking against the standard libs, so why does showing it these .o files fix it?
Anyways, this worked for me, and didn't require a ton of mucking around with .cabal files.
You must declare your exported- and other- modules in order for Cabal to package them all together. For instance (from https://github.com/tel/happstack-heroku-test)
name: hktest -- note the name here names
-- the *library* which is a package name
-- in scope when building the executable
...
library
exposed-modules:
HKTest
other-modules:
-- there aren't any, but there could be some
build-depends: base >= 4.6 && <4.7
...
, mtl >= 2.1.2
hs-source-dirs: src
executable server
main-is: Server.hs
other-modules:
-- there might be some use to having these here,
-- but they'll be harder to get into GHCi, so I wouldn't
-- recommend it---just put them in the library part
build-depends: base >=4.6 && <4.7
, hktest -- note that I grab all the hktest
-- modules here
hs-source-dirs: exe
If I leave out one of those modules I'll likely get a build error as Cabal compiles files which expect to be able to find symbols that haven't been packaged.
In your case, since you're building an executable, the common pattern exemplified above is to put all of your code into a library and then have the executable side depend upon that library. For instance, in this example the complete text of exe/Server.hs is
module Main where
import qualified HKTest as HK
main :: IO ()
main = HK.main

Automatically List Dependencies For a Project

Given a Haskell project, is there a way to automatically calculate the entire list of dependencies? All the libraries it depends on as well as libraries that have been included but are not required.
As I said in the comments, cabal-install already does this (I'm using cabal-install 0.14.0) by guessing the packages via module lookup (like GHCi). It doesn't have any real intelligence w.r.t. versions so it just sets the version to the match major version of what you have installed.
Below you can see me making a dummy package that imports Data.Vector and cabal-install infers I am using vector 0.9.*.
[tommd#mavlo blah]$ pwd
/tmp/blah
[tommd#mavlo blah]$ cat Data/Blah.hs
module Data.Blah where
import Data.Vector
[tommd#mavlo blah]$ cabal init
Package name? [default: blah]
...SNIP...
What does the package build:
1) Library
2) Executable
Your choice? 1
Include documentation on what each field means (y/n)? [default: n]
Guessing dependencies... <--- SEE, SEE! YAY!
Generating LICENSE...
Warning: unknown license type, you must put a copy in LICENSE yourself.
Generating Setup.hs...
Generating blah.cabal...
You may want to edit the .cabal file and add a Description field.
[tommd#mavlo blah]$ cat blah.cabal
-- Initial blah.cabal generated by cabal init. For further documentation,
-- see http://haskell.org/cabal/users-guide/
name: blah
version: 0.1.0.0
synopsis: Sisponys
-- description:
-- license:
license-file: LICENSE
author: Me
maintainer: No#No.No
-- copyright:
-- category:
build-type: Simple
cabal-version: >=1.8
library
exposed-modules: Data.Blah
-- other-modules:
build-depends: base ==4.5.*, vector ==0.9.* <-- SEE?? SEE! YIPPEE!!

Using alex/happy with Cabal

I'm writing a compiler for a class I'm taking. The class isn't specifically Haskell but I'm using Haskell to write my compiler and interpreter. I have a cabal package setup to hopefully make it easy for my prof to run/compile. I have happy and alex in the build-tools field for both executables but Cabal ignores that and then complains that it cannot find the modules that Happy and Alex should be generating. If I manually run:
alex LimpScanner.x
happy LimpParser.y
then cabal runs perfectly.
I thought I had cabal automatically running them earlier but perhaps I remember imperfectly.
limp.cabal:
-- limp.cabal auto-generated by cabal init. For additional options,
-- see
-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.
-- The name of the package.
Name: limp
-- The package version. See the Haskell package versioning policy
-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
-- standards guiding when and how versions should be incremented.
Version: 0.1
-- A short (one-line) description of the package.
Synopsis: LIMP Compiler (Compiler Construction course project)
-- A longer description of the package.
-- Description:
-- URL for the project homepage or repository.
Homepage: http://www.cs.rit.edu/~eca7215/limp/
-- The license under which the package is released.
License: AllRightsReserved
-- The file containing the license text.
License-file: LICENSE
-- The package author(s).
Author: Edward Amsden
-- An email address to which users can send suggestions, bug reports,
-- and patches.
Maintainer: eca7215#cs.rit.edu
-- A copyright notice.
-- Copyright:
Category: Language
Build-type: Simple
-- Extra files to be distributed with the package, such as examples or
-- a README.
-- Extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
Cabal-version: >=1.2
Executable limp
-- .hs or .lhs file containing the Main module.
Main-is: Limp.hs
hs-source-dirs: src
-- Packages needed in order to build this package.
Build-depends: base, array, haskell98
-- Modules not exported by this package.
-- Other-modules:
-- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.
Build-tools: alex, happy
Executable limpi
Main-is: LimpInterpreter.hs
hs-source-dirs: src
Build-depends: base, array, haskell98
Build-tools: alex, happy
Directory layout:
limp/
├── Setup.hs
├── limp.cabal
└── src/
├── Limp.hs
├── LimpInterpreter.hs
├── LimpParser.ly
├── LimpScanner.x
└── LimpToken.hs
For Warren Harris and others like him (and myself) that may come along later, other-modules needs to be set to a list of module names that (I guess?) are expected to be built by the tools listed in build-tools.
So, in my case, the relevant sections of my .cabal file ended up looking like this:
build-tools: alex, happy
other-modules: Language.Heidi.Parser,
Language.Heidi.Lexer
Apparently what I was missing was actually the Other-modules: field. Once this was added, cabal happily (pardon the pun) built my interpreter.

Resources