Telling cabal where the main module is - haskell

I have a project with this structure:
foo.cabal
src/
Foo/
Main.hs
and part of foo.cabal looks like this:
executable foo
main-is: Foo/Main.hs
hs-source-dirs: src
Main.hs has the package name Foo.Main. When I build it cabal compiles everything but doesn't create an executable because it says there is no main module.
Warning: output was redirected with -o, but no output will be generated
because there is no Main module.
What am I doing wrong?
[EDIT] If I move Main up a level and change foo.cabal to read main-is: Main.hs it works. So can I not have a nested module name for Main?

The Main module must be called Main, not Foo.Main or anything else. If you want Foo.Main, then rename main in it to something like defaultMain, then make a top level Main module that imports Foo.Main (defaultMain) and defines main = defaultMain, such as:
src/
Foo/
Main.hs
Main.hs
foo.cabal
Where
-- src/Foo/Main.hs
module Foo.Main
( defaultMain
) where
defaultMain :: IO ()
defaultMain = putStrLn "Hello, world!"
And
-- src/Main.hs
module Main where
import Foo.Main (defaultMain)
main :: IO ()
main = defaultMain
Alternatively, you could keep it Foo.Main.main and just import it qualified.

Cabal's main-is: specifies only the filename from which to build the module containing the entrypoint (default Main.main); it does not allow changing that default.
Changing the default is compiler-specific, but if you are using GHC you can do this by passing a compiler command-line option: to your Cabal file executable definition add ghc-options: -main-is Foo. You may also use a different top-level function as the entry point: ghc-options: Foo.startup for example. See -main-is <thing> in the documentation for more details.
If you're using a package.yaml (as used by hpack/Stack/etc.) you may specify for main: either:
A .hs filename, in which case that will be directly used on the main-is: line in the generated Cabal file; or
A module name or module and exported definition name (e.g., Foo or Foo.startup) in which case the appropriate ghc-options: -main-is ... will be added to the generated Cabal file and main-is: will be a filename generated from the module name (e.g. main-is: Foo.hs).

Related

Build dependency in library or executable section of cabal file?

