Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow executables to have Main modules with other names #1847

Closed
mcandre opened this issue May 5, 2014 · 24 comments
Closed

Allow executables to have Main modules with other names #1847

mcandre opened this issue May 5, 2014 · 24 comments

Comments

@mcandre
Copy link

mcandre commented May 5, 2014

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.

@ttuegel
Copy link
Member

ttuegel commented Feb 28, 2015

Cabal does allow any filename for an executable.

@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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)

@ttuegel
Copy link
Member

ttuegel commented Sep 18, 2015

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

@23Skidoo
Copy link
Member

@ttuegel +1.

@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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 ttuegel reopened this Sep 18, 2015
@ttuegel ttuegel changed the title Allow flexibility in main program filenames Allow executables to have Main modules with other names Sep 18, 2015
@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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

@ttuegel
Copy link
Member

ttuegel commented Sep 18, 2015

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

@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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.

@ttuegel
Copy link
Member

ttuegel commented Sep 18, 2015

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.

@ttuegel
Copy link
Member

ttuegel commented Sep 18, 2015

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.

@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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.

@ttuegel
Copy link
Member

ttuegel commented Sep 18, 2015

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

@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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?

@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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.

@ttuegel
Copy link
Member

ttuegel commented Sep 18, 2015

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?

@gbaz
Copy link
Collaborator

gbaz commented Sep 18, 2015

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

@ezyang
Copy link
Contributor

ezyang commented Dec 22, 2015

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

@gbaz
Copy link
Collaborator

gbaz commented Feb 7, 2018

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.

@23Skidoo
Copy link
Member

23Skidoo commented Feb 8, 2018

#5122 was merged, closing.

@saurabhnanda
Copy link

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.

@hansroland
Copy link

hansroland commented Sep 28, 2020

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

@andreasabel
Copy link
Member

So the workaround for a main module that is called Foo and resides in file Foo.hs seems to be then:

  main-is: Foo.hs
  ghc-options: -main-is Foo

@friedbrice
Copy link

So the workaround for a main module that is called Foo and resides in file Foo.hs seems to be then:

  main-is: Foo.hs
  ghc-options: -main-is Foo

Seems the work around doesn't work. I keep getting linker errors.

@andreasabel
Copy link
Member

@friedbrice : Do you have a small reproducer? Might want to share this.

However, I don't know how the standing toward this feature is, whether the cabal team will make efforts to support it properly. (https://github.com/haskell/cabal/pull/5122/files)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants