Set up Haskell Project and run tests - haskell

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.)

Related

How to import Control.Lens in Haskell?

I am new to Haskell. I want to use Control.Lens package. I've read this and applied what it says but I get an error that says:
Resolving dependencies...
cabal-3.6.2.0.exe: Cannot build the executables in the package lens because it
does not contain any executables. Check the .cabal file for the package and
make sure that it properly declares the components that you expect.
when I try to run cabal install lens in the project folder.(I opened terminal in the project folder.)
Any help would be appreciated.
Solution: I've learned that I need to add 'lens' into build-depends part of .cabal file.Then I need to import Control.Lens inside Main.hs . I give my .cabal file as an example:
executable Prelude3
main-is: Main.hs
-- Modules included in this executable, other than Main.
--other-modules: Basic
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends: containers,lens,microlens,microlens-platform,base ^>=4.14.3.0
hs-source-dirs: app
default-language: Haskell2010
On the top of the Main.hs I write import Control.Lens. Finally after I run 'cabal build' and cabal run in the terminal necessary packages successfully downlaoaded.

When does cabal recompile a module which contains Template 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.

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

Creating a Full Haskell Stack with Tests

I'm new to Haskell and I'm trying to structure a program under test. I have decided to use HUnit and Cabal.
From what I have seen a well strucutred project looks the following:
src/
AppName/
Appname.hs
testsuite/
tests/
AppName/
TestRunner.hs
AppName.cabal
Setup.hs
The parts that are a mystery to me are the TestRunner.hs and the AppName.cabal.
What would a testrunner look like that runs all of the test under the testsuite/tests directory and sub directories? And how can it be integrated with Cabal?
Also, how do you put the hackage dependencies in the AppName.cabal and build them from the command line?
I am having a hard time finding a full example building an application from scratch with tests and dependencies.
Thanks
Here's a fragment of the .cabal file I used for one of my recent libraries.
...
Library
Build-depends: base >= 4 && < 5, bytestring, directory, filepath, hslogger,
SHA, zlib
Ghc-options: -Wall
Exposed-modules: Ltc.Store
Test-suite reference
Hs-Source-Dirs: Test, .
Main-Is: ReferenceProps.hs
Type: exitcode-stdio-1.0
Build-Depends: base >= 4 && < 5, bytestring, directory, filepath, hslogger,
SHA, zlib
Ghc-Options: -Wall
Build-Depends: test-framework, test-framework-hunit, test-framework-quickcheck2,
HUnit, QuickCheck
As we can see the cabal file defines a library and a testsuite. The library defines the modules it exports, the packages it depends on, and sets some custom GHC options.
We can easily build and package the library for distribution with:
% cabal configure
% cabal build
% cabal sdist
The testsuite looks a lot like the the library: first off, it has the same dependencies as the library (see the first Build-Depends line), and it then adds some extra test dependencies (see the second Build-Depends line). The testsuite here is a combination of HUnit and QuickCheck tests, and it uses Test-Framework as the runner. The test proper is Test/ReferenceProps.hs. It's a exitcode-stdio type test. This means that cabal will say that the tests pass if ReferenceProps exits with code 0. Otherwise, it will say the tests failed.
The testsuite looks like this (but, here, we're going to use some simple tests for list reversals):
import Data.Monoid
import Test.Framework
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2
import Test.HUnit
import Test.QuickCheck
main :: IO ()
main = defaultMainWithOpts
[ testCase "rev" testRev
, testProperty "listRevRevId" propListRevRevId
] mempty
testRev :: Assertion
testRev = reverse [1, 2, 3] #?= [3, 2, 1]
propListRevRevId :: [Int] -> Property
propListRevRevId xs = not (null xs) ==> reverse (reverse xs) == xs
The main is just a harness. You can also set various options for test-framework by replacing the mempty. The function testRev is a HUnit test, and propListRevRevId is a QuickCheck test; see the relevant docs on how to write these.
Finally, we can run the tests:
% cabal configure --enable-tests
% cabal test

cabal FFI dependency

I am making a small Haskell game in Windows, where I would like to respond each time the user presses a key. Because getChar behaves strangely on Windows, I use FFI to get access to getch in conio.h, as described here. The relevant code is:
foreign import ccall unsafe "conio.h getch" c_getch :: IO CInt
This works fine, when I run it in ghci, or compile it with ghc. I also want to try making a cabal package out of it, so extending from this question, I include the following in my cabal file:
...
executable noughts
Includes: conio.h
Extra-libraries conio
...
But when I run cabal configure, it tells me:
cabal: Missing dependency on a foreign library:
* Missing C library: conio
It makes sense, because in my haskell platform directory, under ...\Haskell Platform\2012.4.0.0\mingw there is a conio.h file under the include directory, but no other conio file to provide the object code.
Am I doing this the right way, and if so, how can I find out which library to include in my cabal file?
First off, there is not always a one-to-one mapping between C header files and libraries. In this case, the functions declared in conio.h can be found in various runtime libraries, such as crtdll (deprecated) or msvcrt (preferred, I guess).
With the Haskell Platform on Windows, Cabal will look for these libraries in .\mingw\lib (under your Haskell Platform directory): if you ask for msvcrt, it will look for .\mingw\lib\libmsvcrt.a. This specific library should already be shipped with your Haskell Platform. (If you want to point to other directories with lib*.a files, you can use Cabal's --extra-lib-dirs option.)
A tiny example of this would be as follows; this is Main.hs:
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.Types
foreign import ccall unsafe "conio.h _putch" c_putch :: CInt -> IO ()
main :: IO ()
main = do
c_putch . toEnum . fromEnum $ '!'
c_putch . toEnum . fromEnum $ '\n'
And this would be something-awesome.cabal:
name: something-awesome
version: 0.1.0.0
build-type: Simple
cabal-version: >=1.8
executable yay
main-is: Main.hs
build-depends: base ==4.5.*
includes: conio.h
extra-libraries: msvcrt
This should work fine:
c:\tmp\something-awesome> dir /B
Main.hs
something-awesome.cabal
c:\tmp\something-awesome> cabal configure
Resolving dependencies...
Configuring something-awesome-0.1.0.0...
c:\tmp\something-awesome> cabal build
Building something-awesome-0.1.0.0...
Preprocessing executable 'yay' for something-awesome-0.1.0.0...
[1 of 1] Compiling Main ( Main.hs, dist\build\yay\yay-tmp\Main.o )
Linking dist\build\yay\yay.exe ...
c:\tmp\something-awesome> dist\build\yay\yay.exe
!

Resources