Skip to content

Cabal doesn't pass *-options to GHC when compiling ordinary Haskell sources #10969

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

zlonast
Copy link
Collaborator

@zlonast zlonast commented May 22, 2025

Will close issues #9801 #4435
Main idea #9801 (comment)

*-options should be always passed when invoking GHC, similarly as ghc-options should be always used when invoking ghc - regardless of what is the intention of a particular GHC-call. GHC might use or not use the options, Cabal cannot know and should not guess.


Template Α: This PR modifies behaviour or interface

Include the following checklist in your PR:

@zlonast zlonast force-pushed the options-invoking-ghc branch 2 times, most recently from 1bd501e to c3e68be Compare May 22, 2025 17:02
@zlonast zlonast marked this pull request as ready for review May 22, 2025 17:52
@zlonast
Copy link
Collaborator Author

zlonast commented May 22, 2025

I probably need to think about tests, maybe something started working besides CApiFFI 🤔

@zlonast
Copy link
Collaborator Author

zlonast commented May 22, 2025

Ok, this only works for things newer than 8.8
gcc: fatal error: cannot specify ‘-o’ with ‘-c’, ‘-S’ or ‘-E’ with multiple files

Ok, got it, we just need to separate the flags -optc and -optcxx
Pass -optcxx for GHC >= 8.10 #7072

@zlonast zlonast added this to the Considered for 3.16 milestone May 22, 2025
@zlonast zlonast force-pushed the options-invoking-ghc branch 10 times, most recently from a439429 to 1b8e5bb Compare May 24, 2025 15:33
@zlonast
Copy link
Collaborator Author

zlonast commented May 24, 2025

@ulysses4ever @phadej It's not very easy to add flags to sources files due to backward compatibility, so for now I suggest considering the pull request as is ¯\_(ツ)_/¯ (Anyway, this solved the problem with macros in header files)

Copy link
Collaborator

@ulysses4ever ulysses4ever left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start, thank you! Below are some inline questions.

One general question: did you check that this doesn't lead to duplicated options when cabal calls GHC? I assume other *-options are passed today sometimes. So, sometimes we'll get those options passed as today and through your change too. Is that right? It seems undesirable.

It's not very easy to add flags to sources files due to backward compatibility

Can you be more specific? Do you mean it's hard to test on older GHCs? This shouldn't be a problem because the tests can run for specific versions of GHC (e.g. starting from 8.10).

prs: 10969
---

*-options should be always passed when invoking GHC,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you specify exactly which *-options are meant currently? (ASM, LD, etc...) And explain what's the difference with how there handled now: I assume they're passed only under specific circumstances now. Which ones?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were only partially in componentGhcOptions, after my changes, some cross-interaction became possible. This is currently only available for .c and .h or .cpp/.cxx/.c++ and .h. I think so.

@@ -0,0 +1,3 @@
# ForeignOptsCapi

This test case asserts that cabal passes `cc-options` to the C compiler when use `foreign import capi`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the patch does something to other *-options too. Is it possible to test this change w.r.t. other *-options?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to think about it, but I don't know yet. Maybe I need to read other issues to see if anything worked.

Copy link
Collaborator

@geekosaur geekosaur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concerns as @ulysses4ever raised.

@phadej
Copy link
Collaborator

phadej commented May 24, 2025

did you check that this doesn't lead to duplicated options when cabal calls GHC?

Yes, it looks like so.

I'd say the problem is rather that GhcOptions is not used uniformly. There is e.g. componentCcGhcOptions which does set ghcOptCcOptions. Why these don't show up when compiling files CApiFFI headers?

IMHO, it's wrong that there are separate componentAsmGhcOptions, componentJsGhcOptions, componentGhcOptions. There should be just one componentGhcOptions with all the options, and then it's up to GHC which ones are relevant, and which aren't.

EDIT: There might be situations where ghcOptMode or something like that is different, and input files "obviously". But the bulk of options should be the same. Starting with verbosity. I'd imagine there is only single line in the codebase which does ghcOptVerbosity = toFlag (min verbosity normal). Currently there are six copies of that. Cannot be right. I suspect there are many subtle inconsistencies hiding because of that.

EDIT: So AFAICT, this is not the correct fix, not what I'd imagine seeing.

@zlonast zlonast force-pushed the options-invoking-ghc branch 3 times, most recently from 0b74992 to 37417aa Compare May 28, 2025 16:26
@zlonast zlonast force-pushed the options-invoking-ghc branch from 37417aa to 17df07a Compare May 28, 2025 16:31
@zlonast
Copy link
Collaborator Author

zlonast commented May 28, 2025

Basically I just merged most of the options into one base one and added the separation in 8.10.
Now buildHaskellModules, buildAllExtraSources, linkOrLoadComponent have the same base buildOpts.

