From 88c301298e442a2f8ce9613c022df0d4301135ea Mon Sep 17 00:00:00 2001 From: Ilia Baryshnikov Date: Wed, 8 Apr 2026 15:29:12 +0300 Subject: [PATCH 1/3] Pass *-options to GHC Refactor componentGhcOptions for pass *-options to all invoking GHC. During the refactoring process we needed to add componentGhcOptions to all Haskell sources. It was also worth add linkGhcOptions to linkLibrary same as componentGhcOptions to linkExecutable, linkFLib. Add test for PackageTests/FFI/ForeignOptsCapi to pass cc-options flags to *.h. Fixes #9801 #4435 Co-authored-by: Mikolaj Konarski --- Cabal/src/Distribution/Simple/GHC.hs | 7 +- .../Simple/GHC/Build/ExtraSources.hs | 57 ++++- .../src/Distribution/Simple/GHC/Build/Link.hs | 42 +-- Cabal/src/Distribution/Simple/GHC/Internal.hs | 240 +++++------------- Cabal/src/Distribution/Simple/GHCJS.hs | 51 ++-- Cabal/src/Distribution/Simple/Program/GHC.hs | 1 + .../PackageTests/FFI/ForeignOptsCapi/Main.hs | 15 ++ .../FFI/ForeignOptsCapi/README.md | 3 + .../FFI/ForeignOptsCapi/cabal.out | 10 + .../FFI/ForeignOptsCapi/cabal.project | 1 + .../FFI/ForeignOptsCapi/cabal.test.hs | 4 + .../FFI/ForeignOptsCapi/cbits/clib.c | 0 .../FFI/ForeignOptsCapi/cbits/clib.h | 7 + .../ForeignOptsCapi/foreign-opts-capi.cabal | 11 + changelog.d/pr-10969.md | 22 ++ 15 files changed, 232 insertions(+), 239 deletions(-) create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/Main.hs create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/README.md create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.out create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.project create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cbits/clib.c create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cbits/clib.h create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/foreign-opts-capi.cabal create mode 100644 changelog.d/pr-10969.md diff --git a/Cabal/src/Distribution/Simple/GHC.hs b/Cabal/src/Distribution/Simple/GHC.hs index 51eda011e42..b751f7bc96a 100644 --- a/Cabal/src/Distribution/Simple/GHC.hs +++ b/Cabal/src/Distribution/Simple/GHC.hs @@ -61,7 +61,6 @@ module Distribution.Simple.GHC , hcPkgInfo , registerPackage , Internal.componentGhcOptions - , Internal.componentCcGhcOptions , getGhcAppDir , getLibDir , compilerBuildWay @@ -794,7 +793,7 @@ libAbiHash verbosity _pkg_descr lbi lib clbi = do , ghcOptFPic = toFlag True , ghcOptHiSuffix = toFlag "dyn_hi" , ghcOptObjSuffix = toFlag "dyn_o" - , ghcOptExtra = hcOptions GHC libBi ++ hcSharedOptions GHC libBi + , ghcOptExtra = hcSharedOptions GHC libBi } profArgs = vanillaArgs @@ -806,7 +805,7 @@ libAbiHash verbosity _pkg_descr lbi lib clbi = do (withProfLibDetail lbi) , ghcOptHiSuffix = toFlag "p_hi" , ghcOptObjSuffix = toFlag "p_o" - , ghcOptExtra = hcOptions GHC libBi ++ hcProfOptions GHC libBi + , ghcOptExtra = hcProfOptions GHC libBi } profDynArgs = vanillaArgs @@ -820,7 +819,7 @@ libAbiHash verbosity _pkg_descr lbi lib clbi = do , ghcOptFPic = toFlag True , ghcOptHiSuffix = toFlag "p_dyn_hi" , ghcOptObjSuffix = toFlag "p_dyn_o" - , ghcOptExtra = hcOptions GHC libBi ++ hcProfSharedOptions GHC libBi + , ghcOptExtra = hcProfSharedOptions GHC libBi } ghcArgs = let (libWays, _, _) = buildWays lbi diff --git a/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs b/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs index ad273d89212..470ffceef2d 100644 --- a/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs +++ b/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs @@ -10,6 +10,7 @@ import Control.Monad import Data.Foldable import Distribution.Simple.Flag import qualified Distribution.Simple.GHC.Internal as Internal +import Distribution.Simple.Program import Distribution.Simple.Program.GHC import Distribution.Simple.Utils import Distribution.Utils.NubList @@ -17,12 +18,12 @@ import Distribution.Utils.NubList import Distribution.Types.BuildInfo import Distribution.Types.Component import Distribution.Types.TargetInfo +import Distribution.Types.Version import Distribution.Simple.Build.Inputs -import Distribution.Simple.GHC.Build.Modules +import Distribution.Simple.BuildWay import Distribution.Simple.GHC.Build.Utils import Distribution.Simple.LocalBuildInfo -import Distribution.Simple.Program.Types import Distribution.Simple.Setup.Common (commonSetupTempFileOptions) import Distribution.System (Arch (JavaScript), Platform (..)) import Distribution.Types.ComponentLocalBuildInfo @@ -77,7 +78,26 @@ buildCSources buildCSources mbMainFile = buildExtraSources "C Sources" - Internal.componentCcGhcOptions + ( \verbosity lbi bi clbi odir filename -> + (Internal.sourcesGhcOptions verbosity lbi bi clbi odir filename) + { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc + -- we want to be able to support cxx-options and cc-options separately + -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsC + ghcOptCxxOptions = + Internal.ghcOptionsSince + (mkVersion [8, 10]) + (compiler lbi) + (Internal.optimizationCFlags lbi ++ cxxOptions bi) + , -- 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. Otherwise, + -- we pass the flag only to source files #11712 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc + ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) + } + ) ( \c -> do let cFiles = cSources (componentBuildInfo c) case c of @@ -90,7 +110,26 @@ buildCSources mbMainFile = buildCxxSources mbMainFile = buildExtraSources "C++ Sources" - Internal.componentCxxGhcOptions + ( \verbosity lbi bi clbi odir filename -> + (Internal.sourcesGhcOptions verbosity lbi bi clbi odir filename) + { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc + -- we want to be able to support cxx-options and cc-options separately + -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsCxx + ghcOptCcOptions = + Internal.ghcOptionsSince + (mkVersion [8, 10]) + (compiler lbi) + (Internal.optimizationCFlags lbi ++ ccOptions bi) + , -- 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. Otherwise, + -- we pass the flag only to source files #11712 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc + ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) + } + ) ( \c -> do let cxxFiles = cxxSources (componentBuildInfo c) case c of @@ -105,7 +144,7 @@ buildJsSources _mbMainFile ghcProg buildTargetDir neededWays verbHandles = do let hasJsSupport = hostArch == JavaScript buildExtraSources "JS Sources" - Internal.componentJsGhcOptions + Internal.sourcesGhcOptions ( \c -> if hasJsSupport then -- JS files are C-like with GHC's JS backend: they are @@ -122,12 +161,12 @@ buildJsSources _mbMainFile ghcProg buildTargetDir neededWays verbHandles = do buildAsmSources _mbMainFile = buildExtraSources "Assembler Sources" - Internal.componentAsmGhcOptions + Internal.sourcesGhcOptions (asmSources . componentBuildInfo) buildCmmSources _mbMainFile = buildExtraSources "C-- Sources" - Internal.componentCmmGhcOptions + Internal.sourcesGhcOptions (cmmSources . componentBuildInfo) -- | Create 'PreBuildComponentRules' for a given type of extra build sources @@ -145,9 +184,7 @@ buildExtraSources -> GhcOptions ) -- ^ Function to determine the @'GhcOptions'@ for the - -- invocation of GHC when compiling these extra sources (e.g. - -- @'Internal.componentCxxGhcOptions'@, - -- @'Internal.componentCmmGhcOptions'@) + -- invocation of GHC when compiling these extra sources -> (Component -> [SymbolicPath Pkg File]) -- ^ View the extra sources of a component, typically from -- the build info (e.g. @'asmSources'@, @'cSources'@). diff --git a/Cabal/src/Distribution/Simple/GHC/Build/Link.hs b/Cabal/src/Distribution/Simple/GHC/Build/Link.hs index a1f07219e3e..367d12ba0fe 100644 --- a/Cabal/src/Distribution/Simple/GHC/Build/Link.hs +++ b/Cabal/src/Distribution/Simple/GHC/Build/Link.hs @@ -120,10 +120,9 @@ linkOrLoadComponent linkerOpts rpaths = mempty { ghcOptLinkOptions = - PD.ldOptions bi - ++ [ "-static" - | withFullyStaticExe lbi - ] + [ "-static" + | withFullyStaticExe lbi + ] -- Pass extra `ld-options` given -- through to GHC's linker. ++ maybe @@ -322,36 +321,11 @@ linkLibrary buildTargetDir cleanedExtraLibDirs verbosity runGhcProg lib lbi clbi -- Right now, instead, we pass the path to each object file. ghcBaseLinkArgs :: GhcOptions ghcBaseLinkArgs = - mempty - { -- TODO: This basically duplicates componentGhcOptions. - -- I think we want to do the same as we do for executables: re-use the - -- base options, and link by module names, not object paths. - ghcOptExtra = hcStaticOptions GHC libBi - , ghcOptHideAllPackages = toFlag True - , ghcOptNoAutoLinkPackages = toFlag True - , ghcOptPackageDBs = withPackageDB lbi - , ghcOptThisUnitId = case clbi of - LibComponentLocalBuildInfo{componentCompatPackageKey = pk} -> - toFlag pk - _ -> mempty - , ghcOptThisComponentId = case clbi of - LibComponentLocalBuildInfo - { componentInstantiatedWith = insts - } -> - if null insts - then mempty - else toFlag (componentComponentId clbi) - _ -> mempty - , ghcOptInstantiatedWith = case clbi of - LibComponentLocalBuildInfo - { componentInstantiatedWith = insts - } -> - insts - _ -> [] - , ghcOptPackages = - toNubListR $ - Internal.mkGhcOptPackages mempty clbi - } + Internal.linkGhcOptions (verbosityLevel verbosity) lbi libBi clbi + <> mempty + { ghcOptExtra = hcStaticOptions GHC libBi + , ghcOptNoAutoLinkPackages = toFlag True + } -- After the relocation lib is created we invoke ghc -shared -- with the dependencies spelled out as -package arguments diff --git a/Cabal/src/Distribution/Simple/GHC/Internal.hs b/Cabal/src/Distribution/Simple/GHC/Internal.hs index 1cfef4c38d4..51d01fa6773 100644 --- a/Cabal/src/Distribution/Simple/GHC/Internal.hs +++ b/Cabal/src/Distribution/Simple/GHC/Internal.hs @@ -1,6 +1,5 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} ----------------------------------------------------------------------------- @@ -20,12 +19,8 @@ module Distribution.Simple.GHC.Internal , getExtensions , targetPlatform , getGhcInfo - , componentCcGhcOptions - , componentCmmGhcOptions - , componentCxxGhcOptions - , componentAsmGhcOptions - , componentJsGhcOptions , componentGhcOptions + , sourcesGhcOptions , mkGHCiLibName , mkGHCiProfLibName , filterGhciFlags @@ -35,6 +30,9 @@ module Distribution.Simple.GHC.Internal , substTopDir , checkPackageDbEnvVar , profDetailLevelFlag + , ghcOptionsSince + , linkGhcOptions + , optimizationCFlags -- * GHC platform and version strings , ghcArchString @@ -59,16 +57,14 @@ import qualified Data.Map as Map import qualified Data.Set as Set import Distribution.Backpack import Distribution.Compat.Stack -import qualified Distribution.InstalledPackageInfo as IPI import Distribution.Lex import qualified Distribution.ModuleName as ModuleName -import Distribution.PackageDescription import Distribution.Parsec (simpleParsec) import Distribution.Pretty (prettyShow) import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler import Distribution.Simple.Errors -import Distribution.Simple.Flag (Flag, maybeToFlag, toFlag, pattern NoFlag) +import Distribution.Simple.Flag import Distribution.Simple.GHC.ImplInfo import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program @@ -76,9 +72,14 @@ import Distribution.Simple.Program.GHC import Distribution.Simple.Setup.Common (extraCompilationArtifacts) import Distribution.Simple.Utils import Distribution.System +import Distribution.Types.BuildInfo import Distribution.Types.ComponentLocalBuildInfo import Distribution.Types.GivenComponent +import qualified Distribution.Types.InstalledPackageInfo as IPI +import Distribution.Types.Library import Distribution.Types.LocalBuildInfo +import Distribution.Types.ModuleRenaming +import Distribution.Types.PackageName import Distribution.Types.TargetInfo import Distribution.Types.UnitId import Distribution.Types.Version @@ -345,7 +346,7 @@ includePaths lbi bi clbi odir = | dir <- mapMaybe (symbolicPathRelative_maybe . unsafeCoerceSymbolicPath) $ includeDirs bi ] -componentCcGhcOptions +sourcesGhcOptions :: VerbosityLevel -> LocalBuildInfo -> BuildInfo @@ -353,134 +354,34 @@ componentCcGhcOptions -> SymbolicPath Pkg (Dir Artifacts) -> SymbolicPath Pkg File -> GhcOptions -componentCcGhcOptions verbosity lbi bi clbi odir filename = - mempty - { -- Respect -v0, but don't crank up verbosity on GHC if - -- Cabal verbosity is requested. For that, use --ghc-option=-v instead! - ghcOptVerbosity = toFlag (min verbosity Normal) +sourcesGhcOptions verbosity lbi bi clbi odir filename = + (componentGhcOptions verbosity lbi bi clbi odir) + { ghcOptVerbosity = toFlag (min verbosity Normal) , ghcOptMode = toFlag GhcModeCompile , ghcOptInputFiles = toNubListR [filename] - , ghcOptCppIncludePath = includePaths lbi bi clbi odir - , ghcOptHideAllPackages = toFlag True - , ghcOptPackageDBs = withPackageDB lbi - , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi - , ghcOptCcOptions = - ( case withOptimization lbi of - NoOptimisation -> [] - _ -> ["-O2"] - ) - ++ ( case withDebugInfo lbi of - NoDebugInfo -> [] - MinimalDebugInfo -> ["-g1"] - NormalDebugInfo -> ["-g"] - MaximalDebugInfo -> ["-g3"] - ) - ++ ccOptions bi - , ghcOptCcProgram = - maybeToFlag $ - programPath - <$> lookupProgram gccProgram (withPrograms lbi) - , ghcOptObjDir = toFlag odir - , ghcOptExtra = hcOptions GHC bi - } - -componentCxxGhcOptions - :: VerbosityLevel - -> LocalBuildInfo - -> BuildInfo - -> ComponentLocalBuildInfo - -> SymbolicPath Pkg (Dir Artifacts) - -> SymbolicPath Pkg File - -> GhcOptions -componentCxxGhcOptions verbosity lbi bi clbi odir filename = - mempty - { -- Respect -v0, but don't crank up verbosity on GHC if - -- Cabal verbosity is requested. For that, use --ghc-option=-v instead! - ghcOptVerbosity = toFlag (min verbosity Normal) - , ghcOptMode = toFlag GhcModeCompile - , ghcOptInputFiles = toNubListR [filename] - , ghcOptCppIncludePath = includePaths lbi bi clbi odir - , ghcOptHideAllPackages = toFlag True - , ghcOptPackageDBs = withPackageDB lbi - , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi - , ghcOptCxxOptions = - ( case withOptimization lbi of - NoOptimisation -> [] - _ -> ["-O2"] - ) - ++ ( case withDebugInfo lbi of - NoDebugInfo -> [] - MinimalDebugInfo -> ["-g1"] - NormalDebugInfo -> ["-g"] - MaximalDebugInfo -> ["-g3"] - ) - ++ cxxOptions bi - , ghcOptCcProgram = - maybeToFlag $ - programPath - <$> lookupProgram gccProgram (withPrograms lbi) , ghcOptObjDir = toFlag odir - , ghcOptExtra = hcOptions GHC bi - } - -componentAsmGhcOptions - :: VerbosityLevel - -> LocalBuildInfo - -> BuildInfo - -> ComponentLocalBuildInfo - -> SymbolicPath Pkg (Dir Artifacts) - -> SymbolicPath Pkg File - -> GhcOptions -componentAsmGhcOptions verbosity lbi bi clbi odir filename = - mempty - { -- Respect -v0, but don't crank up verbosity on GHC if - -- Cabal verbosity is requested. For that, use --ghc-option=-v instead! - ghcOptVerbosity = toFlag (min verbosity Normal) - , ghcOptMode = toFlag GhcModeCompile - , ghcOptInputFiles = toNubListR [filename] - , ghcOptCppIncludePath = includePaths lbi bi clbi odir - , ghcOptHideAllPackages = toFlag True - , ghcOptPackageDBs = withPackageDB lbi , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi - , ghcOptAsmOptions = - ( case withOptimization lbi of - NoOptimisation -> [] - _ -> ["-O2"] - ) - ++ ( case withDebugInfo lbi of - NoDebugInfo -> [] - MinimalDebugInfo -> ["-g1"] - NormalDebugInfo -> ["-g"] - MaximalDebugInfo -> ["-g3"] - ) - ++ asmOptions bi - , ghcOptObjDir = toFlag odir - , ghcOptExtra = hcOptions GHC bi } -componentJsGhcOptions - :: VerbosityLevel - -> LocalBuildInfo - -> BuildInfo - -> ComponentLocalBuildInfo - -> SymbolicPath Pkg (Dir Artifacts) - -> SymbolicPath Pkg File - -> GhcOptions -componentJsGhcOptions verbosity lbi bi clbi odir filename = - mempty - { -- Respect -v0, but don't crank up verbosity on GHC if - -- Cabal verbosity is requested. For that, use --ghc-option=-v instead! - ghcOptVerbosity = toFlag (min verbosity Normal) - , ghcOptMode = toFlag GhcModeCompile - , ghcOptInputFiles = toNubListR [filename] - , ghcOptJSppOptions = jsppOptions bi - , ghcOptCppIncludePath = includePaths lbi bi clbi odir - , ghcOptHideAllPackages = toFlag True - , ghcOptPackageDBs = withPackageDB lbi - , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi - , ghcOptObjDir = toFlag odir - , ghcOptExtra = hcOptions GHC bi - } +optimizationCFlags :: LocalBuildInfo -> [String] +optimizationCFlags lbi = + ( case withOptimization lbi of + -- see --disable-optimization + NoOptimisation -> [] + -- '*-options: -O[n]' is generally not needed. When building with + -- optimisations Cabal automatically adds '-O2' for * code. Setting it + -- yourself interferes with the --disable-optimization flag. + -- see https://github.com/haskell/cabal/pull/8250 + NormalOptimisation -> ["-O2"] + -- see --enable-optimization + MaximumOptimisation -> ["-O2"] + ) + ++ ( case withDebugInfo lbi of + NoDebugInfo -> [] + MinimalDebugInfo -> ["-g1"] + NormalDebugInfo -> ["-g"] + MaximalDebugInfo -> ["-g3"] + ) -- Applies options only if the GHC version is greater than or -- equal to the given one. @@ -500,11 +401,42 @@ componentGhcOptions -> SymbolicPath Pkg (Dir build) -> GhcOptions componentGhcOptions verbosity lbi bi clbi odir = + let implInfo = getImplInfo $ compiler lbi + in (linkGhcOptions verbosity lbi bi clbi) + { ghcOptSourcePath = + toNubListR $ + (hsSourceDirs bi) + ++ [coerceSymbolicPath odir] + ++ [autogenComponentModulesDir lbi clbi] + ++ [autogenPackageModulesDir lbi] + , ghcOptCppIncludePath = includePaths lbi bi clbi odir + , ghcOptObjDir = toFlag $ coerceSymbolicPath odir + , ghcOptHiDir = toFlag $ coerceSymbolicPath odir + , ghcOptHieDir = bool NoFlag (toFlag $ coerceSymbolicPath odir (extraCompilationArtifacts makeRelativePathEx "hie")) $ flagHie implInfo + , ghcOptStubDir = toFlag $ coerceSymbolicPath odir + , ghcOptOutputDir = toFlag $ coerceSymbolicPath odir + , ghcOptBytecodeDir = toFlag $ coerceSymbolicPath odir + } + +linkGhcOptions + :: VerbosityLevel + -> LocalBuildInfo + -> BuildInfo + -> ComponentLocalBuildInfo + -> GhcOptions +linkGhcOptions verbosity lbi bi clbi = let implInfo = getImplInfo $ compiler lbi in mempty { -- Respect -v0, but don't crank up verbosity on GHC if -- Cabal verbosity is requested. For that, use --ghc-option=-v instead! ghcOptVerbosity = toFlag (min verbosity Normal) + , ghcOptCcOptions = ccOptions bi + , ghcOptCxxOptions = cxxOptions bi + , ghcOptAsmOptions = asmOptions bi + , ghcOptLinkOptions = ldOptions bi + , ghcOptCppOptions = cppOptions bi + , ghcOptJSppOptions = jsppOptions bi + , ghcOptExtra = hcOptions GHC bi <> cmmOptions bi , ghcOptCabal = toFlag True , ghcOptThisUnitId = case clbi of LibComponentLocalBuildInfo{componentCompatPackageKey = pk} -> @@ -539,27 +471,11 @@ componentGhcOptions verbosity lbi bi clbi odir = , ghcOptSplitSections = toFlag (splitSections lbi) , ghcOptSplitObjs = toFlag (splitObjs lbi) , ghcOptSourcePathClear = toFlag True - , ghcOptSourcePath = - toNubListR $ - (hsSourceDirs bi) - ++ [coerceSymbolicPath odir] - ++ [autogenComponentModulesDir lbi clbi] - ++ [autogenPackageModulesDir lbi] - , ghcOptCppIncludePath = includePaths lbi bi clbi odir - , ghcOptCppOptions = cppOptions bi - , ghcOptJSppOptions = jsppOptions bi , ghcOptCppIncludes = toNubListR [coerceSymbolicPath (autogenComponentModulesDir lbi clbi makeRelativePathEx cppHeaderName)] , ghcOptFfiIncludes = toNubListR $ map getSymbolicPath $ includes bi - , ghcOptObjDir = toFlag $ coerceSymbolicPath odir - , ghcOptHiDir = toFlag $ coerceSymbolicPath odir - , ghcOptHieDir = bool NoFlag (toFlag $ coerceSymbolicPath odir (extraCompilationArtifacts makeRelativePathEx "hie")) $ flagHie implInfo - , ghcOptBytecodeDir = toFlag $ coerceSymbolicPath odir - , ghcOptStubDir = toFlag $ coerceSymbolicPath odir - , ghcOptOutputDir = toFlag $ coerceSymbolicPath odir , ghcOptOptimisation = toGhcOptimisation (withOptimization lbi) , ghcOptDebugInfo = toFlag (withDebugInfo lbi) - , ghcOptExtra = hcOptions GHC bi , ghcOptExtraPath = toNubListR exe_paths , ghcOptLanguage = toFlag (fromMaybe Haskell98 (defaultLanguage bi)) , -- Unsupported extensions have already been checked by configure @@ -589,34 +505,6 @@ toGhcOptimisation NoOptimisation = mempty -- TODO perhaps override? toGhcOptimisation NormalOptimisation = toFlag GhcNormalOptimisation toGhcOptimisation MaximumOptimisation = toFlag GhcMaximumOptimisation -componentCmmGhcOptions - :: VerbosityLevel - -> LocalBuildInfo - -> BuildInfo - -> ComponentLocalBuildInfo - -> SymbolicPath Pkg (Dir Artifacts) - -> SymbolicPath Pkg File - -> GhcOptions -componentCmmGhcOptions verbosity lbi bi clbi odir filename = - mempty - { -- Respect -v0, but don't crank up verbosity on GHC if - -- Cabal verbosity is requested. For that, use --ghc-option=-v instead! - ghcOptVerbosity = toFlag (min verbosity Normal) - , ghcOptMode = toFlag GhcModeCompile - , ghcOptInputFiles = toNubListR [filename] - , ghcOptCppIncludePath = includePaths lbi bi clbi odir - , ghcOptCppOptions = cppOptions bi - , ghcOptCppIncludes = - toNubListR [autogenComponentModulesDir lbi clbi makeRelativePathEx cppHeaderName] - , ghcOptHideAllPackages = toFlag True - , ghcOptPackageDBs = withPackageDB lbi - , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi - , ghcOptOptimisation = toGhcOptimisation (withOptimization lbi) - , ghcOptDebugInfo = toFlag (withDebugInfo lbi) - , ghcOptExtra = hcOptions GHC bi <> cmmOptions bi - , ghcOptObjDir = toFlag odir - } - -- | Strip out flags that are not supported in ghci filterGhciFlags :: [String] -> [String] filterGhciFlags = filter supported diff --git a/Cabal/src/Distribution/Simple/GHCJS.hs b/Cabal/src/Distribution/Simple/GHCJS.hs index 838c87cf5b2..4c96a34984a 100644 --- a/Cabal/src/Distribution/Simple/GHCJS.hs +++ b/Cabal/src/Distribution/Simple/GHCJS.hs @@ -24,7 +24,6 @@ module Distribution.Simple.GHCJS , hcPkgInfo , registerPackage , componentGhcOptions - , Internal.componentCcGhcOptions , getLibDir , isDynamic , getGlobalPackageDB @@ -1443,13 +1442,24 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do sequence_ [ do let baseCxxOpts = - Internal.componentCxxGhcOptions - (verbosityLevel verbosity) - lbi - bnfo - clbi - tmpDir - filename + (Internal.sourcesGhcOptions (verbosityLevel verbosity) lbi bnfo clbi odir filename) + { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc + -- we want to be able to support cxx-options and cc-options separately + -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsCxx + ghcOptCcOptions = + Internal.ghcOptionsSince + (mkVersion [8, 10]) + (compiler lbi) + (Internal.optimizationCFlags lbi ++ ccOptions bnfo) + , -- 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. Otherwise, + -- we pass the flag only to source files #11712 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc + ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) + } vanillaCxxOpts = if isGhcDynamic then -- Dynamic GHC requires C++ sources to be built @@ -1489,13 +1499,24 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do sequence_ [ do let baseCcOpts = - Internal.componentCcGhcOptions - (verbosityLevel verbosity) - lbi - bnfo - clbi - tmpDir - filename + (Internal.sourcesGhcOptions (verbosityLevel verbosity) lbi bnfo clbi tmpDir filename) + { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc + -- we want to be able to support cxx-options and cc-options separately + -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsC + ghcOptCxxOptions = + Internal.ghcOptionsSince + (mkVersion [8, 10]) + (compiler lbi) + (Internal.optimizationCFlags lbi ++ cxxOptions bnfo) + , -- 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. Otherwise, + -- we pass the flag only to source files #11712 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc + ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) + } vanillaCcOpts = if isGhcDynamic then -- Dynamic GHC requires C sources to be built diff --git a/Cabal/src/Distribution/Simple/Program/GHC.hs b/Cabal/src/Distribution/Simple/Program/GHC.hs index d68ae7af414..e049749ee7e 100644 --- a/Cabal/src/Distribution/Simple/Program/GHC.hs +++ b/Cabal/src/Distribution/Simple/Program/GHC.hs @@ -868,6 +868,7 @@ renderGhcOptions comp _platform@(Platform _arch os) opts ] , ["-optc" ++ opt | opt <- ghcOptCcOptions opts] , -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc + -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 let cxxflag = case compilerCompatVersion GHC comp of Just v | v >= mkVersion [8, 10] -> "-optcxx" _ -> "-optc" diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/Main.hs b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/Main.hs new file mode 100644 index 00000000000..f46505ab473 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/Main.hs @@ -0,0 +1,15 @@ +{-# LANGUAGE CApiFFI #-} + +module Main where + +import Foreign.C (CInt (..)) + +foreign import capi "clib.h myplus" + myplus :: CInt -> CInt -> IO CInt + +main :: IO () +main = do + result <- myplus 5 6 + if (result == 11) + then putStrLn ("The result is " ++ show result) + else error ("Expected value 11, got " ++ show result) diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/README.md b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/README.md new file mode 100644 index 00000000000..e9484711f3d --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/README.md @@ -0,0 +1,3 @@ +# ForeignOptsCapi + +This test case asserts that cabal passes `cc-options` to the C compiler when use `foreign import capi`. diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.out b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.out new file mode 100644 index 00000000000..7b6f22a35ad --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.out @@ -0,0 +1,10 @@ +# cabal v2-build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - foreign-opts-capi-0.1 (exe:foreign-opts-capi-exe) (first run) +Configuring executable 'foreign-opts-capi-exe' for foreign-opts-capi-0.1... +Preprocessing executable 'foreign-opts-capi-exe' for foreign-opts-capi-0.1... +Building executable 'foreign-opts-capi-exe' for foreign-opts-capi-0.1... +# foreign-opts-capi foreign-opts-capi-exe +The result is 11 diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.project b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.project new file mode 100644 index 00000000000..e6fdbadb439 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.project @@ -0,0 +1 @@ +packages: . diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs new file mode 100644 index 00000000000..dc22334a884 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs @@ -0,0 +1,4 @@ +import Test.Cabal.Prelude +main = cabalTest $ do + cabal "v2-build" ["foreign-opts-capi-exe"] + withPlan $ runPlanExe "foreign-opts-capi" "foreign-opts-capi-exe" [] diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cbits/clib.c b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cbits/clib.c new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cbits/clib.h b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cbits/clib.h new file mode 100644 index 00000000000..d3f26d370ca --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cbits/clib.h @@ -0,0 +1,7 @@ +#ifndef MYDEF +#error "Did not get required MYDEF from cc-options" +#endif + +static inline int myplus(int a, int b) { + return a + b; +} diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/foreign-opts-capi.cabal b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/foreign-opts-capi.cabal new file mode 100644 index 00000000000..68f3b45603e --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/foreign-opts-capi.cabal @@ -0,0 +1,11 @@ +cabal-version: 3.0 +name: foreign-opts-capi +version: 0.1 +build-type: Simple + +executable foreign-opts-capi-exe + main-is: Main.hs + build-depends: base + default-language: Haskell2010 + include-dirs: cbits + cc-options: -DMYDEF=1 diff --git a/changelog.d/pr-10969.md b/changelog.d/pr-10969.md new file mode 100644 index 00000000000..920ff6a64ef --- /dev/null +++ b/changelog.d/pr-10969.md @@ -0,0 +1,22 @@ +--- +synopsis: Pass *-options and -pgmc gcc to GHC when compiling ordinary Haskell sources +packages: [Cabal] +prs: 10969 +issues: [9801, 4435] +significance: significant +--- + +`cc-options`, `cxx-options`, `jspp-options`, `asm-options`, +`cmm-options`, `ld-options`, `cpp-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. + +[Historical note] +Before `ghc` 8.10, `cc-options` and `cxx-options` were passed via one flag `-optc`, +then in 8.10 they started to split into `-optc` and `-optcxx`, cabal picked up +this change and started to give flags separately not only for new versions, +but also for old ones. That is, now for 8.8 we send `-optc` flags separately for +different sources. + +`cc-options` and `cxx-options` still need to be separated (C/C++) for versions below 8.10. From db141072e3de88dbf00d6cb3ff4064b197074780 Mon Sep 17 00:00:00 2001 From: Ilia Baryshnikov Date: Thu, 14 May 2026 14:58:17 +0300 Subject: [PATCH 2/3] add test for foreign source in TemplateHaskell Co-authored-by: Yuriy Syrovetskiy --- .../FFI/ForeignOptsCapi/cabal.test.hs | 5 +++-- .../addForeignSourceC.hs | 11 +++++++++++ .../addForeignSourceCxx.hs | 11 +++++++++++ .../FFI/ForeignSourceTemplateHaskell/cabal.out | 15 +++++++++++++++ .../ForeignSourceTemplateHaskell/cabal.project | 1 + .../ForeignSourceTemplateHaskell/cabal.test.hs | 5 +++++ .../foreign-source-template-haskell.cabal | 18 ++++++++++++++++++ 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceC.hs create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceCxx.hs create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.out create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.project create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/foreign-source-template-haskell.cabal diff --git a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs index dc22334a884..a6a03355445 100644 --- a/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs +++ b/cabal-testsuite/PackageTests/FFI/ForeignOptsCapi/cabal.test.hs @@ -1,4 +1,5 @@ import Test.Cabal.Prelude + main = cabalTest $ do - cabal "v2-build" ["foreign-opts-capi-exe"] - withPlan $ runPlanExe "foreign-opts-capi" "foreign-opts-capi-exe" [] + cabal "v2-build" ["foreign-opts-capi-exe"] + withPlan $ runPlanExe "foreign-opts-capi" "foreign-opts-capi-exe" [] diff --git a/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceC.hs b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceC.hs new file mode 100644 index 00000000000..4e21b6cbf89 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceC.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE TemplateHaskell #-} + +import Language.Haskell.TH.Syntax (ForeignSrcLang (LangC), addForeignSource) + +$( do + addForeignSource LangC "int test_one_c() { return ONE; }" + pure [] + ) + +main :: IO () +main = pure () diff --git a/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceCxx.hs b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceCxx.hs new file mode 100644 index 00000000000..a43122ec805 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/addForeignSourceCxx.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE TemplateHaskell #-} + +import Language.Haskell.TH.Syntax (ForeignSrcLang (LangCxx), addForeignSource) + +$( do + addForeignSource LangCxx "int test_two_cxx() { return TWO; }" + pure [] + ) + +main :: IO () +main = pure () diff --git a/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.out b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.out new file mode 100644 index 00000000000..cf2390b786a --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.out @@ -0,0 +1,15 @@ +# cabal v2-build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - foreign-source-template-haskell-0.1 (exe:addForeignSourceC) (first run) +Configuring executable 'addForeignSourceC' for foreign-source-template-haskell-0.1... +Preprocessing executable 'addForeignSourceC' for foreign-source-template-haskell-0.1... +Building executable 'addForeignSourceC' for foreign-source-template-haskell-0.1... +# cabal v2-build +Build profile: -w ghc- -O1 +In order, the following will be built: + - foreign-source-template-haskell-0.1 (exe:addForeignSourceCxx) (first run) +Configuring executable 'addForeignSourceCxx' for foreign-source-template-haskell-0.1... +Preprocessing executable 'addForeignSourceCxx' for foreign-source-template-haskell-0.1... +Building executable 'addForeignSourceCxx' for foreign-source-template-haskell-0.1... diff --git a/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.project b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.project new file mode 100644 index 00000000000..e6fdbadb439 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.project @@ -0,0 +1 @@ +packages: . diff --git a/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.test.hs b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.test.hs new file mode 100644 index 00000000000..01efef746e1 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/cabal.test.hs @@ -0,0 +1,5 @@ +import Test.Cabal.Prelude + +main = cabalTest $ do + cabal "v2-build" ["addForeignSourceC"] + cabal "v2-build" ["addForeignSourceCxx"] diff --git a/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/foreign-source-template-haskell.cabal b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/foreign-source-template-haskell.cabal new file mode 100644 index 00000000000..6123c0387c4 --- /dev/null +++ b/cabal-testsuite/PackageTests/FFI/ForeignSourceTemplateHaskell/foreign-source-template-haskell.cabal @@ -0,0 +1,18 @@ +cabal-version: 3.8 +name: foreign-source-template-haskell +version: 0.1 +build-type: Simple + +executable addForeignSourceC + build-depends: base, template-haskell + + main-is: addForeignSourceC.hs + + cc-options: -DONE=1 + +executable addForeignSourceCxx + build-depends: base, template-haskell + + main-is: addForeignSourceCxx.hs + + cxx-options: -DTWO=2 From 1da4f9eec259a76acf2fafc9a2dca79723014578 Mon Sep 17 00:00:00 2001 From: Ilia Baryshnikov Date: Mon, 18 May 2026 15:20:05 +0300 Subject: [PATCH 3/3] [Historical note] Before GHC 8.10, Cabal passed both `cc-options` and `cxx-options` via a single GHC flag, `-optc`. When GHC 8.10 introduced `-optcxx`, Cabal picked up this change: `cc-options` and `cxx-options` are now mapped to `-optc` and `-optcxx`, respectively. However, Cabal also changed its behavior for older GHC versions (8.8 and below): it now sends `cc-options` and `cxx-options` through separate instances of `-optc`. Co-authored-by: Artem Pelenitsyn --- .../Simple/GHC/Build/ExtraSources.hs | 43 +------------ Cabal/src/Distribution/Simple/GHC/Internal.hs | 61 +++++++++++++++++++ Cabal/src/Distribution/Simple/GHCJS.hs | 38 +----------- changelog.d/pr-10969.md | 7 --- 4 files changed, 65 insertions(+), 84 deletions(-) diff --git a/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs b/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs index 470ffceef2d..d2bc73f2a67 100644 --- a/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs +++ b/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs @@ -18,7 +18,6 @@ import Distribution.Utils.NubList import Distribution.Types.BuildInfo import Distribution.Types.Component import Distribution.Types.TargetInfo -import Distribution.Types.Version import Distribution.Simple.Build.Inputs import Distribution.Simple.BuildWay @@ -78,26 +77,7 @@ buildCSources buildCSources mbMainFile = buildExtraSources "C Sources" - ( \verbosity lbi bi clbi odir filename -> - (Internal.sourcesGhcOptions verbosity lbi bi clbi odir filename) - { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc - -- we want to be able to support cxx-options and cc-options separately - -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsC - ghcOptCxxOptions = - Internal.ghcOptionsSince - (mkVersion [8, 10]) - (compiler lbi) - (Internal.optimizationCFlags lbi ++ cxxOptions bi) - , -- 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. Otherwise, - -- we pass the flag only to source files #11712 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc - ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) - } - ) + (Internal.splitCandCxxOptions Internal.CcProgram) ( \c -> do let cFiles = cSources (componentBuildInfo c) case c of @@ -110,26 +90,7 @@ buildCSources mbMainFile = buildCxxSources mbMainFile = buildExtraSources "C++ Sources" - ( \verbosity lbi bi clbi odir filename -> - (Internal.sourcesGhcOptions verbosity lbi bi clbi odir filename) - { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc - -- we want to be able to support cxx-options and cc-options separately - -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsCxx - ghcOptCcOptions = - Internal.ghcOptionsSince - (mkVersion [8, 10]) - (compiler lbi) - (Internal.optimizationCFlags lbi ++ ccOptions bi) - , -- 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. Otherwise, - -- we pass the flag only to source files #11712 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc - ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) - } - ) + (Internal.splitCandCxxOptions Internal.CxxProgram) ( \c -> do let cxxFiles = cxxSources (componentBuildInfo c) case c of diff --git a/Cabal/src/Distribution/Simple/GHC/Internal.hs b/Cabal/src/Distribution/Simple/GHC/Internal.hs index 51d01fa6773..ac2116b4b09 100644 --- a/Cabal/src/Distribution/Simple/GHC/Internal.hs +++ b/Cabal/src/Distribution/Simple/GHC/Internal.hs @@ -33,6 +33,8 @@ module Distribution.Simple.GHC.Internal , ghcOptionsSince , linkGhcOptions , optimizationCFlags + , splitCandCxxOptions + , SplitSource (..) -- * GHC platform and version strings , ghcArchString @@ -346,6 +348,65 @@ includePaths lbi bi clbi odir = | dir <- mapMaybe (symbolicPathRelative_maybe . unsafeCoerceSymbolicPath) $ includeDirs bi ] +data SplitSource = CcProgram | CxxProgram + +splitCandCxxOptions + :: SplitSource + -> VerbosityLevel + -> LocalBuildInfo + -> BuildInfo + -> ComponentLocalBuildInfo + -> SymbolicPathX 'AllowAbsolute Pkg (Dir Artifacts) + -> SymbolicPathX 'AllowAbsolute Pkg File + -> GhcOptions +splitCandCxxOptions source verbosity lbi bi clbi odir filename = case source of + CxxProgram -> + setCcProgram $ setCcOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename + CcProgram -> + setCcProgram $ setCxxOptions $ sourcesGhcOptions verbosity lbi bi clbi odir filename + where + setCcOptions xxx = + xxx + { -- C compiler options: GHC >= 8.10 requires -optcxx, older requires -optc + -- we want to be able to support cxx-options and cc-options separately + -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsCxx + ghcOptCcOptions = + ghcOptionsSince + (mkVersion [8, 10]) + (compiler lbi) + (optimizationCFlags lbi ++ ccOptions bi) + } + setCxxOptions xxx = + xxx + { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc + -- we want to be able to support cxx-options and cc-options separately + -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsC + ghcOptCxxOptions = + ghcOptionsSince + (mkVersion [8, 10]) + (compiler lbi) + (optimizationCFlags lbi ++ cxxOptions bi) + } + setCcProgram xxx = + xxx + { -- We pass -pgmc to ensure that GHC respects cc-options and ld-options (#4435, #9801). + -- However, we deliberately restrict this flag ONLY to C and C++ source files. + -- We do not pass it globally (e.g., during linking or ordinary Haskell compilation) + -- for two reasons: + -- 1. Status quo: This preserves Cabal's historical behavior. + -- 2. GHC < 9.4 bug (https://gitlab.haskell.org/ghc/ghc/-/issues/15319): Before GHC 9.4, + -- passing a custom C compiler via -pgmc caused GHC to automatically disable the + -- -no-pie flag (which is critical during linking on auto-hardened toolchains). + -- Passing -pgmc globally would therefore break linking on older GHCs. + -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6949 fixed this by moving + -- the -no-pie check to -pgml, but scoping -pgmc exclusively to C/C++ compilation + -- protects older GHC versions from this link-time breakage. + -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc + ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) + } + sourcesGhcOptions :: VerbosityLevel -> LocalBuildInfo diff --git a/Cabal/src/Distribution/Simple/GHCJS.hs b/Cabal/src/Distribution/Simple/GHCJS.hs index 4c96a34984a..2fb3ab6bb41 100644 --- a/Cabal/src/Distribution/Simple/GHCJS.hs +++ b/Cabal/src/Distribution/Simple/GHCJS.hs @@ -1442,24 +1442,7 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do sequence_ [ do let baseCxxOpts = - (Internal.sourcesGhcOptions (verbosityLevel verbosity) lbi bnfo clbi odir filename) - { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc - -- we want to be able to support cxx-options and cc-options separately - -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsCxx - ghcOptCcOptions = - Internal.ghcOptionsSince - (mkVersion [8, 10]) - (compiler lbi) - (Internal.optimizationCFlags lbi ++ ccOptions bnfo) - , -- 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. Otherwise, - -- we pass the flag only to source files #11712 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc - ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) - } + (Internal.splitCandCxxOptions Internal.CxxProgram (verbosityLevel verbosity) lbi bnfo clbi odir filename) vanillaCxxOpts = if isGhcDynamic then -- Dynamic GHC requires C++ sources to be built @@ -1499,24 +1482,7 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do sequence_ [ do let baseCcOpts = - (Internal.sourcesGhcOptions (verbosityLevel verbosity) lbi bnfo clbi tmpDir filename) - { -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc - -- we want to be able to support cxx-options and cc-options separately - -- https://gitlab.haskell.org/ghc/ghc/-/issues/16477 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsC - ghcOptCxxOptions = - Internal.ghcOptionsSince - (mkVersion [8, 10]) - (compiler lbi) - (Internal.optimizationCFlags lbi ++ cxxOptions bnfo) - , -- 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. Otherwise, - -- we pass the flag only to source files #11712 - -- see example in cabal-testsuite/PackageTests/FFI/ForeignOptsPgmc - ghcOptCcProgram = maybeToFlag $ programPath <$> lookupProgram gccProgram (withPrograms lbi) - } + (Internal.splitCandCxxOptions Internal.CcProgram (verbosityLevel verbosity) lbi bnfo clbi tmpDir filename) vanillaCcOpts = if isGhcDynamic then -- Dynamic GHC requires C sources to be built diff --git a/changelog.d/pr-10969.md b/changelog.d/pr-10969.md index 920ff6a64ef..05f59fac3a7 100644 --- a/changelog.d/pr-10969.md +++ b/changelog.d/pr-10969.md @@ -12,11 +12,4 @@ 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. -[Historical note] -Before `ghc` 8.10, `cc-options` and `cxx-options` were passed via one flag `-optc`, -then in 8.10 they started to split into `-optc` and `-optcxx`, cabal picked up -this change and started to give flags separately not only for new versions, -but also for old ones. That is, now for 8.8 we send `-optc` flags separately for -different sources. - `cc-options` and `cxx-options` still need to be separated (C/C++) for versions below 8.10.