Cabal: Allow executables to have Main modules with other names

Created on 5 May 2014  路  21Comments  路  Source: haskell/cabal

Originally #172. This ticket edited by @ttuegel on 2015-09-18 to include original problem description.

Query on haskell-cafe:

It seems the meaning of the -main-is switch for GHC and the Main-Is
build option for Cabal executables differ. With GHC, I can point to
any function "main" in any module, but in Cabal I must point to a
filename with precisely the module name "Main". This is tying my hands
with regard to organizing a default executable and exposing some of
its functionality as a library. Is there a way to get around this
restriction?
Concretely, I want to point Cabal's Main-Is to Program/Main.hs
which starts with
  module Program.Main where
instead of just
  module Main where

So in GHC it has this meaning:

-main-is thing
The normal rule in Haskell is that your program must supply a
main function in module Main. When testing, it is often
convenient to change which function is the "main" one, and
the -main-is flag allows you to do so. The thing can be one of:
A lower-case identifier foo. GHC assumes that the main
function is Main.foo.
    An module name A. GHC assumes that the main function
    is A.main.
    An qualified name A.foo. GHC assumes that the main
    function is A.foo.

In Cabal the main-is: field specifies the file name of the Main module.

GHC's -main-is flag is an extension to Haskell that is not supported by the other Haskell implementations.

other bug

Most helpful comment

@gbaz Try ghc-options: -main-is Foo. Can ghc --make alone even infer which module is Main?

All 21 comments

Cabal _does_ allow any filename for an executable.

The problem is more subtle, and just bit someone again. The original ticket characterized it correctly, and this does not. cabal does not allow for arbitrary _module_ names for executables -- i.e. whatever the _file_ is named, the _module_ must be named main.

(this was the underlying cause of the confusion here: https://www.reddit.com/r/haskell/comments/3lax6y/discussion_thread_about_stack/cv5uv0p)

@gbaz Try ghc-options: -main-is Foo. Can ghc --make alone even infer which module is Main?

@ttuegel +1.

Yes, ghc --make _can_ handle this. As the original ticket this references stated, GHC does this, and Cabal does not, and the choice was made to preserve this behavior because the GHC main-is support is GHC specific, and it was desired to support "all compilers."

At this point, I think that ship has well sailed in this regard, with regards to legacy compilers. And we can expect new compilers to provide this.

Anyway, the linked post above illustrates how this behavior can end up being quite confusing. At a minimum we should perhaps hard fail if we can't find a Main module instead of providing Warning: output was redirected with -o, but no output will be generated because there is no Main module. Not generating output seems like it should be an error to me :-)

@ttuegel I'm happy to look at this if you or someone else could toss me a few pointers regarding the easiest path to tackle it.

@gbaz The main barrier to fixing this today is the fact that we (Cabal) have no idea what the module name in that file is. We could change the meaning of the main-is: field of the package description, but that would certainly break a lot of things. We could add a field main-module-is: as suggested in the original ticket, but that doesn't meet your criteria for the same reason that ghc-options: -main-is Foo doesn't. We can't even fail if there's no Main module, because we have no idea if that's the case or not! As far as I know, there is no path forward on this problem, short of rewriting Cabal to drive GHC through its API rather than the command line.

if you point me to the relevant files in the cabal codebase even, I could have a think :-)

and failing that, if there are no objections, i'll just upgrade that warning to an error.

Yes, ghc --make can handle this. As the original ticket this references stated, GHC does this, and Cabal does not, and the choice was made to preserve this behavior because the GHC main-is support is GHC specific, and it was desired to support "all compilers."

I just tried this, and no, GHC definitely cannot handle this. Observe:

Hello.hs:

module Hello where

main :: IO ()
main = putStrLn "Hello, world!"
$ ghc --make Hello.hs
[1 of 1] Compiling Hello            ( Hello.hs, Hello.o )

$ ls Hello*
Hello.hi  Hello.hs  Hello.o

On the other hand,

$ ghc --make Hello.hs -main-is Hello                                    
[1 of 1] Compiling Hello            ( Hello.hs, Hello.o ) [flags changed]
Linking Hello ...

$ ls Hello*                                                             
Hello  Hello.hi  Hello.hs  Hello.o

So, GHC cannot handle this automatically. Therefore, I don't think it's unreasonable to require the user to specify the name of module in a field such as main-module: (I think that matches exposed-modules: and other-modules: better than main-module-is:.)

