Skip to content
Draft
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
54 changes: 49 additions & 5 deletions Cabal/src/Distribution/Simple/GHC/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ import Distribution.Types.GivenComponent
import Distribution.Types.LocalBuildInfo
import Distribution.Types.TargetInfo
import Distribution.Types.UnitId
import Distribution.Types.Version
import Distribution.Utils.NubList (NubListR, toNubListR)
import Distribution.Utils.Path
import Distribution.Verbosity
import Distribution.Version (Version)
import Language.Haskell.Extension
import System.Directory (listDirectory)
import System.Environment (getEnv)
Expand Down Expand Up @@ -415,10 +415,19 @@ componentCxxGhcOptions verbosity lbi bi clbi odir filename =
MaximalDebugInfo -> ["-g3"]
)
++ cxxOptions bi
, ghcOptCcProgram =
maybeToFlag $
programPath
<$> lookupProgram gccProgram (withPrograms lbi)
, -- Similarly, you need to add a split for cxx-sources in the configure script.
-- The configure split can be found here: https://github.com/haskell/cabal/pull/10844
ghcOptGppProgram =
ghcOptionsSince
(mkVersion [9, 4])
(compiler lbi)
(maybeToFlag $ programPath <$> lookupProgram gppProgram (withPrograms lbi))
, -- The assumption that the C++ compiler is part of the toolchain is only since ghc-9.4.
ghcOptCcProgram =
ghcOptionsBefore
(mkVersion [9, 4])
(compiler lbi)
(maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi))
, ghcOptObjDir = toFlag odir
, ghcOptExtra = hcOptions GHC bi
}
Expand Down Expand Up @@ -482,6 +491,25 @@ componentJsGhcOptions verbosity lbi bi clbi odir filename =
, ghcOptExtra = hcOptions GHC bi
}

-- Applies options only if the GHC version is greater than or
-- equal to the given one.
ghcOptionsSince :: Monoid a => Version -> Compiler -> a -> a
ghcOptionsSince ver comp defOptions =
case compilerCompatVersion GHC comp of
Just v
| v >= ver -> 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 @@ -555,6 +583,22 @@ componentGhcOptions verbosity lbi bi clbi odir =
, -- Unsupported extensions have already been checked by configure
ghcOptExtensions = toNubListR $ usedExtensions bi
, ghcOptExtensionMap = Map.fromList . compilerExtensions $ (compiler lbi)
, -- Use -pgmc to ensure that Cabal always passes cc-options, ld-options to GHC (#4435, #9801)
-- We can only do this on GHC >= 9.4, as we need https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6949
-- Without that GHC MR, this change would cause GHC to never pass -no-pie when linking,
-- which can cause breakage depending on the C toolchain use. We would have to appropriately
-- pass -pgmc-supports-no-pie as appropriate to avoid this regression.
ghcOptCcProgram =
ghcOptionsSince
(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
6 changes: 5 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,10 @@ 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 and also
-- program to use for the C++ compiler before 9.4.
, ghcOptGppProgram :: Flag FilePath
-- ^ Program to use for the C++ compiler; the @ghc -pgmcxx@ flag.
, ----------------------------
-- Language and extensions

Expand Down Expand Up @@ -874,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/ForeignOptsPgmc/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 "pgmclib.h meaning_of_life_pgmc"
meaning_of_life_pgmc :: IO CInt

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

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

The cabal file sets `ghc-options: -pgmc scripts/cc-wrapper.sh`, pointing GHC at a shell script wrapper (`scripts/cc-wrapper.sh`) instead of the system C compiler. The wrapper adds `-D__TESTOPT_PGMC__=66` to every compilation and then delegates to the real `cc`. The C source requires `__TESTOPT_PGMC__` 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: .
13 changes: 13 additions & 0 deletions cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc/cabal.test.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Test.Cabal.Prelude

main = do
skipIfWindows "requires a POSIX shell script as the C compiler wrapper"
-- Use -pgmc to ensure that Cabal always passes cc-options, ld-options to GHC (#4435, #9801)
-- We can only do this on GHC >= 9.4, as we need https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6949
-- Without that GHC MR, this change would cause GHC to never pass -no-pie when linking,
-- which can cause breakage depending on the C toolchain use. We would have to appropriately
-- pass -pgmc-supports-no-pie as appropriate to avoid this regression.
cabalTest $ recordMode DoNotRecord $ do
skipUnlessGhcVersion ">= 9.4"
cabal "v2-build" ["foreign-opts-pgmc-exe"]
withPlan $ runPlanExe "foreign-opts-pgmc" "foreign-opts-pgmc-exe" []
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "pgmclib.h"

#ifndef __TESTOPT_PGMC__
#error "Did not get required __TESTOPT_PGMC__ from the -pgmc wrapper"
#endif

int meaning_of_life_pgmc() {
return __TESTOPT_PGMC__;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef PGMCLIB_H
#define PGMCLIB_H

int meaning_of_life_pgmc();

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

executable foreign-opts-pgmc-exe
main-is: Main.hs
build-depends: base
default-language: Haskell2010
include-dirs: cbits
c-sources: cbits/pgmclib.c
ghc-options: -pgmc scripts/cc-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
# Wrapper around cc that adds -D__TESTOPT_PGMC__=66 to every compilation.
# Used by the ForeignOptsPgmc test to verify that -pgmc selects this wrapper.
exec cc -D__TESTOPT_PGMC__=66 "$@"
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 following options can be ignore with -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