@zlonast zlonast force-pushed the options-invoking-ghc branch 5 times, most recently from 9dae1ff to d7fec38 Compare May 29, 2025 11:28
optimizationCFLags lbi =
( case withOptimization lbi of
NoOptimisation -> []
NormalOptimisation -> ["-O1"] -- _ -> ["-O2"] -- attention: needs-review toGhcOptimisation
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this change, even though it seems logical.

@@ -17,13 +17,13 @@ Warning: [unknown-directory] 'hs-source-dirs: doesnt-exist' specifies a director
Preprocessing executable 'Complex' for Complex-0.1.0.0...
Building executable 'Complex' for Complex-0.1.0.0...
# show-build-info Complex exe:Complex
{"cabal-lib-version":"<CABALVER>","compiler":{"flavour":"ghc","compiler-id":"ghc-<GHCVER>","path":"<GHCPATH>"},"components":[{"type":"exe","name":"exe:Complex","unit-id":"Complex-0.1.0.0-inplace-Complex","compiler-args":["-fbuilding-cabal-package","-O","-outputdir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-odir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-hidir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-hiedir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/extra-compilation-artifacts/hie","-stubdir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-i","-iapp","-isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/Complex/autogen","-isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/global-autogen","-Isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/Complex/autogen","-Isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/global-autogen","-Isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-optP-include","-optPsingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/Complex/autogen/cabal_macros.h","-this-unit-id","Complex-0.1.0.0-inplace-Complex","-hide-all-packages","-Wmissing-home-modules","-no-user-package-db","-package-db","<ROOT>/single.dist/home/.cabal/store/ghc-<GHCVER>/package.db","-package-db","<ROOT>/single.dist/work/dist/packagedb/ghc-<GHCVER>","-package-id","<PACKAGEDEP>","-package-id","<PACKAGEDEP>","-XHaskell2010","-threaded","-rtsopts","-with-rtsopts=-N -T","-Wredundant-constraints"],"modules":["Other","Paths_Complex"],"src-files":["Main.lhs"],"hs-src-dirs":["app"],"src-dir":"<ROOT>/","cabal-file":"Complex.cabal"}]}
{"cabal-lib-version":"<CABALVER>","compiler":{"flavour":"ghc","compiler-id":"ghc-<GHCVER>","path":"<GHCPATH>"},"components":[{"type":"exe","name":"exe:Complex","unit-id":"Complex-0.1.0.0-inplace-Complex","compiler-args":["-fbuilding-cabal-package","-O","-outputdir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-odir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-hidir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-hiedir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/extra-compilation-artifacts/hie","-stubdir","single.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-i","-iapp","-isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/Complex/autogen","-isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/global-autogen","-Isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/Complex/autogen","-Isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/global-autogen","-Isingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build","-optP-include","-optPsingle.dist/work/./dist/build/<ARCH>/ghc-<GHCVER>/Complex-0.1.0.0/x/Complex/build/Complex/autogen/cabal_macros.h","-optc-O1","-optcxx-O1","-opta-O1","-this-unit-id","Complex-0.1.0.0-inplace-Complex","-hide-all-packages","-Wmissing-home-modules","-no-user-package-db","-package-db","<ROOT>/single.dist/home/.cabal/store/ghc-<GHCVER>/package.db","-package-db","<ROOT>/single.dist/work/dist/packagedb/ghc-<GHCVER>","-package-id","<PACKAGEDEP>","-package-id","<PACKAGEDEP>","-XHaskell2010","-threaded","-rtsopts","-with-rtsopts=-N -T","-Wredundant-constraints"],"modules":["Other","Paths_Complex"],"src-files":["Main.lhs"],"hs-src-dirs":["app"],"src-dir":"<ROOT>/","cabal-file":"Complex.cabal"}]}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add "-optc-O1","-optcxx-O1","-opta-O1"

@zlonast
Copy link
Collaborator Author

zlonast commented May 29, 2025

One general question: did you check that this doesn't lead to duplicated options when cabal calls GHC? I assume other *-options are passed today sometimes. So, sometimes we'll get those options passed as today and through your change too. Is that right? It seems undesirable.

@ulysses4ever Yes, you're right, now I've fixed it.

Can you be more specific? Do you mean it's hard to test on older GHCs? This shouldn't be a problem because the tests can run for specific versions of GHC (e.g. starting from 8.10).

@phadej correctly noted that compilerCompatVersion should have been added.

I'd say the problem is rather that GhcOptions is not used uniformly. There is e.g. componentCcGhcOptions which does set ghcOptCcOptions. Why these don't show up when compiling files CApiFFI headers?

Because they were only transferred to buildAllExtraSources files.

IMHO, it's wrong that there are separate componentAsmGhcOptions, componentJsGhcOptions, componentGhcOptions. There should be just one componentGhcOptions with all the options, and then it's up to GHC which ones are relevant, and which aren't.

I think you're right, now I did it this way.

EDIT: There might be situations where ghcOptMode or something like that is different, and input files "obviously". But the bulk of options should be the same.

Yes, I separated them into sourcesGhcOptions and componentGhcOptions.

Currently there are six copies of that. Cannot be right. I suspect there are many subtle inconsistencies hiding because of that.

Looks like I'll have to think about how to break the cabal :)

EDIT: So AFAICT, this is not the correct fix, not what I'd imagine seeing.

I hope you like the new version better.

@zlonast zlonast force-pushed the options-invoking-ghc branch from d7fec38 to 879010c Compare May 29, 2025 20:04
{ ghcOptCcOptions =
( case compilerCompatVersion GHC (compiler lbi) of
Just v
| v >= mkVersion [8, 10] -> Internal.defaultGhcOptCcOptions lbi bi
Copy link
Collaborator

@phadej phadej May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this (duplicated snippet) is not in sourcesGhcOptions . It has access to lbi?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not completely match, there are different defaultGhcOptCcOptions and defaultGhcOptCxxOptions.
You are right, ghcOptCcOptions and ghcOptCxxOptions can be moved to sourcesGhcOptions.
On the other hand, ghcOptCcProgram should be left here, since not everywhere (it seems to me) there may be access to gccProgram.

Copy link
Collaborator Author

@zlonast zlonast May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't quite understand about the access of lbi...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here may be access to gccProgram.

That's a bug on its own:

  • If --with-gcc is given, it should always be passed to GHC (so GHC always consistently uses the same GCC).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are different defaultGhcOptCcOptions and defaultGhcOptCxxOptions

What are the differences? Why. I don't find that intuitive. If there is a reason, we should have a comment about that. But in that case we should work on removing that reason. I only see a downsides of using subtly different calls to GHC when compiling C or C++ files.

Copy link
Collaborator Author

@zlonast zlonast May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we do such refactoring in renderGhcOptions from

            , -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc
          let cxxflag = case compilerCompatVersion GHC comp of
                Just v | v >= mkVersion [8, 10] -> "-optcxx"
                _ -> "-optc"
           in [cxxflag ++ opt | opt <- ghcOptCxxOptions opts]

to

        , ["-optcxx" ++ opt | opt <- ghcOptCxxOptions opts]

and we will also make options in accordance with their appearance

          -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc
        , ghcOptCxxOptions = case compilerCompatVersion GHC (compiler lbi) of
            Just v
              | v >= mkVersion [8, 10] -> optimizationCFlags lbi ++ cxxOptions bi
              | otherwise -> []
            Nothing -> []
        , ghcOptCcOptions = case compilerCompatVersion GHC (compiler lbi) of
            Just v
              | v >= mkVersion [8, 10] -> optimizationCFlags lbi ++ ccOptions bi
              | otherwise -> optimizationCFlags lbi ++ cxxOptions bi ++ ccOptions bi
            Nothing -> []

we will fail on foreign-opts-cxx.cabal test as it is tested on versions older than 8.10

cabal-version:       2.2
name:                foreign-opts-cxx
version:             0.1
build-type:          Simple

executable foreign-opts-cxx-exe
  main-is:             Main.hs
  build-depends:       base
  default-language:    Haskell2010
  include-dirs:        cxxbits
  extra-libraries:     stdc++
  cxx-sources:         cxxbits/cxxlib.cpp
  -- The following options are shared in all ForeignOpts cases:
  cc-options:          -D__TESTOPT_C__=11
  cxx-options:         -D__TESTOPT_CXX__=22
#ifdef __TESTOPT_C__
#error "Got unexpected __TESTOPT_C__ from cc-options"
#endif

to avoid these subtleties we can skipUnlessGhcVersion ">= 8.10" ???

Copy link
Collaborator

@phadej phadej May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Am I explaining myself unclearly. Let's try again.

it looks like that

  • You set C options and ghcOptCcProgram only in buildCSources
  • You set C++ options and ghcOptCxxProgram only in buildCxxSources

That cannot be right. You should set these options always.

My comments weren't related to >=8.10 check.

Copy link
Collaborator Author

@zlonast zlonast May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I always set after 8.10 and try to separate them in 8.8 for buildCSources and buildCxxSources, because otherwise the foreign-opts-cxx.cabal test will fail. In order for foreign-opts-cxx.cabal to pass on 8.8 they need to be separated or use skipUnlessGhcVersion ">= 8.10".

Copy link
Collaborator

@phadej phadej May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see. The GHC-8.8 doesn't know about C++ compiler, so we have to deal with it separately, and in an ugly way.

Could you write a note, referencing the test. By looking at the code only, it looks wrong or at least suspicious.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the retrospective, cabal-install could refused to support an accidental "but it did work before with older GHC, cc-options were passed to C compiler" when proper cxx-options were added. Those accidents just make code base very spagetti.

@zlonast zlonast force-pushed the options-invoking-ghc branch 4 times, most recently from 490eb39 to 016e3ef Compare May 31, 2025 13:25
All resource options now use base options.

Add test for FFI/ForeignOptsCapi.

Update test ShowBuildInfo/Complex.

Add ghcOptCcProgram to componentGhcOptions.
@zlonast zlonast force-pushed the options-invoking-ghc branch from 016e3ef to a4a1f41 Compare May 31, 2025 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cabal doesn't pass cc-options to GHC when compiling ordinary Haskell sources cc-options do not get passed to GHC
4 participants