Skip to content

Commit e08f28b

Browse files
authored
Merge pull request #11763 from zlonast/zlonast/pgmcxx-option
Adding an explicit pgmcxx flag for interaction with GHC
2 parents c48713c + c596c67 commit e08f28b

14 files changed

Lines changed: 141 additions & 7 deletions

File tree

Cabal/src/Distribution/Simple/GHC/Internal.hs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,16 @@ splitCandCxxOptions
361361
-> GhcOptions
362362
splitCandCxxOptions source verbosity lbi bi clbi odir filename = case source of
363363
CxxProgram ->
364-
setCcProgram $ setCcOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename
364+
-- For C++ sources: reset ccOptions for GHC < 8.10, because on those
365+
-- old GHCs there's no -optcxx flag — all options go through -optc.
366+
-- Without this reset, C-specific flags (ccOptions) would leak into
367+
-- C++ compilation via -optc, which is wrong.
368+
setGppProgram $ setCcOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename
365369
CcProgram ->
370+
-- For C sources: reset cxxOptions for GHC < 8.10, because on those
371+
-- old GHCs there's no -optcxx flag — all options go through -optc.
372+
-- Without this reset, C++-specific flags (cxxOptions) would leak into
373+
-- C compilation via -optc, which is wrong.
366374
setCcProgram $ setCxxOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename
367375
where
368376
setCcOptions xxx =
@@ -406,6 +414,29 @@ splitCandCxxOptions source verbosity lbi bi clbi odir filename = case source of
406414
-- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc
407415
ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi)
408416
}
417+
setGppProgram xxx =
418+
xxx
419+
{ -- We explicitly pass the C++ compiler via -pgmcxx because GHC >= 9.4
420+
-- does not automatically detect it from the toolchain. Without this GHC
421+
-- falls back to a gcc as a C++ compiler.
422+
-- Related: #11805
423+
ghcOptGppProgram =
424+
ghcOptionsSince
425+
(mkVersion [9, 4])
426+
(compiler lbi)
427+
(maybeToFlag $ programPath <$> lookupProgram gppProgram (withPrograms lbi))
428+
, -- For GHC < 9.4, which does not support -pgmcxx, we fall back to
429+
-- passing the C++ compiler via -pgmc. This ensures C++ sources are
430+
-- compiled with the correct compiler even on older GHCs.
431+
-- Note: we use gppProgram (C++ compiler), NOT gccProgram (C compiler),
432+
-- because this path is specifically for C++ source files.
433+
-- Related: #11805
434+
ghcOptCcProgram =
435+
ghcOptionsBefore
436+
(mkVersion [9, 4])
437+
(compiler lbi)
438+
(maybeToFlag $ programPath <$> lookupProgram gppProgram (withPrograms lbi))
439+
}
409440

410441
sourcesGhcOptions
411442
:: VerbosityLevel
@@ -454,6 +485,15 @@ ghcOptionsSince ver comp defOptions =
454485
| otherwise -> mempty
455486
Nothing -> mempty
456487

488+
-- Applies options only if the GHC version is less than the given one.
489+
ghcOptionsBefore :: Monoid a => Version -> Compiler -> a -> a
490+
ghcOptionsBefore ver comp defOptions =
491+
case compilerCompatVersion GHC comp of
492+
Just v
493+
| v < ver -> defOptions
494+
| otherwise -> mempty
495+
Nothing -> mempty
496+
457497
componentGhcOptions
458498
:: VerbosityLevel
459499
-> LocalBuildInfo
@@ -552,6 +592,12 @@ linkGhcOptions verbosity lbi bi clbi =
552592
(mkVersion [9, 4])
553593
(compiler lbi)
554594
(maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi))
595+
, -- The assumption that the C++ compiler is part of the toolchain is only since ghc-9.4.
596+
ghcOptGppProgram =
597+
ghcOptionsSince
598+
(mkVersion [9, 4])
599+
(compiler lbi)
600+
(maybeToFlag $ programPath <$> lookupProgram gppProgram (withPrograms lbi))
555601
}
556602
where
557603
exe_paths =

