Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion Cabal/src/Distribution/Simple/GHC/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,16 @@ splitCandCxxOptions
-> GhcOptions
splitCandCxxOptions source verbosity lbi bi clbi odir filename = case source of
CxxProgram ->
setCcProgram $ setCcOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename
-- For C++ sources: reset ccOptions for GHC < 8.10, because on those
-- old GHCs there's no -optcxx flag — all options go through -optc.
-- Without this reset, C-specific flags (ccOptions) would leak into
-- C++ compilation via -optc, which is wrong.
setGppProgram $ setCcOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename
CcProgram ->
-- For C sources: reset cxxOptions for GHC < 8.10, because on those
-- old GHCs there's no -optcxx flag — all options go through -optc.
-- Without this reset, C++-specific flags (cxxOptions) would leak into
-- C compilation via -optc, which is wrong.
setCcProgram $ setCxxOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename
where
setCcOptions xxx =
Expand Down Expand Up @@ -406,6 +414,29 @@ splitCandCxxOptions source verbosity lbi bi clbi odir filename = case source of
-- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc
ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi)
}
setGppProgram xxx =
xxx
{ -- We explicitly pass the C++ compiler via -pgmcxx because GHC >= 9.4
-- does not automatically detect it from the toolchain. Without this GHC
-- falls back to a gcc as a C++ compiler.
-- Related: #11805
ghcOptGppProgram =
ghcOptionsSince
(mkVersion [9, 4])
(compiler lbi)
(maybeToFlag $ programPath <$> lookupProgram gppProgram (withPrograms lbi))
, -- For GHC < 9.4, which does not support -pgmcxx, we fall back to
-- passing the C++ compiler via -pgmc. This ensures C++ sources are
-- compiled with the correct compiler even on older GHCs.
-- Note: we use gppProgram (C++ compiler), NOT gccProgram (C compiler),
-- because this path is specifically for C++ source files.
-- Related: #11805
ghcOptCcProgram =
ghcOptionsBefore
(mkVersion [9, 4])
(compiler lbi)
(maybeToFlag $ programPath <$> lookupProgram gppProgram (withPrograms lbi))
}

sourcesGhcOptions
:: VerbosityLevel
Expand Down Expand Up @@ -454,6 +485,15 @@ ghcOptionsSince ver comp defOptions =
| otherwise -> mempty
Nothing -> mempty

-- Applies options only if the GHC version is less than the given one.
ghcOptionsBefore :: Monoid a => Version -> Compiler -> a -> a
ghcOptionsBefore ver comp defOptions =
case compilerCompatVersion GHC comp of
Just v
| v < ver -> defOptions
| otherwise -> mempty
Nothing -> mempty

componentGhcOptions
:: VerbosityLevel
-> LocalBuildInfo
Expand Down Expand Up @@ -552,6 +592,12 @@ linkGhcOptions verbosity lbi bi clbi =
(mkVersion [9, 4])
(compiler lbi)
(maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi))
, -- The assumption that the C++ compiler is part of the toolchain is only since ghc-9.4.
ghcOptGppProgram =
ghcOptionsSince
(mkVersion [9, 4])
(compiler lbi)
(maybeToFlag $ programPath <$> lookupProgram gppProgram (withPrograms lbi))
}
where
exe_paths =
Expand Down
5 changes: 4 additions & 1 deletion Cabal/src/Distribution/Simple/Program/GHC.hs
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,9 @@ data GhcOptions = GhcOptions
, ghcOptFfiIncludes :: NubListR FilePath
-- ^ Extra header files to include for old-style FFI; the @ghc -#include@ flag.
, ghcOptCcProgram :: Flag FilePath
-- ^ Program to use for the C and C++ compiler; the @ghc -pgmc@ flag.
-- ^ Program to use for the C compiler; the @ghc -pgmc@ flag.
, ghcOptGppProgram :: Flag FilePath
-- ^ Program to use for the C++ compiler; the @ghc -pgmcxx@ flag.
, ----------------------------
-- Language and extensions