First off, I'm new to using cabal and external packages with Haskell.
I'm trying to use the Graphics.Gloss package inside of MyLib. I can make it work if I include gloss in both the build-depends of library and executable.
Here is the relevant portion of the cabal file:
library
exposed-modules: MyLib
build-depends: base ^>=4.13.0.0,
gloss ^>=1.13.1.1
default-language: Haskell2010
executable ray-tracer
main-is: Main.hs
other-modules: MyLib
build-depends: base ^>=4.13.0.0, ray-tracer,
haskell-say ^>=1.0.0.0,
gloss ^>=1.13.1.1
MyLib.hs
module MyLib (someFunc) where
import Graphics.Gloss
someFunc :: IO ()
someFunc = display (InWindow "My Window" (200,200) (10,10)) white (Circle 80)
Main.hs
module Main where
import qualified MyLib (someFunc)
import HaskellSay (haskellSay)
main :: IO ()
main = do
MyLib.someFunc
Why doesn't this work when gloss is only included in the library dependencies?
You can make it work. There is a problem in your current set up, which is that the files for the library and the executable are in the same directory. See also this question How to avoid recompiling in this cabal file? which is a symptom of the same underlying problem: when you build the executable, it rebuilds MyLib from scratch (which requires the gloss dependency) instead of reusing your library that was already built.
MyLib/
ray-tracer.cabal
MyLib.hs
Main.hs # Bad
Move the .hs files in separate directories (technically you only need to move one of them, but I think it's better to keep the root directory as uniform as possible):
MyLib/
MyLib.cabal
src/
MyLib.hs
exe/
Main.hs
And in the cabal file, add hs-source-dirs: src and hs-source-dirs: exe to the corresponding sections:
library
hs-source-dirs: src
...
executable ray-tracer
hs-source-dirs: exe
...

How do I import a text file in a cabal project?

In app/Main.hs, I want to open a text file, "foo.txt". I know how to open a text file in a plain Haskell program. In my cabal project,
import System.IO
Main = do
contents <- readFile "foo.txt"
print $ Main.lex contents
return contents
type Keyword = String
lex :: String -> [Keyword]
lex "" = []
lex x = words x
gives the error
openFile: does not exist (No such file or directory)
What do I need to change in my cabal file, or the file path or location to be able to open the file? I've tried putting it next to the output binary, and that doesn't work either.
This is my cabal file:
-- This file has been generated from package.yaml by hpack version 0.28.2.
--
-- see: https://github.com/sol/hpack
--
-- hash: baf2fc7e230f4b4937dfd918a13fefb55b66c7a4468b24d0e3e90cad675b26d5
name: CCompiler
version: 0.1.0.0
description: Please see the README on GitHub at <https://github.com/githubuser/CCompiler#readme>
homepage: https://github.com/githubuser/CCompiler#readme
bug-reports: https://github.com/githubuser/CCompiler/issues
author: Author name here
maintainer: example#example.com
copyright: 2018 Author name here
license: BSD3
license-file: LICENSE
build-type: Simple
cabal-version: >= 1.10
extra-source-files:
ChangeLog.md
README.md
source-repository head
type: git
location: https://github.com/githubuser/CCompiler
library
exposed-modules:
Lib
other-modules:
Paths_CCompiler
hs-source-dirs:
src
build-depends:
base >=4.7 && <5
default-language: Haskell2010
executable CCompiler-exe
main-is: Main.hs
other-modules:
Paths_CCompiler
hs-source-dirs:
app
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends:
CCompiler
, base >=4.7 && <5
default-language: Haskell2010
test-suite CCompiler-test
type: exitcode-stdio-1.0
main-is: Spec.hs
other-modules:
Paths_CCompiler
hs-source-dirs:
test
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends:
CCompiler
, base >=4.7 && <5
default-language: Haskell2010
add
data-dir: data
in the top section of the cabal file.
create the directory 'data' next to src and app, and put all files in there.
Make sure your cabal file also has this line
other-modules:
Paths_CCompiler
with your project's name instead of CCompiler.
My main function is now this
module Main where
import Lib
import System.IO
import Paths_CCompiler
main = do
filepath <- getDataFileName "return_2.c"
contents <- readFile filepath
print $ Lib.lex contents
return contents
Thanks to this blog post.
I understand your question to be about finding files at runtime, which you want to process and aren't packaged with your package.
Where are severals ways, how you could find files at runtime, which aren't packaged.
Either add an command line flag and call your executable with the absolute path of the file you want to process.
Or implement a file chooser dialog, with e.g. gi-gtk.
Or hard coding relative paths which isn't advisable, as they are interpreted relative to the current working directory of your process, which can be different, depending on how the program did get started.
If you want to determine, which current working directory your program runs in, if started with cabal run, just do a litte test project with the following cabal file:
name: test2
build-type: Simple
cabal-version: >= 1.10
version: 0.0.0.1
executable test2
hs-source-dirs: .
main-is: test2.hs
build-depends:
base
, directory
and the following test2.hs:
module Main where
import System.Directory
main :: IO ()
main = do
cwd <- getCurrentDirectory
putStrLn cwd

Testing Haskell code defined in the Main module

I'm looking for help regarding how to write tests for Haskell code that is defined in the Main module.
The project I want to test is a cabal package in which I've defined multiple executables.
Each executable code is declared only made of a single file (one for each project euler problem), and I usually run them individually with the cabal run command.
I have tried to write a test, also in the Main module, but when compiling, the function I'm trying to test is not found ("Not in scope" error).
What is the right way to write tests in this case?
For information, below is the directory layout of my project:
pe/ # root
pe.cabal
src/
Util.hs
Problem001.hs # "module Main where" and declares a main function
Problem002.hs # "module Main where" and declares a main function
(...)
test/
TestProblem001.hs # "module Main where" and declares a main function
Below is an extract from pe.cabal:
test-suite test-all
hs-source-dirs: test
type: exitcode-stdio-1.0
main-is: TestProblem001.hs
build-depends: base, HUnit, Cabal >= 1.9.2
executable problem-001
hs-source-dirs: src
main-is: Problem001.hs
build-depends: base
ghc-options: -Wall -Werror -O2
[edit]
As I couldn't find any resource for this exact requirement, I opted for a project architecture which is easier to test: problems are defined as library and not individual executables anymore.
I do so
test1 = ...
test2 = ...
main = do
args <- getArgs
case args of
... -> check test1
... -> check test2

Specifying "Up The Tree" Haskell Modules

My understanding is that the dotted syntax of modules, in Haskell, represents logical structure on disk. So, if we have a structure like this:
Main.hs
Foo/
Bar.hs -- exports "Bar"
Quux.hs -- exports "Quux"
...then in our Main.hs, we can do:
import Foo.Bar
import Foo.Quux
(I assume we can only have modules at leaf nodes of the filesystem. For example, in the above, there's no way we could have a Foo module, as well.)
In this example, we're traversing down the tree. What happens if we want to go up?
lib/
SomeModule.hs
XYZ.hs
src/
Main.hs
That is, in Main.hs, how do we import SomeModule or XYZ?
Maybe this wouldn't be a common occurrence with Main, but what about inter-module dependencies? They could legitimately need to reference "cousin" nodes.
Just use the fully qualified name of the module and tell GHC where to find the root of the module hierarchy with the -i option. In your example, this means that you should use import XYZ in Main.hs to import the module and the command ghc -i../src --make Main.hs to compile your program. If you need to compile mutually recursive modules, take a look at this section of the GHC manual.
If you're using Cabal to build your package, you can group the modules under lib in a library and then make that library a dependency of your executable. You will have the following directory structure:
some-package.cabal
lib/
XYZ.hs
src/
Main.hs
The relevant parts of the some-package.cabal file will look like this:
Name: some-package
Version: 1.0
...
Library
...
Exposed-modules: XYZ
Hs-source-dirs: lib
...
Executable some-executable
...
build-depends: some-package == 1.0
...
...
This is especially useful if your package includes a test or benchmark suite, because the modules under lib will be compiled only once.
Here's a real-life example of this technique.

How to make a Haskell cabal project with library+executables that still run with runhaskell/ghci?

If you declare a library + executable sections in a cabal file while avoiding double compilation of the library by putting the library into a hs-source-dirs directory, you cannot usually run your project with ghci and runhaskell anymore, especially if the executables have helper modules themselves.
What is a recommended project layout that
only builds what is needed once
allows using runhaskell
has a clean structure without hacks?
Let's assume you have a mylib library, and mylib-commandline and mylib-server executables.
You use hs-source-dirs for the library and each executable so that each has their own project root, avoiding double compilation:
mylib/ # Project root
mylib.cabal
src/ # Root for the library
tests/
mylib-commandline/ # Root for the command line utility + helper modules
mylib-server/ # Root for the web service + helper modules
Full directory layout:
mylib/ # Project root
mylib.cabal
src/ # Root for the library
Web/
Mylib.hs # Main library module
Mylib/
ModuleA # Mylib.ModuleA
ModuleB # Mylib.ModuleB
tests/
...
mylib-commandline/ # Root for the command line utility
Main.hs # "module Main where" stub with "main = Web.Mylib.Commandline.Main.main"
Web/
Mylib/
Commandline/
Main.hs # CLI entry point
Arguments.hs # Programm command line arguments parser
mylib-server/ # Root for the web service
Server.hs # "module Main where" stub with "main = Web.Mylib.Server.Main.main"
Web/
Mylib/
Server/
Main.hs # Server entry point
Arguments.hs # Server command line arguments parser
The stub-like entry point file mylib-commandline/Main.hs looks like this:
module Main where
import qualified Web.Mylib.Server.Main as MylibServer
main :: IO ()
main = MylibServer.main
You need them because an executable must start on a module simply called Main.
Your mylib.cabal looks like this:
library
hs-source-dirs: src
exposed-modules:
Web.Mylib
Web.Mylib.ModuleA
Web.Mylib.ModuleB
build-depends:
base >= 4 && <= 5
, [other dependencies of the library]
executable mylib-commandline
hs-source-dirs: mylib-commandline
main-is: Main.hs
other-modules:
Web.Mylib.Commandline.Main
Web.Mylib.Commandline.Arguments
build-depends:
base >= 4 && <= 5
, mylib
, [other depencencies for the CLI]
executable mylib-server
hs-source-dirs: mylib-server
main-is: Server.hs
other-modules:
Web.Mylib.Server.Main
build-depends:
base >= 4 && <= 5
, mylib
, warp >= X.X
, [other dependencies for the server]
cabal build will build the library and the two executables without double compilation of the library, because each is in their own hs-source-dirs and the executables depend on the library.
You can still run the executables with runghc from your project root, using the -i switch to tell where it shall look for modules (using : as separator):
runhaskell -isrc:mylib-commandline mylib-commandline/Main.hs
runhaskell -isrc:mylib-server mylib-server/Server.hs
This way, you can have a clean layout, executables with helper modules, and everything still works with runhaskell/runghc and ghci. To avoid typing this flag repeatedly, you can add something similar to
:set -isrc:mylib-commandline:mylib-server
to your .ghci file.
Note that sometimes should split your code into separate packages, e.g. mylib, mylib-commandline and mylib-server.
You can use cabal repl to start ghci with the configuration from the cabal file and cabal run to compile and run the executables. Unlike runhaskell and ghci, using cabal repl and cabal run also picks up dependencies from cabal sandboxes correctly.

Resources