Cabal/src/Distribution/Simple/Program/GHC.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,9 @@ data GhcOptions = GhcOptions
519519
, ghcOptFfiIncludes :: NubListR FilePath
520520
-- ^ Extra header files to include for old-style FFI; the @ghc -#include@ flag.
521521
, ghcOptCcProgram :: Flag FilePath
522-
-- ^ Program to use for the C and C++ compiler; the @ghc -pgmc@ flag.
522+
-- ^ Program to use for the C compiler; the @ghc -pgmc@ flag.
523+
, ghcOptGppProgram :: Flag FilePath
524+
-- ^ Program to use for the C++ compiler; the @ghc -pgmcxx@ flag.
523525
, ----------------------------
524526
-- Language and extensions
525527

@@ -875,6 +877,7 @@ renderGhcOptions comp _platform@(Platform _arch os) opts
875877
in [cxxflag ++ opt | opt <- ghcOptCxxOptions opts]
876878
, ["-opta" ++ opt | opt <- ghcOptAsmOptions opts]
877879
, concat [["-pgmc", cc] | cc <- flag ghcOptCcProgram]
880+
, concat [["-pgmcxx", cxx] | cxx <- flag ghcOptGppProgram]
878881
, -----------------
879882
-- Linker stuff
880883

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{-# LANGUAGE ForeignFunctionInterface #-}
2+
3+
module Main where
4+
5+
import Foreign.C (CInt (..))
6+
7+
foreign import ccall "pgmcxxlib.h meaning_of_life_pgmcxx"
8+
meaning_of_life_pgmcxx :: IO CInt
9+
10+
main :: IO ()
11+
main = do
12+
secret <- meaning_of_life_pgmcxx
13+
-- The value 67 comes from __TESTOPT_PGMCXX__ - see cxx-wrapper.sh.
14+
if secret == 67
15+
then putStrLn ("The secret is " ++ show secret)
16+
else error ("Expected value 67, got " ++ show secret)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# ForeignOptsPgmcxx
2+
3+
This test case asserts that cabal passes the `-pgmcxx` GHC option to override the C++ compiler program.
4+
5+
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`.
6+
7+
This test is skipped on Windows (no POSIX shell).

cabal-testsuite/PackageTests/FFI/ForeignOptsPgmcxx/cabal.out

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
packages: .
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Test.Cabal.Prelude
2+
3+
main = do
4+
skipIfWindows "requires a POSIX shell script as the C++ compiler wrapper"
5+
-- The assumption that the C++ compiler is part of the toolchain is only since ghc-9.4.
6+
cabalTest $ recordMode DoNotRecord $ do
7+
skipUnlessGhcVersion ">= 9.4"
8+
cabal "v2-build" ["foreign-opts-pgmcxx-exe"]
9+
withPlan $ runPlanExe "foreign-opts-pgmcxx" "foreign-opts-pgmcxx-exe" []
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "pgmcxxlib.h"
2+
3+
#ifndef __TESTOPT_PGMCXX__
4+
#error "Did not get required __TESTOPT_PGMCXX__ from the -pgmcxx wrapper"
5+
#endif
6+
7+
int meaning_of_life_pgmcxx() {
8+
return __TESTOPT_PGMCXX__;
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef PGMCXXLIB_H
2+
#define PGMCXXLIB_H
3+
extern "C" {
4+
5+
int meaning_of_life_pgmcxx();
6+
7+
}
8+
#endif
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cabal-version: 2.2
2+
name: foreign-opts-pgmcxx
3+
version: 0.1
4+
build-type: Simple
5+
6+
executable foreign-opts-pgmcxx-exe
7+
main-is: Main.hs
8+
build-depends: base
9+
default-language: Haskell2010
10+
include-dirs: cxxbits
11+
-- The stdc++ extra library option is not needed given that we now pass -pgmcxx by default.
12+
-- Only gcc need stdc++ for C++ code, g++ doesn't need it.
13+
-- extra-libraries: stdc++
14+
cxx-sources: cxxbits/pgmcxxlib.cpp
15+
ghc-options: -pgmcxx scripts/cxx-wrapper.sh

0 commit comments

Comments
 (0)