Expand Down Expand Up @@ -875,6 +877,7 @@ renderGhcOptions comp _platform@(Platform _arch os) opts
in [cxxflag ++ opt | opt <- ghcOptCxxOptions opts]
, ["-opta" ++ opt | opt <- ghcOptAsmOptions opts]
, concat [["-pgmc", cc] | cc <- flag ghcOptCcProgram]
, concat [["-pgmcxx", cxx] | cxx <- flag ghcOptGppProgram]
, -----------------
-- Linker stuff

Expand Down
16 changes: 16 additions & 0 deletions cabal-testsuite/PackageTests/FFI/ForeignOptsPgmcxx/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{-# LANGUAGE ForeignFunctionInterface #-}

module Main where

import Foreign.C (CInt (..))

foreign import ccall "pgmcxxlib.h meaning_of_life_pgmcxx"
meaning_of_life_pgmcxx :: IO CInt

main :: IO ()
main = do
secret <- meaning_of_life_pgmcxx
-- The value 67 comes from __TESTOPT_PGMCXX__ - see cxx-wrapper.sh.
if secret == 67
then putStrLn ("The secret is " ++ show secret)
else error ("Expected value 67, got " ++ show secret)
7 changes: 7 additions & 0 deletions cabal-testsuite/PackageTests/FFI/ForeignOptsPgmcxx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# ForeignOptsPgmcxx

This test case asserts that cabal passes the `-pgmcxx` GHC option to override the C++ compiler program.

The cabal file sets `ghc-options: -pgmcxx scripts/cxx-wrapper.sh`, pointing GHC at a shell script wrapper (`scripts/cxx-wrapper.sh`) instead of the system C++ compiler. The wrapper adds `-D__TESTOPT_PGMCXX__=67` to every compilation and then delegates to the real `g++`. The C++ source requires `__TESTOPT_PGMCXX__` to be defined; if the wrapper is not used as the C++ compiler, the build fails with a `#error`.

This test is skipped on Windows (no POSIX shell).
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages: .
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Test.Cabal.Prelude

main = do
skipIfWindows "requires a POSIX shell script as the C++ compiler wrapper"
-- The assumption that the C++ compiler is part of the toolchain is only since ghc-9.4.
cabalTest $ recordMode DoNotRecord $ do
skipUnlessGhcVersion ">= 9.4"
cabal "v2-build" ["foreign-opts-pgmcxx-exe"]
withPlan $ runPlanExe "foreign-opts-pgmcxx" "foreign-opts-pgmcxx-exe" []
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "pgmcxxlib.h"

#ifndef __TESTOPT_PGMCXX__
#error "Did not get required __TESTOPT_PGMCXX__ from the -pgmcxx wrapper"
#endif

int meaning_of_life_pgmcxx() {
return __TESTOPT_PGMCXX__;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef PGMCXXLIB_H
#define PGMCXXLIB_H
extern "C" {

int meaning_of_life_pgmcxx();

}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cabal-version: 2.2
name: foreign-opts-pgmcxx
version: 0.1
build-type: Simple

executable foreign-opts-pgmcxx-exe
main-is: Main.hs
build-depends: base
default-language: Haskell2010
include-dirs: cxxbits
-- The stdc++ extra library option is not needed given that we now pass -pgmcxx by default.
-- Only gcc need stdc++ for C++ code, g++ doesn't need it.
-- extra-libraries: stdc++
cxx-sources: cxxbits/pgmcxxlib.cpp
ghc-options: -pgmcxx scripts/cxx-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
# Wrapper around g++ that adds -D__TESTOPT_PGMCXX__=67 to every compilation.
# Used by the ForeignOptsPgmcxx test to verify that -pgmcxx selects this wrapper.
exec g++ -D__TESTOPT_PGMCXX__=67 "$@"
Loading
Loading