diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 336411c9158..51d33856334 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -4647,6 +4647,11 @@ determineCoverageFor configuredPkg plan = -- | Look up and merge the options from the project config that apply to all -- packages, all project local packages, and to specific named packages. +-- +-- Options are combined as @all-packages <> specific-package <> local@ so +-- that, for local packages, the top-level (and @program-options@) options +-- take precedence over the @package@ stanza, as documented. See +-- . lookupPerPkgOption :: (Package pkg, Monoid m) => (pkg -> Bool) @@ -4657,10 +4662,10 @@ lookupPerPkgOption -> (PackageConfig -> m) -> m lookupPerPkgOption isLocalPkg allPackagesConfig localPackagesConfig perPackageConfig pkg f = - global `mappend` local `mappend` perpkg + global `mappend` perpkg `mappend` local where global = f allPackagesConfig + perpkg = maybe mempty f (Map.lookup (packageName pkg) perPackageConfig) local | isLocalPkg pkg = f localPackagesConfig | otherwise = mempty - perpkg = maybe mempty f (Map.lookup (packageName pkg) perPackageConfig) diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs index 1381b66642c..083ee7e6b91 100644 --- a/cabal-install/tests/IntegrationTests2.hs +++ b/cabal-install/tests/IntegrationTests2.hs @@ -193,6 +193,7 @@ tests config = , testCase "program options scope all" (testProgramOptionsAll config) , testCase "program options scope local" (testProgramOptionsLocal config) , testCase "program options scope specific" (testProgramOptionsSpecific config) + , testCase "program options scope combined" (testProgramOptionsCombined config) ] , dependentTestGroup "Flag tests" @@ -2112,6 +2113,45 @@ testProgramOptionsSpecific config0 = do { packageConfigProgramArgs = programArgs } +-- | Test that program options specified at the top level (via the +-- @program-options@ stanza) are applied *after* the ones specified for a +-- specific package, so that they can override them (see #8900). +testProgramOptionsCombined :: ProjectConfig -> Assertion +testProgramOptionsCombined config0 = do + (_, elaboratedPlan, _) <- planProject testdir config + let packages = filterConfiguredPackages $ InstallPlan.toList elaboratedPlan + + assertEqual + "q" + (Just [ghcSpecificFlag, ghcLocalFlag]) + (getProgArgs packages "q") + assertEqual + "p" + Nothing + (getProgArgs packages "p") + where + testdir = "regression/program-options" + ghcLocalFlag = "-fno-full-laziness" + ghcSpecificFlag = "-fno-float-in" + + localArgs = MapMappend (Map.fromList [("ghc", [ghcLocalFlag])]) + specificConfig = + mempty + { packageConfigProgramArgs = MapMappend (Map.fromList [("ghc", [ghcSpecificFlag])]) + } + + -- Insert a flag into the local config and a different one into the + -- specific package "q" config. + config = + config0 + { projectConfigLocalPackages = + (projectConfigLocalPackages config0) + { packageConfigProgramArgs = localArgs + } + , projectConfigSpecificPackage = + MapMappend (Map.fromList [(mkPackageName "q", specificConfig)]) + } + filterConfiguredPackages :: [ElaboratedPlanPackage] -> [ElaboratedConfiguredPackage] filterConfiguredPackages [] = [] filterConfiguredPackages (InstallPlan.PreExisting _ : pkgs) = filterConfiguredPackages pkgs diff --git a/cabal-testsuite/PackageTests/ProgramOptions/Main.hs b/cabal-testsuite/PackageTests/ProgramOptions/Main.hs new file mode 100644 index 00000000000..9c956c95541 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProgramOptions/Main.hs @@ -0,0 +1,6 @@ +module Main where + +main :: IO () +main = do + let unused = 42 + putStrLn "hello" diff --git a/cabal-testsuite/PackageTests/ProgramOptions/T8900.cabal b/cabal-testsuite/PackageTests/ProgramOptions/T8900.cabal new file mode 100644 index 00000000000..6f6bc5cc417 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProgramOptions/T8900.cabal @@ -0,0 +1,10 @@ +cabal-version: 2.4 +name: T8900 +version: 0.1.0.0 +build-type: Simple + +executable T8900 + main-is: Main.hs + build-depends: base + default-language: Haskell2010 + ghc-options: -Wall diff --git a/cabal-testsuite/PackageTests/ProgramOptions/cabal.project b/cabal-testsuite/PackageTests/ProgramOptions/cabal.project new file mode 100644 index 00000000000..86ea4d915d2 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProgramOptions/cabal.project @@ -0,0 +1,4 @@ +packages: . + +package T8900 + ghc-options: -Werror diff --git a/cabal-testsuite/PackageTests/ProgramOptions/cabal.project.local b/cabal-testsuite/PackageTests/ProgramOptions/cabal.project.local new file mode 100644 index 00000000000..29fd615b21c --- /dev/null +++ b/cabal-testsuite/PackageTests/ProgramOptions/cabal.project.local @@ -0,0 +1,2 @@ +program-options + ghc-options: -Wwarn diff --git a/cabal-testsuite/PackageTests/ProgramOptions/cabal.test.hs b/cabal-testsuite/PackageTests/ProgramOptions/cabal.test.hs new file mode 100644 index 00000000000..94c19c12142 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProgramOptions/cabal.test.hs @@ -0,0 +1,10 @@ +import Test.Cabal.Prelude + +-- Test for issue #8900: `ghc-options` order when combining multiple +-- configuration files. The `program-options` stanza in `cabal.project.local` +-- must be applied after `package ghc-options` in `cabal.project`, so +-- that `-Wwarn` can override `-Werror`. +main = cabalTest $ recordMode DoNotRecord $ do + skipUnlessGhcVersion ">= 8.8" + r <- cabal' "v2-build" ["all"] + assertOutputContains "warning:" r diff --git a/cabal-testsuite/static/Main.hs b/cabal-testsuite/static/Main.hs index d82a4bd93b7..4210c6093c7 100644 --- a/cabal-testsuite/static/Main.hs +++ b/cabal-testsuite/static/Main.hs @@ -1,4 +1,24 @@ +{-# LANGUAGE CPP #-} module Main where +import Control.Monad.Catch () +import Control.Monad.Trans.Class () +import Data.ByteString () +import Data.Set () +import Data.Time () +import Distribution.Simple () +import Distribution.Simple.SetupHooks () +import Distribution.Types.Version () +import System.Directory () +import System.FilePath () +import System.Process () +import Test.Cabal.Run () + +#ifdef mingw32_HOST_OS +import System.Win32 () +#else +import System.Posix () +#endif + main :: IO () main = return () diff --git a/changelog.d/12263.md b/changelog.d/12263.md new file mode 100644 index 00000000000..d0eb29ef197 --- /dev/null +++ b/changelog.d/12263.md @@ -0,0 +1,13 @@ +--- +synopsis: Fix ghc-options order when combining multiple configuration files +packages: [cabal-install] +prs: 12263 +issues: 8900 +--- + +Options specified at the top level of a project (including the +`program-options` stanza) now take precedence over options specified in a +`package ` stanza for local packages, matching the documented behavior. +Previously the `package ` options were applied last, so e.g. +`program-options ghc-options: -Wwarn` in `cabal.project.local` could not +override `-Werror` set via `package ghc-options` in `cabal.project`.