How do I define a dependency on an executable in Haskell Stack? - haskell

In a default configuration created with stack new, the project structure is something like this:
# package.yaml
name: my-cool-app
library:
source-dirs: src
executables:
my-cool-app-exe:
source-dirs: app
tests:
my-cool-app-tests:
source-dirs: test
dependencies:
- my-cool-app # refers to the library
How do I define a dependency on the executable, so that my test project can include tests of code that resides in ./app?

Related

How to get a graph of dependencies of Haskell project

I have a several executable targets in the project, like:
...
app/
other_app/
src/
package.yaml
stack.yaml
...
so, I have obviously different .hs files in app/, other_app/, src/. If I build it with stack tool then it build apps and other_apps executables from their dependencies, ie, if some .hs file changed in src/ - the result executable will be rebuild. Otherwise - no. stack knows a graph of dependencies like GNU make does it. How to list/print this graph? For example, for C I have makedepend tool which is able to extract dependencies recursively from .c files. Is it possible to be done for Haskell files? With stack tool itself? Maybe with a cabal tool? I tried, for example, v2-freeze of the cabal tool, but it is not related to the problem.
UPDATE:
package.yaml looks like:
.............
executables:
app1-exe:
main: Main.hs
source-dirs: app1
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- app1
app2-exe:
main: Main.hs
source-dirs: app2
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- app1
............
and now I want to list recursively dependencies (in Makefile-style, not dependencies on packages, like stack dot does it!), for instance for app2's Main.hs, something like:
src/Component1/Types.hs
src/Component1/Utils.hs
src/CoolLibrary/CoolUtility.hs
...

Building multiple executables in the default Haskell Stack project

I used the default stack new to setup a project that has a server and a client as separate executables. I altered the package.yaml file in what seems like the right way (As of April 21, 2020 "There is no user guide") and added a new file to my app directory called Client.hs.
I got an error saying "Enabling workaround for Main module 'Main' listed in 'other-modules' illegally!"
How do I have stack build both the client and the server?
When I ran stack build I got:
[... clip ...]
Building executable 'ObjectServer' for ObjectServer-0.1.0.1..
[4 of 4] Compiling Client
Linking .stack-work\dist\29cc6475\build\ObjectServer\ObjectServer.exe ...
Warning: Enabling workaround for Main module 'Main' listed in 'other-modules'
illegally!
Preprocessing executable 'Client' for ObjectServer-0.1.0.1..
Building executable 'Client' for ObjectServer-0.1.0.1..
[3 of 3] Compiling Client
<no location info>: error:
output was redirected with -o, but no output will be generated
because there is no Main module.
-- While building package ObjectServer-0.1.0.1 using:
D:\HaskellStack\setup-exe-cache\x86_64-windows\Cabal-simple_Z6RU0evB_3.0.1.0_ghc-8.8.3.exe --builddir=.stack-work\dist\29cc6475 build lib:ObjectServer exe:Client exe:ObjectServer --ghc-options " -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
The relevant portion of package.yaml looks like this:
executables:
ObjectServer:
main: Main.hs
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- ObjectServer
Client:
main: Client.hs
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- ObjectServer
There are two problems here. First, the default value for other-modules in hpack is "all modules in source-dirs except main and modules mentioned in a when clause". If you look at the generated .cabal file, you'll see that as a result of this default, each executable has incorrectly included the other executable's module in its other-modules list. Second, the main setting gives the source file that contains the main module, but doesn't change the name of the module expected by GHC from Main to anything else. Therefore, that module still needs to be named module Main where ..., not module Client where..., unless you also, separately add a -main-is Client GHC option.
So, I would advise modifying Client.hs to make it the Main module:
-- in Client.hs
module Main where
...
and then specifying other-modules: [] explicitly for both executables:
executables:
ObjectServer:
main: Main.hs
other-modules: []
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- ObjectServer
Client:
main: Client.hs
other-modules: []
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- ObjectServer
That seems to work in my testing.

Using hsc2hs with HPack and Stack