and failing that, if there are no objections, i'll just upgrade that warning to an error.

I agree it should be an error and not a warning, but that produced by GHC, so you'll have to patch it there. That would be good, but it doesn't really fix the problem for users of current versions. Actually, I think it _should_ be an error to pass -main-is Foo to GHC is the module Foo can't be found, regardless of whether -o is given or not.

Relevant things to adding a main-module: field:
Distribution.PackageDescription: add a moduleName field to Executable.
Distribution.PackageDescription.Parse: add an entry to executableFieldDescrs.
Distribution.Simple.Program.GHC: add a field for -main-is option
Distribution.Simple.GHC: set the -main-is option. Does GHCJS take -main-is? Then we should set this in Distribution.Simple.GHCJS, too.

oh yes, I meant ghc handles this with "main-is" and cabal does not :-)

The way it appears to do so is that it infers the module name from the path given by "main-is".

So I wonder if we couldn't just leverage this behavior in the cabal process... I'll have a look.

@gbaz As I just demonstrated, GHC always assumes -main-is Main unless specified otherwise. It does not do any inference or anything else.

Ah, I think I see the issue. Cabal's "main-is" picks out a file. GHC's "main-is" picks out a _module name_ perhaps?

As per the manual:

The normal rule in Haskell is that your program must supply a main function in module Main. When testing, it is often convenient to change which function is the "main" one, and the -main-is flag allows you to do so. The thing can be one of:

  • A lower-case identifier foo. GHC assumes that the main function is Main.foo.
  • A module name A. GHC assumes that the main function is A.main.
  • A qualified name A.foo. GHC assumes that the main function is A.foo.

Strictly speaking, -main-is is not a link-phase flag at all; it has no effect on the link step. The flag must be specified when compiling the module containing the specified main function (e.g. module A in the latter two items above). It has no effect for other modules, and hence can safely be given to ghc --make. However, if all the modules are otherwise up to date, you may need to force recompilation both of the module where the new "main" is, and of the module where the "main" function used to be; ghc is not clever enough to figure out that they both need recompiling. You can force recompilation by removing the object file, or by using the -fforce-recomp flag.

so what I would like to see is if we generate the ghc main-is flag to pick out the module name of the file which is also specified in the "main-is" flag or something?

This is tricky because the semantics feel similar but do different things, lending to confusion.

so what I would like to see is if we generate the ghc main-is flag to pick out the module name of the file which is also specified in the "main-is" flag or something?

Cabal doesn't have access to the module name, and we can't safely infer it because that would break existing packages. Maybe you could modify the -main-is flag of GHC to accept a filename also?

on the warning/error thing, ghc bug filed: https://ghc.haskell.org/trac/ghc/ticket/10895#ticket

I think of ghc --make and filenames as a place where GHC got the interface horribly, horribly wrong. If ghc --make has been written so that a filename MUST be an executable, we wouldn't be having this discussion, because there would be no -main-is flag at the module name. (In general, ghc --make's handling of file targets is bonkers, but the ship has sailed since there are many convenient cases where you want to say ghci A.hs and mean the module named A.)

There are only two ways to fix this:

  1. Teach Cabal how to read out the module name of an hs file,
  2. Add a new mode to GHC to make it do what you want

I would much prefer (2). Cabal doesn't really have any business parsing hs files. (Well, being a build system, it kind of does, but that's a whole different kettle of fish.)

Ok, the upstream GHC issue I filed is fixed, and given ezyang's last comment here, I don't think we want to do anything fancy. (1) is a pain, and (2) seems like overkill for something that doesn't really matter. So let's just improve the docs a bit, as per the above-PR I filed, and when that's done, close this.

5122 was merged, closing.

Faced this issue in 2018 (this issue is from 2014 or even older). I couldn't understand _why_ Cabal was not fixed to handle this. Is it because of the following:

The main barrier to fixing this today is the fact that we (Cabal) have no idea what the module name in that file is.

I don't think it is possible to have divergent file names and module names, right? Whenever I've tried I get a compile error.

@saurabhnanda: This issue came again up in GHC.

I don't think it is possible to have divergent file names and module names

Unfortunately this is possible: The module name in a file Foo.hs may be either Foo or Main.

Hence it's not possible to translate a cabal main-is stanza automatically into an GHC main-isoption, without parsing the file Foo.hs.

Was this page helpful?
0 / 5 - 0 ratings