I'd like to use hsc2hs via Stack and HPack. The problem is, since HPack doesn't allow me to list the .hsc file as a source file, only .hs, changes to the .hsc file don't trigger a rebuild in stack build.
My package.yaml file is as follows:
name: bounce-bench
version: 0.1.0
category: acme
dependencies:
- base
source-dirs:
- src
executables:
sim-ffi:
source-dirs: sim
main: sim-ffi.hs
build-tools: hsc2hs
c-sources:
- csim/Bounce.h
- csim/Bounce.c
include-dirs: csim
With the above file, the initial stack build succeeds (incl. building sim-ffi.hs from sim-ffi.hsc), however, subsequent stack build invocations don't do anything if sim-ffi.hsc is changed.
If I try to change the main key's value from sim-ffi.hs to sim-ffi.hsc, I am explicitly told off:
Cabal-simple_mPHDZzAJ_2.4.0.1_ghc-8.6.5: The 'main-is' field must specify a
'.hs' or '.lhs' file (even if it is generated by a preprocessor), or it may
specify a C/C++/obj-C source file.

How to define multiple executables / Main modules with stack/hpack

I use stack and a package.yaml file for hpack to compile my haskell project. It has three executables backed by one library. As one would expect the executables are all defining a Main module:
$ head -n1 app/*
==> app/Foo.hs <==
module Main where
==> app/Bar.hs <==
module Main where
==> app/Baz.hs <==
module Main where
And I use this package.yaml which looks very similar (to me) to the one linked from the official docs for hpack (the third one).
name: myproject
dependencies:
- base
library:
source-dirs: src
executables:
foo:
main: Foo.hs
source-dirs: app
dependencies: myproject
bar:
main: Bar.hs
source-dirs: app
dependencies: myproject
baz:
main: Baz.hs
source-dirs: app
dependencies: myproject
But when I stack build I get the error, that the module name does not match
the file name:
Building all executables for `myproject' once. After a successful build of all of them, only specified executables will be rebuilt.
myproject-0.0.0: build (lib + exe)
Preprocessing library for myproject-0.0.0..
Building library for myproject-0.0.0..
[1 of 2] Compiling Lib ( src/Lib.hs, .stack-work/dist/x86_64-linux-tinfo6/Cabal-2.2.0.1/build/Lib.o )
[2 of 2] Compiling Paths_myproject ( .stack-work/dist/x86_64-linux-tinfo6/Cabal-2.2.0.1/build/autogen/Paths_myproject.hs, .stack-work/dist/x86_64-linux-tinfo6/Cabal-2.2.0.1/build/Paths_myproject.o )
Preprocessing executable 'bar' for myproject-0.0.0..
Building executable 'bar' for myproject-0.0.0..
/home/luc/test/app/Baz.hs:1:8: error:
File name does not match module name:
Saw: ‘Main’
Expected: ‘Baz’
|
1 | module Main where
| ^^^^
-- While building package myproject-0.0.0 using:
/home/luc/.stack/setup-exe-cache/x86_64-linux-tinfo6/Cabal-simple_mPHDZzAJ_2.2.0.1_ghc-8.4.4 --builddir=.stack-work/dist/x86_64-linux-tinfo6/Cabal-2.2.0.1 build lib:myproject exe:bar exe:baz exe:foo --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
The only difference between my setup and the example that I found was the capital letters of the files in app/. And indeed, if I change them to lower case (in the filesystem and the package.yaml) it all builds correctly.
But why is that and where is that documented?
I think app should not be considered as a convention to be a folder of all final applications. In my projects I always split these to foo/Main.hs and bar/Main.hs for foo and bar targets. Hence
package.yaml should contain
executables:
foo:
main: Main.hs
source-dirs: foo
dependencies:
- myproject
bar:
main: Main.hs
source-dirs: bar
dependencies:
- myproject
And treat myproject just as a library for them

Could not find module ‘Test.HUnit’

I have a fresh installation of stack and ghci:
$ stack --version
Version 1.6.3, Git revision b27e629b8c4ce369e3b8273f04db193b060000db (5454 commits) x86_64 hpack-0.20.0
$ ghci --version
The Glorious Glasgow Haskell Compilation System, version 8.2.1
I make a new project:
$ stack new so-mve
Downloading template "new-template" to create project "so-mve" in so-mve/
... blah blah blah ...
Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- so-mve/
Selecting the best among 12 snapshots...
* Matches lts-10.3
Selected resolver: lts-10.3
Initialising configuration using resolver: lts-10.3
Total number of user packages considered: 1
Writing configuration to file: so-mve/stack.yaml
All done.
Looks pretty good:
$ tree so-mve
so-mve
├── ChangeLog.md
├── LICENSE
├── README.md
├── Setup.hs
├── app
│   └── Main.hs
├── package.yaml
├── so-mve.cabal
├── src
│   └── Lib.hs
├── stack.yaml
└── test
└── Spec.hs
It builds and runs:
$ cd so-mve
$ stack build
so-mve-0.1.0.0: build (lib + exe)
Preprocessing library for so-mve-0.1.0.0..
Building library for so-mve-0.1.0.0..
Preprocessing executable 'so-mve-exe' for so-mve-0.1.0.0..
Building executable 'so-mve-exe' for so-mve-0.1.0.0..
so-mve-0.1.0.0: copy/register
Installing library in /...blah-blah.../so-mve/.stack-work/install/x86_64-osx/lts-10.3/8.2.2/lib/x86_64-osx-ghc-8.2.2/so-mve-0.1.0.0-5kG2WnHWwo99IiYYGoxrcC
Installing executable so-mve-exe in /...blah-blah.../so-mve/.stack-work/install/x86_64-osx/lts-10.3/8.2.2/bin
Registering library for so-mve-0.1.0.0..
$ stack exec so-mve-exe
someFunc
Tests run:
$ stack test
blah blah blah
[2 of 2] Compiling Main ( test/Spec.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/so-mve-test/so-mve-test-tmp/Main.o )
...blah-blah-blah...
Progress: 1/2Test suite not yet implemented
so-mve-0.1.0.0: Test suite so-mve-test passed
Completed 2 action(s).
I triple check that HUnit is installed
$ stack install HUnit
Populated index cache.
I add one line to test/Spec.hs
$ cat test/Spec.hs
import Test.HUnit
main :: IO ()
main = putStrLn "Test suite not yet implemented"
Doesn't work:
$ stack test
so-mve-0.1.0.0: unregistering (components added: test:so-mve-test)
so-mve-0.1.0.0: build (lib + exe + test)
Preprocessing library for so-mve-0.1.0.0..
Building library for so-mve-0.1.0.0..
Preprocessing executable 'so-mve-exe' for so-mve-0.1.0.0..
Building executable 'so-mve-exe' for so-mve-0.1.0.0..
Preprocessing test suite 'so-mve-test' for so-mve-0.1.0.0..
Building test suite 'so-mve-test' for so-mve-0.1.0.0..
[2 of 2] Compiling Main ( test/Spec.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/so-mve-test/so-mve-test-tmp/Main.o )
/...blah-blah.../so-mve/test/Spec.hs:1:1: error:
Could not find module ‘Test.HUnit’
Use -v to see a list of the files searched for.
|
1 | import Test.HUnit
| ^^^^^^^^^^^^^^^^^
Progress: 1/2
-- While building custom Setup.hs for package so-mve-0.1.0.0 using:
/Users/XXXXXXXX/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_2.0.1.0_ghc-8.2.2 --builddir=.stack-work/dist/x86_64-osx/Cabal-2.0.1.0 build lib:so-mve exe:so-mve-exe test:so-mve-test --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
I don't have trouble importing other libraries, like Text.Read and Text.Printf.
I googled around a bunch, but didn't find an answer. Any ideas for me?
You just need to add HUnit to the dependencies for your test project. When using stack, you should edit the package.yaml file to specify dependencies. In particular, your test configuration should look something like:
tests:
so-mve-test:
main: Spec.hs
source-dirs: test
ghc-options:
- ...
dependencies:
- HUnit
This is documented in the latest Stack Guide, under the section Adding Dependencies.
You installed HUnit globally with stack, but that doesn't mean it is specified for your project.
Your cabal file for the project needs to specify a dependency on HUnit:
--so-mve.cabal
...
test-suite so-mve
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Spec.hs
build-depends: base
, HUnit
...
Text.Read and Text.Printf are both included in base, so you don't need to specify an additional dependency.
As pointed out in the comments, since you're using a package.yaml (as opposed to a stack.yaml) configuration with the newer version of stack, you'll need to specify the dependency there instead of the .cabal file:
tests:
so-mve-test:
main: Spec.hs
source-dirs: test
ghc-options:
- -threaded
dependencies:
- HUnit

Resources