From e7272999e85657737e4df84ba7206e4660309317 Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Wed, 2 Jul 2025 10:06:17 +0100 Subject: [PATCH 01/39] release: Bump boot compiler for release to 9.10.2 Also bumps the cabal-install used during the release process to 3.14.2.0. --- .github/workflows/validate.yml | 8 ++++---- .gitlab-ci.yml | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 70bb0c8a6dd..abf19bffab8 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -32,10 +32,10 @@ on: env: # We choose a stable ghc version across all os's # which will be used to do the next release - GHC_FOR_RELEASE: "9.4.8" + GHC_FOR_RELEASE: "9.10.2" # Ideally we should use the version about to be released for hackage tests and benchmarks - GHC_FOR_SOLVER_BENCHMARKS: "9.4.8" - GHC_FOR_COMPLETE_HACKAGE_TESTS: "9.4.8" + GHC_FOR_SOLVER_BENCHMARKS: "9.10.2" + GHC_FOR_COMPLETE_HACKAGE_TESTS: "9.10.2" COMMON_FLAGS: "-j 2 -v" # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions @@ -61,7 +61,7 @@ jobs: ghc: [ "9.12.2", - "9.10.1", + "9.10.2", "9.8.4", "9.6.7", "9.4.8", diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 87b6d0de22a..70dda50b46b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,8 +5,8 @@ variables: # Commit of ghc/ci-images repository from which to pull Docker images DOCKER_REV: "a9297a370025101b479cfd4977f8f910814e03ab" - GHC_VERSION: 9.6.4 - CABAL_INSTALL_VERSION: 3.10.2.0 + GHC_VERSION: 9.10.2 + CABAL_INSTALL_VERSION: 3.14.2.0 workflow: rules: @@ -110,9 +110,6 @@ darwin: tags: - ${ARCH}-darwin-m1 variables: - # Using 9.8.2 to work around - # https://gitlab.haskell.org/ghc/ghc/-/issues/24050 - GHC_VERSION: 9.8.2 TARBALL_ARCHIVE_SUFFIX: ${ARCH}-darwin TARBALL_EXT: tar.xz ADD_CABAL_ARGS: "" From 976e0f98432825b6ed6d20a97b837ed711b89477 Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Wed, 2 Jul 2025 10:16:19 +0100 Subject: [PATCH 02/39] Update platforms for release jobs * Drop deb9, ubuntu18 and centos7 jobs which are all EOL * Add ubuntu24_04 * Build a very old alpine bindist (3.12) and a new one (3.20) * Only build 3_20 bindist for alpine i386. The list of release platforms after this commit is Linux * x86_64 - deb10 - deb11 - deb12 - fedora33 - fedora36 - fedora38 - rocky8 - ubuntu20_04 - ubuntu22_04 - ubuntu24_04 - alpine3_12 - alpine3_20 * i386 - deb10 - alpine3_20 * aarch64 - deb10 - deb11 - deb12 - alpine3_18 Darwin * x86_64 * aarch64 Windows * x86_64 --- .gitlab-ci.yml | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 70dda50b46b..4657457baad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ stages: variables: # Commit of ghc/ci-images repository from which to pull Docker images - DOCKER_REV: "a9297a370025101b479cfd4977f8f910814e03ab" + DOCKER_REV: "df20b2eb2fcc1c93aed5ad047c65752fbd4c38d4" GHC_VERSION: 9.10.2 CABAL_INSTALL_VERSION: 3.14.2.0 @@ -33,12 +33,10 @@ linux: - ARCH: i386 TAG: x86_64-linux OS: - - deb9 - deb10 - ARCH: x86_64 TAG: x86_64-linux OS: - - deb9 - deb10 - deb11 - deb12 @@ -46,17 +44,9 @@ linux: - fedora36 - fedora38 - rocky8 - - ubuntu18_04 - ubuntu20_04 - ubuntu22_04 - # Pull this one from the future, since it's missing. - # We can't move the entire file to this DOCKER_REV, because - # i386-linux-deb9 is missing from it. - - ARCH: x86_64 - TAG: x86_64-linux - OS: centos7 - DOCKER_REV: f2d12519f45a13a61fcca03a949f927ceead6492 - + - ubuntu24_04 - ARCH: aarch64 TAG: aarch64-linux OS: @@ -76,12 +66,11 @@ alpine-linux: extends: .build parallel: matrix: - - ARCH: [i386, x86_64] - OS: [alpine3_12, alpine3_15, alpine3_17] + - ARCH: [x86_64] + OS: [alpine3_12, alpine3_20] TAG: x86_64-linux - # Was 3_18 for i386 intentionally left off? - - ARCH: x86_64 - OS: alpine3_18 + - ARCH: [i386] + OS: alpine3_20 TAG: x86_64-linux - ARCH: [aarch64] OS: [alpine3_18] From 34e7e62588fad4cc2f612aa97606cae3fe7b0f14 Mon Sep 17 00:00:00 2001 From: Benjamin McRae Date: Wed, 25 Jun 2025 10:17:41 +0100 Subject: [PATCH 03/39] add paragraph mentioning stackage --- doc/cabal-project-description-file.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/cabal-project-description-file.rst b/doc/cabal-project-description-file.rst index b8dab19a5d5..de4c42217dd 100644 --- a/doc/cabal-project-description-file.rst +++ b/doc/cabal-project-description-file.rst @@ -85,6 +85,13 @@ architecture and version information from, which will force some commands (update, sdist) to require ghc present where otherwise it would not be necessitated. +One use case for imports is to specify a `Stackage ` +snapshot, so that your cabal project can use the same set of packages as +that snapshot. To use the ``lts-21.25`` resolver, you can write +``import: https://www.stackage.org/lts-21.25/cabal.config`` in your +``cabal.project``. Note that Stackage does not guarantee that these will work +with regards to revisions. + Specifying the local packages ----------------------------- From d2db2d0a0ab428398e3b7c63e5ab80b98d19ae6d Mon Sep 17 00:00:00 2001 From: Benjamin McRae Date: Wed, 25 Jun 2025 17:25:21 +0100 Subject: [PATCH 04/39] add some discussion of import config limitations --- doc/cabal-project-description-file.rst | 7 +++++-- doc/nix-local-build.rst | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/cabal-project-description-file.rst b/doc/cabal-project-description-file.rst index de4c42217dd..7b9bd5d5241 100644 --- a/doc/cabal-project-description-file.rst +++ b/doc/cabal-project-description-file.rst @@ -89,8 +89,11 @@ One use case for imports is to specify a `Stackage ` snapshot, so that your cabal project can use the same set of packages as that snapshot. To use the ``lts-21.25`` resolver, you can write ``import: https://www.stackage.org/lts-21.25/cabal.config`` in your -``cabal.project``. Note that Stackage does not guarantee that these will work -with regards to revisions. +``cabal.project``. + +There are a number of limitations that come with this approach however; please +see :ref:`How can I have a reproducible set of versions for my dependencies?` for +more information. Specifying the local packages ----------------------------- diff --git a/doc/nix-local-build.rst b/doc/nix-local-build.rst index 6144cea85ba..f4532b29c1a 100644 --- a/doc/nix-local-build.rst +++ b/doc/nix-local-build.rst @@ -104,6 +104,8 @@ for each package using :cfg-field:`profiling-detail`:: Alternately, you can call ``cabal build --enable-profiling`` to temporarily build with profiling. +.. _how reproducible: + How can I have a reproducible set of versions for my dependencies? ------------------------------------------------------------------ @@ -134,6 +136,17 @@ development environments. .. _Stackage: https://stackage.org/ .. _versions of packages in lts-19.2: https://www.stackage.org/lts-19.2 +Limitations +^^^^^^^^^^^ + +Stackage does not guarantee that the config files will work with revisions, and +it's not currently possible to `override used versions of packages ` +or to `specify revisions ` using +cabal. + +To mitigate these shortcomings, you can download the config file that is linked +to and remove the troublesome constraints. + How it works ============ From 0436a815862751b6205f8f756af80e7f174fe092 Mon Sep 17 00:00:00 2001 From: Benjamin <10566848+L0neGamer@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:35:40 +0100 Subject: [PATCH 05/39] Update doc/nix-local-build.rst Co-authored-by: Phil de Joux --- doc/nix-local-build.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/nix-local-build.rst b/doc/nix-local-build.rst index f4532b29c1a..312778f8d47 100644 --- a/doc/nix-local-build.rst +++ b/doc/nix-local-build.rst @@ -144,8 +144,7 @@ it's not currently possible to `override used versions of packages ` using cabal. -To mitigate these shortcomings, you can download the config file that is linked -to and remove the troublesome constraints. +To mitigate these shortcomings, download the linked ``cabal.config`` file, import this locally with a relative path and repeatedly ``cabal build all --dry-run`` to identify and then comment out version constraint conflicts until the cabal solver is happy. How it works ============ From ca7a9387e27e7c540169ef81d9ce7ad158d5d036 Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Wed, 2 Jul 2025 10:29:08 +0100 Subject: [PATCH 06/39] Check the GHC and cabal-install version which are used to build the release --- .gitlab/ci.sh | 72 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/.gitlab/ci.sh b/.gitlab/ci.sh index 94abd39cdf9..708ff0e0d30 100755 --- a/.gitlab/ci.sh +++ b/.gitlab/ci.sh @@ -4,20 +4,70 @@ set -Eeuo pipefail source "$CI_PROJECT_DIR/.gitlab/common.sh" -## Figure out how to get the Haskell toolchain. - -# For backport, the centos image no longer has the right GHC version, so use -# ghcup. -if [[ -f /etc/os-release && "$(grep ^ID /etc/os-release)" =~ "centos" ]]; then - . "$CI_PROJECT_DIR/.gitlab/ghcup.sh" -# All the other ones are fine. -elif [[ "$(uname)" == "Linux" ]]; then - export PATH="/opt/ghc/${GHC_VERSION}/bin:${PATH}" -# Not all runners use ci-images, so ghcup is used. +# Required arguments to the script +: "${GHC_VERSION:?Need to set GHC_VERSION}" +: "${CABAL_INSTALL_VERSION:?Need to set CABAL_INSTALL_VERSION}" + +# If GHC is set, extract its directory and add it to PATH +# The linux images set the GHC variable to specify the location of the GHC installation. +if [[ -n "${GHC:-}" ]]; then + echo "GHC variable is set to: $GHC" + export PATH="$(dirname "$GHC"):$PATH" +else + GHC="ghc" +fi + +echo "Checking toolchain versions..." + +# GHC check +ghc_version_ok=false +if command -v ghc &>/dev/null; then + current_ghc_version=$(ghc --numeric-version) + if [[ "$current_ghc_version" == "$GHC_VERSION" ]]; then + ghc_version_ok=true + else + echo "Wrong GHC version: found $current_ghc_version, expected $GHC_VERSION" + fi else - . "$CI_PROJECT_DIR/.gitlab/ghcup.sh" + echo "GHC executable '$GHC' not found on PATH" fi +# cabal check +cabal_version_ok=false +if command -v cabal &>/dev/null; then + current_cabal_version=$(cabal --numeric-version) + if [[ "$current_cabal_version" == "$CABAL_INSTALL_VERSION" ]]; then + cabal_version_ok=true + else + echo "Wrong cabal version: found $current_cabal_version, expected $CABAL_INSTALL_VERSION" + fi +else + echo "cabal not found on PATH" +fi + +# If either is wrong, install via ghcup +if ! $ghc_version_ok || ! $cabal_version_ok; then + echo "Installing correct GHC and/or cabal via ghcup..." + . "$CI_PROJECT_DIR/.gitlab/ghcup.sh" +fi + +# Final verification (ghc and cabal must now be on PATH) +echo "Verifying installed versions..." + +actual_ghc_version=$(ghc --numeric-version) +actual_cabal_version=$(cabal --numeric-version) + +if [[ "$actual_ghc_version" != "$GHC_VERSION" ]]; then + fail "Incorrect GHC version (actual: $actual_ghc_version, expected: $GHC_VERSION)" +fi + +if [[ "$actual_cabal_version" != "$CABAL_INSTALL_VERSION" ]]; then + fail "Incorrect cabal version (actual: $actual_cabal_version, expected: $CABAL_INSTALL_VERSION)" +fi + +echo "Using GHC version: $actual_ghc_version" +echo "Using cabal-install version: $actual_cabal_version" + export CABAL_DIR="$CI_PROJECT_DIR/cabal" case "$(uname)" in From 03fe89485a9640137489534ae40f68502a910270 Mon Sep 17 00:00:00 2001 From: brandon s allbery kf8nh Date: Thu, 3 Jul 2025 15:35:15 -0400 Subject: [PATCH 07/39] missed version bumps Found while un-bumping for 3.16. --- Cabal/Makefile | 2 +- doc/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cabal/Makefile b/Cabal/Makefile index e74ee82079d..0ef9d1dd195 100644 --- a/Cabal/Makefile +++ b/Cabal/Makefile @@ -1,4 +1,4 @@ -VERSION=3.15.0.0 +VERSION=3.17.0.0 #KIND=devel KIND=rc diff --git a/doc/conf.py b/doc/conf.py index 9f17ba5766c..aa5325655a8 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -13,7 +13,7 @@ sys.path.insert(0, os.path.abspath('.')) import cabaldomain -version = "3.15.0.0" +version = "3.17.0.0" extensions = [ 'sphinx.ext.extlinks', From e5a9144ece3179d8c6e6660a7d5234977f1b0c4a Mon Sep 17 00:00:00 2001 From: brandon s allbery kf8nh Date: Thu, 3 Jul 2025 18:05:31 -0400 Subject: [PATCH 08/39] adjust Mergify queue rules for release branches It just inappropriately used a backport queue (luckily with no ill effects) for a release branch non-backport PR. Use the label we set on backports (which has a better pattern for matching them) instead of a body check. Also undid the attempted hack to stop the Mergify upgrade bot from breaking our priorities. --- .github/mergify.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/mergify.yml b/.github/mergify.yml index 214c53c575d..bc5a17c546e 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -11,12 +11,12 @@ priority_rules: - name: priority for queue `default` conditions: - queue-name=default - priority: 2750 + priority: 2500 - name: priority for queue `squash-merge` conditions: - queue-name=squash-merge - priority: 2750 + priority: 2500 # The idea is we slightly prioritize those PRs because we're in # a release cycle if a PR matches. @@ -24,7 +24,7 @@ priority_rules: conditions: - 'base~=^3\.' - 'label!=backport' - priority: 2800 + priority: 2750 pull_request_rules: @@ -142,7 +142,7 @@ pull_request_rules: conditions: - label=merge me - base!=master - - -body~=backport + - -label=backport - '#approved-reviews-by>=2' - '-label~=^blocked:' @@ -154,7 +154,7 @@ pull_request_rules: conditions: - base!=master - label=squash+merge me - - -body~=backport + - -label=backport - '#approved-reviews-by>=2' - '-label~=^blocked:' @@ -166,7 +166,7 @@ pull_request_rules: conditions: - label=merge me - base!=master - - body~=backport + - label=backport - '#approved-reviews-by>=1' - '-label~=^blocked:' @@ -178,7 +178,7 @@ pull_request_rules: conditions: - label=squash+merge me - base!=master - - body~=backport + - label=backport - '#approved-reviews-by>=1' - '-label~=^blocked:' From aecf41efc6aa4398916d59f7024d220d17f416de Mon Sep 17 00:00:00 2001 From: Yvan Sraka Date: Fri, 27 Jun 2025 13:04:44 +1000 Subject: [PATCH 09/39] Refactor cabal-install solver config log output The main goal is to add an intermediate log message type to the processing of the solver log. There are zero known changes to the cabal solver's output. Co-Authored-By: Erik de Castro Lopo --- .../cabal-install-solver.cabal | 1 + .../src/Distribution/Solver/Modular.hs | 62 ++++--- .../src/Distribution/Solver/Modular/Log.hs | 15 +- .../Distribution/Solver/Modular/Message.hs | 167 ++++++++++-------- .../Solver/Types/DependencyResolver.hs | 13 +- .../Solver/Types/SummarizedMessage.hs | 48 +++++ .../src/Distribution/Client/Dependency.hs | 64 ++++--- changelog.d/pr-10854 | 7 + 8 files changed, 250 insertions(+), 127 deletions(-) create mode 100644 cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs create mode 100644 changelog.d/pr-10854 diff --git a/cabal-install-solver/cabal-install-solver.cabal b/cabal-install-solver/cabal-install-solver.cabal index 7e4386b149f..87c43723165 100644 --- a/cabal-install-solver/cabal-install-solver.cabal +++ b/cabal-install-solver/cabal-install-solver.cabal @@ -95,6 +95,7 @@ library Distribution.Solver.Types.SolverId Distribution.Solver.Types.SolverPackage Distribution.Solver.Types.SourcePackage + Distribution.Solver.Types.SummarizedMessage Distribution.Solver.Types.Variable build-depends: diff --git a/cabal-install-solver/src/Distribution/Solver/Modular.hs b/cabal-install-solver/src/Distribution/Solver/Modular.hs index 9111b2d78d0..a4baebf496c 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular.hs @@ -27,8 +27,14 @@ import Distribution.Solver.Modular.ConfiguredConversion ( convCP ) import qualified Distribution.Solver.Modular.ConflictSet as CS import Distribution.Solver.Modular.Dependency -import Distribution.Solver.Modular.Flag -import Distribution.Solver.Modular.Index + ( Var(..), + showVar, + ConflictMap, + ConflictSet, + showConflictSet, + RevDepMap ) +import Distribution.Solver.Modular.Flag ( SN(SN), FN(FN) ) +import Distribution.Solver.Modular.Index ( Index ) import Distribution.Solver.Modular.IndexConversion ( convPIs ) import Distribution.Solver.Modular.Log @@ -36,25 +42,38 @@ import Distribution.Solver.Modular.Log import Distribution.Solver.Modular.Package ( PN ) import Distribution.Solver.Modular.RetryLog + ( RetryLog, + toProgress, + fromProgress, + retry, + failWith, + continueWith ) import Distribution.Solver.Modular.Solver ( SolverConfig(..), PruneAfterFirstSuccess(..), solve ) import Distribution.Solver.Types.DependencyResolver + ( DependencyResolver ) import Distribution.Solver.Types.LabeledPackageConstraint + ( LabeledPackageConstraint, unlabelPackageConstraint ) import Distribution.Solver.Types.PackageConstraint -import Distribution.Solver.Types.PackagePath + ( PackageConstraint(..), scopeToPackageName ) +import Distribution.Solver.Types.PackagePath ( QPN ) import Distribution.Solver.Types.PackagePreferences + ( PackagePreferences ) import Distribution.Solver.Types.PkgConfigDb ( PkgConfigDb ) import Distribution.Solver.Types.Progress -import Distribution.Solver.Types.Variable + ( Progress(..), foldProgress ) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage(StringMsg) ) +import Distribution.Solver.Types.Variable ( Variable(..) ) import Distribution.System ( Platform(..) ) import Distribution.Simple.Setup ( BooleanFlag(..) ) import Distribution.Simple.Utils - ( ordNubBy ) -import Distribution.Verbosity - + ( ordNubBy ) +import Distribution.Verbosity ( normal, verbose ) +import Distribution.Solver.Modular.Message ( renderSummarizedMessage ) -- | Ties the two worlds together: classic cabal-install vs. the modular -- solver. Performs the necessary translations before and after. @@ -120,21 +139,21 @@ solve' :: SolverConfig -> (PN -> PackagePreferences) -> Map PN [LabeledPackageConstraint] -> Set PN - -> Progress String String (Assignment, RevDepMap) + -> Progress SummarizedMessage String (Assignment, RevDepMap) solve' sc cinfo idx pkgConfigDB pprefs gcs pns = toProgress $ retry (runSolver printFullLog sc) createErrorMsg where runSolver :: Bool -> SolverConfig - -> RetryLog String SolverFailure (Assignment, RevDepMap) + -> RetryLog SummarizedMessage SolverFailure (Assignment, RevDepMap) runSolver keepLog sc' = displayLogMessages keepLog $ solve sc' cinfo idx pkgConfigDB pprefs gcs pns createErrorMsg :: SolverFailure - -> RetryLog String String (Assignment, RevDepMap) + -> RetryLog SummarizedMessage String (Assignment, RevDepMap) createErrorMsg failure@(ExhaustiveSearch cs cm) = if asBool $ minimizeConflictSet sc - then continueWith ("Found no solution after exhaustively searching the " + then continueWith (mkStringMsg $ "Found no solution after exhaustively searching the " ++ "dependency tree. Rerunning the dependency solver " ++ "to minimize the conflict set ({" ++ showConflictSet cs ++ "}).") $ @@ -155,7 +174,7 @@ solve' sc cinfo idx pkgConfigDB pprefs gcs pns = rerunSolverForErrorMsg cs ++ finalErrorMsg sc failure createErrorMsg failure@BackjumpLimitReached = continueWith - ("Backjump limit reached. Rerunning dependency solver to generate " + (mkStringMsg $ "Backjump limit reached. Rerunning dependency solver to generate " ++ "a final conflict set for the search tree containing the " ++ "first backjump.") $ retry (runSolver printFullLog sc { pruneAfterFirstSuccess = PruneAfterFirstSuccess True }) $ @@ -181,13 +200,16 @@ solve' sc cinfo idx pkgConfigDB pprefs gcs pns = -- original goal order. goalOrder' = preferGoalsFromConflictSet cs <> fromMaybe mempty (goalOrder sc) - in unlines ("Could not resolve dependencies:" : messages (toProgress (runSolver True sc'))) + in unlines ("Could not resolve dependencies:" : map renderSummarizedMessage (messages (toProgress (runSolver True sc')))) printFullLog = solverVerbosity sc >= verbose messages :: Progress step fail done -> [step] messages = foldProgress (:) (const []) (const []) +mkStringMsg :: String -> SummarizedMessage +mkStringMsg msg = StringMsg msg + -- | Try to remove variables from the given conflict set to create a minimal -- conflict set. -- @@ -219,11 +241,11 @@ solve' sc cinfo idx pkgConfigDB pprefs gcs pns = -- solver to add new unnecessary variables to the conflict set. This function -- discards the result from any run that adds new variables to the conflict -- set, but the end result may not be completely minimized. -tryToMinimizeConflictSet :: forall a . (SolverConfig -> RetryLog String SolverFailure a) +tryToMinimizeConflictSet :: forall a . (SolverConfig -> RetryLog SummarizedMessage SolverFailure a) -> SolverConfig -> ConflictSet -> ConflictMap - -> RetryLog String SolverFailure a + -> RetryLog SummarizedMessage SolverFailure a tryToMinimizeConflictSet runSolver sc cs cm = foldl (\r v -> retryNoSolution r $ tryToRemoveOneVar v) (fromProgress $ Fail $ ExhaustiveSearch cs cm) @@ -249,14 +271,14 @@ tryToMinimizeConflictSet runSolver sc cs cm = tryToRemoveOneVar :: Var QPN -> ConflictSet -> ConflictMap - -> RetryLog String SolverFailure a + -> RetryLog SummarizedMessage SolverFailure a tryToRemoveOneVar v smallestKnownCS smallestKnownCM -- Check whether v is still present, because it may have already been -- removed in a previous solver rerun. | not (v `CS.member` smallestKnownCS) = fromProgress $ Fail $ ExhaustiveSearch smallestKnownCS smallestKnownCM | otherwise = - continueWith ("Trying to remove variable " ++ varStr ++ " from the " + continueWith (mkStringMsg $ "Trying to remove variable " ++ varStr ++ " from the " ++ "conflict set.") $ retry (runSolver sc') $ \case err@(ExhaustiveSearch cs' _) @@ -268,14 +290,14 @@ tryToMinimizeConflictSet runSolver sc cs cm = ++ "conflict set." in -- Use the new conflict set, even if v wasn't removed, -- because other variables may have been removed. - failWith (msg ++ " Continuing with " ++ showCS cs' ++ ".") err + failWith (mkStringMsg $ msg ++ " Continuing with " ++ showCS cs' ++ ".") err | otherwise -> - failWith ("Failed to find a smaller conflict set. The new " + failWith (mkStringMsg $ "Failed to find a smaller conflict set. The new " ++ "conflict set is not a subset of the previous " ++ "conflict set: " ++ showCS cs') $ ExhaustiveSearch smallestKnownCS smallestKnownCM BackjumpLimitReached -> - failWith "Reached backjump limit while minimizing conflict set." + failWith (mkStringMsg "Reached backjump limit while minimizing conflict set.") BackjumpLimitReached where varStr = "\"" ++ showVar v ++ "\"" diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs index 321a051070b..1eb759bd263 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs @@ -7,11 +7,14 @@ import Prelude () import Distribution.Solver.Compat.Prelude import Distribution.Solver.Types.Progress - -import Distribution.Solver.Modular.Dependency -import Distribution.Solver.Modular.Message + ( Progress(Done, Fail), foldProgress ) +import Distribution.Solver.Modular.ConflictSet + ( ConflictMap, ConflictSet ) import Distribution.Solver.Modular.RetryLog - + ( RetryLog, toProgress, fromProgress ) +import Distribution.Solver.Modular.Message (Message, summarizeMessages) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage(..) ) -- | Information about a dependency solver failure. data SolverFailure = ExhaustiveSearch ConflictSet ConflictMap @@ -22,10 +25,10 @@ data SolverFailure = -- 'keepLog'), for efficiency. displayLogMessages :: Bool -> RetryLog Message SolverFailure a - -> RetryLog String SolverFailure a + -> RetryLog SummarizedMessage SolverFailure a displayLogMessages keepLog lg = fromProgress $ if keepLog - then showMessages progress + then summarizeMessages progress else foldProgress (const id) Fail Done progress where progress = toProgress lg diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs index 2bc28286df0..d6ffadf0abf 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs @@ -1,9 +1,9 @@ {-# LANGUAGE BangPatterns #-} -{-# LANGUAGE LambdaCase #-} module Distribution.Solver.Modular.Message ( Message(..), - showMessages + summarizeMessages, + renderSummarizedMessage, ) where import qualified Data.List as L @@ -14,26 +14,50 @@ import qualified Data.Set as S import Data.Maybe (catMaybes, mapMaybe, isJust) import Prelude hiding (pi) -import Distribution.Pretty (prettyShow) -- from Cabal +import Distribution.Pretty ( prettyShow ) -- from Cabal import qualified Distribution.Solver.Modular.ConflictSet as CS import Distribution.Solver.Modular.Dependency -import Distribution.Solver.Modular.Flag ( QFN, QSN ) -import qualified Distribution.Solver.Modular.Flag as Flag ( showQFN, showQFNBool, showQSN, showQSNBool ) + ( Var(P), + ConflictSet, + showConflictSet, + QGoalReason, + GoalReason(DependencyGoal, UserGoal), + Goal(Goal), + DependencyReason(DependencyReason), + ExposedComponent(..), + PkgComponent(PkgComponent), + CI(Constrained, Fixed), + showDependencyReason ) +import Distribution.Solver.Modular.Flag + ( QSN, QFN, showQFNBool, showQSNBool, showQFN, showQSN ) import Distribution.Solver.Modular.MessageUtils - (showUnsupportedExtension, showUnsupportedLanguage) + ( showUnsupportedExtension, showUnsupportedLanguage ) import Distribution.Solver.Modular.Package + ( PI(PI), showI, showPI ) import Distribution.Solver.Modular.Tree - ( FailReason(..), POption(..), ConflictingDep(..) ) + ( FailReason(..), POption(..), ConflictingDep(..) ) import Distribution.Solver.Modular.Version + ( VR, Ver, showVer, showVR, (.||.) ) + import Distribution.Solver.Types.ConstraintSource + ( ConstraintSource (..), showConstraintSource ) import Distribution.Solver.Types.PackagePath + ( QPN, Qualified(Q), showQPN ) import Distribution.Solver.Types.Progress -import Distribution.Solver.Types.ProjectConfigPath (docProjectConfigPathFailReason) + ( Progress(..) ) +import Distribution.Solver.Types.ProjectConfigPath + ( docProjectConfigPathFailReason) +import Distribution.Solver.Types.SummarizedMessage + ( Entry(..), EntryAtLevel(..), SummarizedMessage(..) ) import Distribution.Types.LibraryName + ( LibraryName(LSubLibName, LMainLibName) ) import Distribution.Types.UnqualComponentName -import Text.PrettyPrint (nest, render) + ( unUnqualComponentName ) + +import Text.PrettyPrint ( nest, render ) +-- A data type to hold log information from the modular solver. data Message = Enter -- ^ increase indentation level | Leave -- ^ decrease indentation level @@ -45,51 +69,81 @@ data Message = | Success | Failure ConflictSet FailReason --- | Transforms the structured message type to actual messages (strings). +renderSummarizedMessage :: SummarizedMessage -> String +renderSummarizedMessage (SummarizedMsg i) = displayMessageAtLevel i +renderSummarizedMessage (StringMsg s) = s + +displayMessageAtLevel :: EntryAtLevel -> String +displayMessageAtLevel (AtLevel l msg) = + let s = show l + in "[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ displayMessage msg + +displayMessage :: Entry -> String +displayMessage (EntryPackageGoal qpn gr) = "next goal: " ++ showQPN qpn ++ showGR gr +displayMessage (EntryRejectF qfn b c fr) = "rejecting: " ++ showQFNBool qfn b ++ showFR c fr +displayMessage (EntryRejectS qsn b c fr) = "rejecting: " ++ showQSNBool qsn b ++ showFR c fr +displayMessage (EntrySkipping cs) = "skipping: " ++ showConflicts cs +displayMessage (EntryTryingF qfn b) = "trying: " ++ showQFNBool qfn b +displayMessage (EntryTryingP qpn i) = "trying: " ++ showOption qpn i +displayMessage (EntryTryingNewP qpn i gr) = "trying: " ++ showOption qpn i ++ showGR gr +displayMessage (EntryTryingS qsn b) = "trying: " ++ showQSNBool qsn b +displayMessage (EntryUnknownPackage qpn gr) = "unknown package: " ++ showQPN qpn ++ showGR gr +displayMessage EntrySuccess = "done" +displayMessage (EntryFailure c fr) = "fail" ++ showFR c fr +displayMessage (EntrySkipMany qsn b cs) = "skipping: " ++ showOptions qsn b ++ " " ++ showConflicts cs +-- Instead of displaying `aeson-1.0.2.1, aeson-1.0.2.0, aeson-1.0.1.0, ...`, +-- the following line aims to display `aeson: 1.0.2.1, 1.0.2.0, 1.0.1.0, ...`. +-- +displayMessage (EntryRejectMany qpn is c fr) = "rejecting: " ++ showOptions qpn is ++ showFR c fr + +-- | Transforms the structured message type to actual messages (SummarizedMessage s). -- -- The log contains level numbers, which are useful for any trace that involves -- backtracking, because only the level numbers will allow to keep track of -- backjumps. -showMessages :: Progress Message a b -> Progress String a b -showMessages = go 0 +summarizeMessages :: Progress Message a b -> Progress SummarizedMessage a b +summarizeMessages = go 0 where -- 'go' increments the level for a recursive call when it encounters -- 'TryP', 'TryF', or 'TryS' and decrements the level when it encounters 'Leave'. - go :: Int -> Progress Message a b -> Progress String a b + go :: Int -> Progress Message a b -> Progress SummarizedMessage a b go !_ (Done x) = Done x go !_ (Fail x) = Fail x + -- complex patterns go !l (Step (TryP qpn i) (Step Enter (Step (Failure c fr) (Step Leave ms)))) = goPReject l qpn [i] c fr ms + go !l (Step (TryP qpn i) (Step Enter (Step (Skip conflicts) (Step Leave ms)))) = goPSkip l qpn [i] conflicts ms + go !l (Step (TryF qfn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) = - (atLevel l $ blurbQFNBool Rejecting qfn b ++ showFR c fr) (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryRejectF qfn b c fr)) (go l ms) + go !l (Step (TryS qsn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) = - (atLevel l $ blurbQSNBool Rejecting qsn b ++ showFR c fr) (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryRejectS qsn b c fr)) (go l ms) + + -- "Trying ..." message when a new goal is started go !l (Step (Next (Goal (P _ ) gr)) (Step (TryP qpn' i) ms@(Step Enter (Step (Next _) _)))) = - (atLevel l $ blurbOption Trying qpn' i ++ showGR gr) (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryTryingNewP qpn' i gr)) (go l ms) + go !l (Step (Next (Goal (P qpn) gr)) (Step (Failure _c UnknownPackage) ms)) = - atLevel l ("unknown package: " ++ showQPN qpn ++ showGR gr) $ go l ms + Step (SummarizedMsg $ AtLevel l $ (EntryUnknownPackage qpn gr)) (go l ms) + -- standard display go !l (Step Enter ms) = go (l+1) ms go !l (Step Leave ms) = go (l-1) ms - go !l (Step (TryP qpn i) ms) = (atLevel l $ blurbOption Trying qpn i) (go l ms) - go !l (Step (TryF qfn b) ms) = (atLevel l $ blurbQFNBool Trying qfn b) (go l ms) - go !l (Step (TryS qsn b) ms) = (atLevel l $ blurbQSNBool Trying qsn b) (go l ms) - go !l (Step (Next (Goal (P qpn) gr)) ms) = (atLevel l $ showPackageGoal qpn gr) (go l ms) - go !l (Step (Next _) ms) = go l ms -- ignore flag goals in the log - go !l (Step (Skip conflicts) ms) = - -- 'Skip' should always be handled by 'goPSkip' in the case above. - (atLevel l $ blurb Skipping ++ showConflicts conflicts) (go l ms) - go !l (Step (Success) ms) = (atLevel l $ "done") (go l ms) - go !l (Step (Failure c fr) ms) = (atLevel l $ showFailure c fr) (go l ms) - - showPackageGoal :: QPN -> QGoalReason -> String - showPackageGoal qpn gr = "next goal: " ++ showQPN qpn ++ showGR gr - - showFailure :: ConflictSet -> FailReason -> String - showFailure c fr = "fail" ++ showFR c fr + + go !l (Step (TryP qpn i) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryTryingP qpn i)) (go l ms) + go !l (Step (TryF qfn b) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryTryingF qfn b)) (go l ms) + go !l (Step (TryS qsn b) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryTryingS qsn b)) (go l ms) + go !l (Step (Next (Goal (P qpn) gr)) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryPackageGoal qpn gr)) (go l ms) + go !l (Step (Next _) ms) = go l ms -- ignore flag goals in the log + + -- 'Skip' should always be handled by 'goPSkip' in the case above. + go !l (Step (Skip conflicts) ms) = Step (SummarizedMsg $ AtLevel l $ (EntrySkipping conflicts)) (go l ms) + go !l (Step (Success) ms) = Step (SummarizedMsg $ AtLevel l $ EntrySuccess) (go l ms) + go !l (Step (Failure c fr) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryFailure c fr)) (go l ms) -- special handler for many subsequent package rejections goPReject :: Int @@ -98,14 +152,13 @@ showMessages = go 0 -> ConflictSet -> FailReason -> Progress Message a b - -> Progress String a b + -> Progress SummarizedMessage a b goPReject l qpn is c fr (Step (TryP qpn' i) (Step Enter (Step (Failure _ fr') (Step Leave ms)))) | qpn == qpn' && fr == fr' = -- By prepending (i : is) we reverse the order of the instances. goPReject l qpn (i : is) c fr ms goPReject l qpn is c fr ms = - (atLevel l $ blurbOptions Rejecting qpn (reverse is) ++ showFR c fr) - (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryRejectMany qpn (reverse is) c fr)) (go l ms) -- Handle many subsequent skipped package instances. goPSkip :: Int @@ -113,25 +166,18 @@ showMessages = go 0 -> [POption] -> Set CS.Conflict -> Progress Message a b - -> Progress String a b + -> Progress SummarizedMessage a b goPSkip l qpn is conflicts (Step (TryP qpn' i) (Step Enter (Step (Skip conflicts') (Step Leave ms)))) | qpn == qpn' && conflicts == conflicts' = -- By prepending (i : is) we reverse the order of the instances. goPSkip l qpn (i : is) conflicts ms goPSkip l qpn is conflicts ms = - let msg = blurbOptions Skipping qpn (reverse is) ++ showConflicts conflicts - in atLevel l msg (go l ms) - - -- write a message with the current level number - atLevel :: Int -> String -> Progress String a b -> Progress String a b - atLevel l x xs = - let s = show l - in Step ("[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ x) xs + Step (SummarizedMsg $ AtLevel l $ (EntrySkipMany qpn (reverse is) conflicts)) (go l ms) -- | Display the set of 'Conflicts' for a skipped package version. showConflicts :: Set CS.Conflict -> String showConflicts conflicts = - " (has the same characteristics that caused the previous version to fail: " + "(has the same characteristics that caused the previous version to fail: " ++ conflictMsg ++ ")" where conflictMsg :: String @@ -213,29 +259,6 @@ data MergedPackageConflict = MergedPackageConflict { , versionConflict :: Maybe VR } -data ProgressAction = - Trying - | Skipping - | Rejecting - -blurb :: ProgressAction -> String -blurb = \case - Trying -> "trying: " - Skipping -> "skipping: " - Rejecting -> "rejecting: " - -blurbQFNBool :: ProgressAction -> QFN -> Bool -> String -blurbQFNBool a q b = blurb a ++ Flag.showQFNBool q b - -blurbQSNBool :: ProgressAction -> QSN -> Bool -> String -blurbQSNBool a q b = blurb a ++ Flag.showQSNBool q b - -blurbOption :: ProgressAction -> QPN -> POption -> String -blurbOption a q p = blurb a ++ showOption q p - -blurbOptions :: ProgressAction -> QPN -> [POption] -> String -blurbOptions a q ps = blurb a ++ showOptions q ps - showOption :: QPN -> POption -> String showOption qpn@(Q _pp pn) (POption i linkedTo) = case linkedTo of @@ -306,8 +329,8 @@ showFR _ (UnsupportedSpecVer ver) = " (unsupported spec-version " ++ pre -- The following are internal failures. They should not occur. In the -- interest of not crashing unnecessarily, we still just print an error -- message though. -showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ Flag.showQFN qfn ++ ")" -showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ Flag.showQSN qsn ++ ")" +showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ showQFN qfn ++ ")" +showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")" showFR _ EmptyGoalChoice = " (INTERNAL ERROR: EMPTY GOAL CHOICE)" showExposedComponent :: ExposedComponent -> String @@ -332,7 +355,9 @@ showConflictingDep (ConflictingDep dr (PkgComponent qpn comp) ci) = componentStr ++ showVR vr -- $setup +-- >>> import Distribution.Solver.Modular.Package -- >>> import Distribution.Solver.Types.PackagePath +-- >>> import Distribution.Types.PackageName -- >>> import Distribution.Types.Version -- >>> import Distribution.Types.UnitId -- >>> let foobarPN = PackagePath DefaultNamespace QualToplevel diff --git a/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs b/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs index 139a6d2b33d..956a4e14849 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs @@ -2,17 +2,22 @@ module Distribution.Solver.Types.DependencyResolver ( DependencyResolver ) where -import Distribution.Solver.Compat.Prelude +import Distribution.Solver.Compat.Prelude ( Maybe, String, Set ) import Prelude () import Distribution.Solver.Types.LabeledPackageConstraint + ( LabeledPackageConstraint ) import Distribution.Solver.Types.PkgConfigDb ( PkgConfigDb ) import Distribution.Solver.Types.PackagePreferences + ( PackagePreferences ) import Distribution.Solver.Types.PackageIndex ( PackageIndex ) import Distribution.Solver.Types.Progress + ( Progress ) import Distribution.Solver.Types.ResolverPackage -import Distribution.Solver.Types.SourcePackage - + ( ResolverPackage ) +import Distribution.Solver.Types.SourcePackage ( SourcePackage ) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage(..) ) import Distribution.Simple.PackageIndex ( InstalledPackageIndex ) import Distribution.Package ( PackageName ) import Distribution.Compiler ( CompilerInfo ) @@ -34,4 +39,4 @@ type DependencyResolver loc = Platform -> (PackageName -> PackagePreferences) -> [LabeledPackageConstraint] -> Set PackageName - -> Progress String String [ResolverPackage loc] + -> Progress SummarizedMessage String [ResolverPackage loc] diff --git a/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs b/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs new file mode 100644 index 00000000000..747db0a88ae --- /dev/null +++ b/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs @@ -0,0 +1,48 @@ +module Distribution.Solver.Types.SummarizedMessage + ( Entry(..) + , EntryAtLevel(..) + , SummarizedMessage(..) + ) where + +import Prelude () +import Distribution.Solver.Compat.Prelude hiding (fail) + +import Distribution.Solver.Modular.Tree + ( FailReason(..), POption(..) ) +import Distribution.Solver.Types.PackagePath ( QPN ) +import Distribution.Solver.Modular.Flag ( QSN, QFN ) +import Distribution.Solver.Modular.Dependency + ( ConflictSet, QGoalReason, GoalReason ) +import qualified Distribution.Solver.Modular.ConflictSet as CS + +-- The following types are used to encode log messages from the +-- dependency solver so they can be easily displayed in the module +-- `Distribution.Solver.Modular.Message`. +-- +-- These types are an intermediate representation of the solver log. +-- The log is converted from a list of Message to a list of +-- SummarizedMessage to a list of String. Message is very similar to +-- the structure of the search tree but difficult to read, and +-- SummarizedMessage has the structure that is shown to users. + +-- Encoding of solver messages. +data Entry + = EntryPackageGoal QPN QGoalReason + | EntryRejectF QFN Bool ConflictSet FailReason + | EntryRejectS QSN Bool ConflictSet FailReason + | EntrySkipping (Set CS.Conflict) + | EntryTryingF QFN Bool + | EntryTryingP QPN POption + | EntryTryingNewP QPN POption (GoalReason QPN) + | EntryTryingS QSN Bool + | EntryRejectMany QPN [POption] ConflictSet FailReason + | EntrySkipMany QPN [POption] (Set CS.Conflict) + | EntryUnknownPackage QPN (GoalReason QPN) + | EntrySuccess + | EntryFailure ConflictSet FailReason + +-- Encode the level at which the solver message occurred. +data EntryAtLevel = AtLevel Int Entry + +-- Messages from the solver. +data SummarizedMessage = SummarizedMsg EntryAtLevel | StringMsg String diff --git a/cabal-install/src/Distribution/Client/Dependency.hs b/cabal-install/src/Distribution/Client/Dependency.hs index d59bc611c44..594afb9e24f 100644 --- a/cabal-install/src/Distribution/Client/Dependency.hs +++ b/cabal-install/src/Distribution/Client/Dependency.hs @@ -123,6 +123,12 @@ import Distribution.Solver.Modular , SolverConfig (..) , modularResolver ) +import Distribution.Solver.Modular.Message + ( renderSummarizedMessage + ) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage (..) + ) import Distribution.System ( Platform ) @@ -152,6 +158,8 @@ import Distribution.Solver.Types.ResolverPackage import Distribution.Solver.Types.Settings import Distribution.Solver.Types.SolverId import Distribution.Solver.Types.SolverPackage + ( SolverPackage (SolverPackage) + ) import Distribution.Solver.Types.SourcePackage import Distribution.Solver.Types.Variable @@ -790,32 +798,33 @@ resolveDependencies resolveDependencies platform comp pkgConfigDB params = Step (showDepResolverParams finalparams) $ fmap (validateSolverResult platform comp indGoals) $ - runSolver - ( SolverConfig - reordGoals - cntConflicts - fineGrained - minimize - indGoals - noReinstalls - shadowing - strFlags - onlyConstrained_ - maxBkjumps - enableBj - solveExes - order - verbosity - (PruneAfterFirstSuccess False) - ) - platform - comp - installedPkgIndex - sourcePkgIndex - pkgConfigDB - preferences - constraints - targets + formatProgress $ + runSolver + ( SolverConfig + reordGoals + cntConflicts + fineGrained + minimize + indGoals + noReinstalls + shadowing + strFlags + onlyConstrained_ + maxBkjumps + enableBj + solveExes + order + verbosity + (PruneAfterFirstSuccess False) + ) + platform + comp + installedPkgIndex + sourcePkgIndex + pkgConfigDB + preferences + constraints + targets where finalparams@( DepResolverParams targets @@ -844,6 +853,9 @@ resolveDependencies platform comp pkgConfigDB params = then params else dontInstallNonReinstallablePackages params + formatProgress :: Progress SummarizedMessage String a -> Progress String String a + formatProgress p = foldProgress (\x xs -> Step (renderSummarizedMessage x) xs) Fail Done p + preferences :: PackageName -> PackagePreferences preferences = interpretPackagesPreference targets defpref prefs diff --git a/changelog.d/pr-10854 b/changelog.d/pr-10854 new file mode 100644 index 00000000000..5209a98c68c --- /dev/null +++ b/changelog.d/pr-10854 @@ -0,0 +1,7 @@ +synopsis: Refactor cabal-install solver config log output +packages: cabal-install-solver +prs: #10854 + +This refactoring is mainly cosmetic changes to the modular solver's +code base. The main change is separating the detection of errors from +the reporting of errors. From 48c2d4c2c6b8f77463b7bd5b8611b1362ebba8e2 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sun, 6 Jul 2025 09:40:02 +0200 Subject: [PATCH 10/39] =?UTF-8?q?Capitalise=20=E2=80=9CAArch=E2=80=9D=20pr?= =?UTF-8?q?operly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The official capitalisation is “AArch”, see https://developer.arm.com/documentation/102374/latest/. --- Cabal/src/Distribution/Simple/Build/PathsModule.hs | 2 +- Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs | 6 +++--- cabal-dev-scripts/src/GenPathsModule.hs | 2 +- changelog.d/pr-10705 | 6 +++--- templates/Paths_pkg.template.hs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cabal/src/Distribution/Simple/Build/PathsModule.hs b/Cabal/src/Distribution/Simple/Build/PathsModule.hs index dc8348f6396..9392acf3cef 100644 --- a/Cabal/src/Distribution/Simple/Build/PathsModule.hs +++ b/Cabal/src/Distribution/Simple/Build/PathsModule.hs @@ -51,7 +51,7 @@ generatePathsModule pkg_descr lbi clbi = , Z.zIsWindows = isWindows , Z.zIsI386 = buildArch == I386 , Z.zIsX8664 = buildArch == X86_64 - , Z.zIsAarch64 = buildArch == AArch64 + , Z.zIsAArch64 = buildArch == AArch64 , Z.zNot = not , Z.zManglePkgName = showPkgName , Z.zPrefix = show flat_prefix diff --git a/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs b/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs index c0a40f55bac..8f33717bbda 100644 --- a/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs +++ b/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs @@ -12,7 +12,7 @@ data Z zIsWindows :: Bool, zIsI386 :: Bool, zIsX8664 :: Bool, - zIsAarch64 :: Bool, + zIsAArch64 :: Bool, zPrefix :: FilePath, zBindir :: FilePath, zLibdir :: FilePath, @@ -350,13 +350,13 @@ render z_root = execWriter $ do tell " c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32\n" return () else do - if (zIsAarch64 z_root) + if (zIsAArch64 z_root) then do tell "foreign import ccall unsafe \"windows.h GetModuleFileNameW\"\n" tell " c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32\n" return () else do - tell "-- win32 supported only with I386, X86_64, Aarch64\n" + tell "-- win32 supported only with I386, X86_64, AArch64\n" tell "c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32\n" tell "c_GetModuleFileName = _\n" return () diff --git a/cabal-dev-scripts/src/GenPathsModule.hs b/cabal-dev-scripts/src/GenPathsModule.hs index ca7380c74df..7c5c947a5b7 100644 --- a/cabal-dev-scripts/src/GenPathsModule.hs +++ b/cabal-dev-scripts/src/GenPathsModule.hs @@ -32,7 +32,7 @@ $(capture "decls" [d| , zIsWindows :: Bool , zIsI386 :: Bool , zIsX8664 :: Bool - , zIsAarch64 :: Bool + , zIsAArch64 :: Bool , zPrefix :: FilePath , zBindir :: FilePath diff --git a/changelog.d/pr-10705 b/changelog.d/pr-10705 index e150428ca27..bc6e2cd219e 100644 --- a/changelog.d/pr-10705 +++ b/changelog.d/pr-10705 @@ -1,8 +1,8 @@ --- -synopsis: 'Add support for Windows Aarch64' +synopsis: 'Add support for Windows AArch64' packages: [Cabal, cabal-install] prs: 10705 --- -Adds to preprocessor branches the option to support `aarch64_HOST_ARCH` platform on Windows Aarch64 target. -`ccall` convention is used at Aarch64, same as for `x86_64_HOST_ARCH`. Introduce `zIsAarch64` to make paths generation support Windows Aarch64 target. +Adds to preprocessor branches the option to support `aarch64_HOST_ARCH` platform on Windows AArch64 target. +`ccall` convention is used at AArch64, same as for `x86_64_HOST_ARCH`. Introduce `zIsAArch64` to make paths generation support Windows AArch64 target. diff --git a/templates/Paths_pkg.template.hs b/templates/Paths_pkg.template.hs index faa497c0b3b..a9b02b0ef64 100644 --- a/templates/Paths_pkg.template.hs +++ b/templates/Paths_pkg.template.hs @@ -205,11 +205,11 @@ foreign import stdcall unsafe "windows.h GetModuleFileNameW" {% elif isX8664 %} foreign import ccall unsafe "windows.h GetModuleFileNameW" c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32 -{% elif isAarch64 %} +{% elif isAArch64 %} foreign import ccall unsafe "windows.h GetModuleFileNameW" c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32 {% else %} --- win32 supported only with I386, X86_64, Aarch64 +-- win32 supported only with I386, X86_64, AArch64 c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32 c_GetModuleFileName = _ {% endif %} From 7d6a46e7f98bbfed4072c6a30e7e59bdba7bcbfb Mon Sep 17 00:00:00 2001 From: Artem Pelenitsyn Date: Tue, 8 Jul 2025 09:45:29 -0400 Subject: [PATCH 11/39] Cabal-3.14.1.0 release notes minor fix: dedup "Other Changes" header --- release-notes/Cabal-3.14.1.0.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/release-notes/Cabal-3.14.1.0.md b/release-notes/Cabal-3.14.1.0.md index d4b32811c5f..b35974dffe5 100644 --- a/release-notes/Cabal-3.14.1.0.md +++ b/release-notes/Cabal-3.14.1.0.md @@ -122,9 +122,6 @@ For reasons of discoverability, the changelog of Cabal & Cabal-syntax 3.14.0.0 i - Add `OrPatterns` extension (GHC proposal #958) [#10339](https://github.com/haskell/cabal/pull/10339) - -### Other changes - - Add flag `--ignore-build-tools` [#10128](https://github.com/haskell/cabal/pull/10128) - Adds flag `--ignore-build-tools` which allows a user to ignore the tool From 47d24dfcd0090f8308dfc54952317b785b7d7e23 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Fri, 4 Jul 2025 17:59:20 +0800 Subject: [PATCH 12/39] Allow relocatable builds on FreeBSD --- Cabal/src/Distribution/Simple/Configure.hs | 2 +- changelog.d/pr-11042 | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog.d/pr-11042 diff --git a/Cabal/src/Distribution/Simple/Configure.hs b/Cabal/src/Distribution/Simple/Configure.hs index afcf9fd23a7..4b29f27b159 100644 --- a/Cabal/src/Distribution/Simple/Configure.hs +++ b/Cabal/src/Distribution/Simple/Configure.hs @@ -2809,7 +2809,7 @@ checkRelocatable verbosity pkg lbi = -- and RPATH, make sure you add your OS to RPATH-support list of: -- Distribution.Simple.GHC.getRPaths checkOS = - unless (os `elem` [OSX, Linux]) $ + unless (os `elem` [OSX, Linux, FreeBSD]) $ dieWithException verbosity $ NoOSSupport os "relocatable builds" where diff --git a/changelog.d/pr-11042 b/changelog.d/pr-11042 new file mode 100644 index 00000000000..e939f8a8914 --- /dev/null +++ b/changelog.d/pr-11042 @@ -0,0 +1,8 @@ +--- +synopsis: 'Allow relocatable builds on FreeBSD' +packages: [Cabal, cabal-install] +prs: 11042 +--- + +Properly enables relocatable builds on FreeBSD, just like on +OSX and Linux. From 7f494c235df2484edf071364e04fc4e0e9804bbc Mon Sep 17 00:00:00 2001 From: brandon s allbery kf8nh Date: Wed, 9 Jul 2025 22:24:22 -0400 Subject: [PATCH 13/39] missed all the copyrights in the cabal files --- Cabal-hooks/Cabal-hooks.cabal | 2 +- Cabal-syntax/Cabal-syntax.cabal | 2 +- Cabal-tests/Cabal-tests.cabal | 2 +- Cabal/Cabal.cabal | 2 +- cabal-benchmarks/cabal-benchmarks.cabal | 2 +- cabal-install-solver/cabal-install-solver.cabal | 2 +- cabal-install/cabal-install.cabal | 2 +- cabal-testsuite/cabal-testsuite.cabal | 2 +- cabal-validate/cabal-validate.cabal | 2 +- solver-benchmarks/solver-benchmarks.cabal | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cabal-hooks/Cabal-hooks.cabal b/Cabal-hooks/Cabal-hooks.cabal index 78e43702f2e..bb6b197cf03 100644 --- a/Cabal-hooks/Cabal-hooks.cabal +++ b/Cabal-hooks/Cabal-hooks.cabal @@ -1,7 +1,7 @@ cabal-version: 3.6 name: Cabal-hooks version: 3.17 -copyright: 2023, Cabal Development Team +copyright: 2025, Cabal Development Team license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team diff --git a/Cabal-syntax/Cabal-syntax.cabal b/Cabal-syntax/Cabal-syntax.cabal index 266c284e098..dd3137786e2 100644 --- a/Cabal-syntax/Cabal-syntax.cabal +++ b/Cabal-syntax/Cabal-syntax.cabal @@ -1,7 +1,7 @@ cabal-version: 3.6 name: Cabal-syntax version: 3.17.0.0 -copyright: 2003-2024, Cabal Development Team (see AUTHORS file) +copyright: 2003-2025, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team diff --git a/Cabal-tests/Cabal-tests.cabal b/Cabal-tests/Cabal-tests.cabal index ad9bf16cf46..f8d06955d71 100644 --- a/Cabal-tests/Cabal-tests.cabal +++ b/Cabal-tests/Cabal-tests.cabal @@ -1,7 +1,7 @@ cabal-version: 2.2 name: Cabal-tests version: 3 -copyright: 2003-2024, Cabal Development Team (see AUTHORS file) +copyright: 2003-2025, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team diff --git a/Cabal/Cabal.cabal b/Cabal/Cabal.cabal index edcf9362e42..e62c6a5505d 100644 --- a/Cabal/Cabal.cabal +++ b/Cabal/Cabal.cabal @@ -1,7 +1,7 @@ cabal-version: 3.6 name: Cabal version: 3.17.0.0 -copyright: 2003-2024, Cabal Development Team (see AUTHORS file) +copyright: 2003-2025, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team diff --git a/cabal-benchmarks/cabal-benchmarks.cabal b/cabal-benchmarks/cabal-benchmarks.cabal index ade0fa3b178..a48e9298677 100644 --- a/cabal-benchmarks/cabal-benchmarks.cabal +++ b/cabal-benchmarks/cabal-benchmarks.cabal @@ -1,7 +1,7 @@ cabal-version: 2.2 name: cabal-benchmarks version: 3 -copyright: 2003-2024, Cabal Development Team (see AUTHORS file) +copyright: 2003-2025, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team diff --git a/cabal-install-solver/cabal-install-solver.cabal b/cabal-install-solver/cabal-install-solver.cabal index 87c43723165..17e9938a46e 100644 --- a/cabal-install-solver/cabal-install-solver.cabal +++ b/cabal-install-solver/cabal-install-solver.cabal @@ -11,7 +11,7 @@ license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team (see AUTHORS file) maintainer: Cabal Development Team -copyright: 2003-2024, Cabal Development Team +copyright: 2003-2025, Cabal Development Team category: Distribution build-type: Simple extra-doc-files: diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal index e511f37367a..8ace48c0a55 100644 --- a/cabal-install/cabal-install.cabal +++ b/cabal-install/cabal-install.cabal @@ -13,7 +13,7 @@ License: BSD-3-Clause License-File: LICENSE Author: Cabal Development Team (see AUTHORS file) Maintainer: Cabal Development Team -Copyright: 2003-2024, Cabal Development Team +Copyright: 2003-2025, Cabal Development Team Category: Distribution Build-type: Simple Extra-Source-Files: diff --git a/cabal-testsuite/cabal-testsuite.cabal b/cabal-testsuite/cabal-testsuite.cabal index 1e5cd30d47c..b59e8dbf8da 100644 --- a/cabal-testsuite/cabal-testsuite.cabal +++ b/cabal-testsuite/cabal-testsuite.cabal @@ -1,7 +1,7 @@ cabal-version: 2.2 name: cabal-testsuite version: 3 -copyright: 2003-2024, Cabal Development Team (see AUTHORS file) +copyright: 2003-2025, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team diff --git a/cabal-validate/cabal-validate.cabal b/cabal-validate/cabal-validate.cabal index 582cf67434a..0f784898ff9 100644 --- a/cabal-validate/cabal-validate.cabal +++ b/cabal-validate/cabal-validate.cabal @@ -1,7 +1,7 @@ cabal-version: 3.0 name: cabal-validate version: 1.0.0 -copyright: 2024-2024, Cabal Development Team (see AUTHORS file) +copyright: 2024-2025, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause author: Cabal Development Team synopsis: An internal tool for building and testing the Cabal package manager diff --git a/solver-benchmarks/solver-benchmarks.cabal b/solver-benchmarks/solver-benchmarks.cabal index f3533e26f9a..1090491187c 100644 --- a/solver-benchmarks/solver-benchmarks.cabal +++ b/solver-benchmarks/solver-benchmarks.cabal @@ -1,7 +1,7 @@ cabal-version: 2.2 name: solver-benchmarks version: 3 -copyright: 2003-2024, Cabal Development Team (see AUTHORS file) +copyright: 2003-2025, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team From 6f6b2c8a1cabfb2d3da576c019d58ebc13aa5444 Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Thu, 10 Jul 2025 12:57:24 +0100 Subject: [PATCH 14/39] Add proposals.md document which explains the proposal process (#11006) The cabal developers are interested to adopt a proposal process, the details are found in the `proposals.md` file. The process is designed to make developers feel empowered to make decisions. * It is light-weight, a PR is opened and discussed on a repo with a fixed discussion period. * It is developer-led, final decisions are made by developers at the Cabal developers meeting. * It is flexible, there is no formal voting procedure, decisions are made by rough consensus. Overall, we hope that this will allow developers to move the cabal project forward. --- CONTRIBUTING.md | 19 ++++++ README.md | 10 +++ proposals.md | 164 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 proposals.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 87ed3626eaa..ef7dcf51228 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -307,6 +307,25 @@ you push a fix of a whitespace violation, please do so in a _separate commit_. F #endif ``` +## Proposal Process + +For larger changes which require additional discussion or consensus building, +the [proposal process](proposals.md) can be used. Proposals are discussed in +the [`cabal-proposals`](http://github.com/haskell/cabal-proposals) repository. + +When does a change require a proposal? + +* Bug fixes or small improvements do not require proposals. +* Improving features which already see wide agreement does not require + proposals (for example, migrating existing commands from v1- to v2-). +* Larger features should be first discussed on a proposal (for example, a new + command, Hooks, private dependencies). +* Significant changes to existing behaviour should be discussed on proposals. + +In general, most changes do not require proposals, developers are trusted to +use their judgement about when seeking a broader consensus is necessary. + + ## GitHub Ticket Conventions Each major `Cabal`/`cabal-install` release (e.g. 3.4, 3.6, etc.) has a diff --git a/README.md b/README.md index d0ebaf3a1ec..9a9a353dd44 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,16 @@ This Cabal Git repository contains the following main packages: The canonical upstream repository is located at https://github.com/haskell/cabal. +Proposals for the Cabal project +------------------------------- + +The [proposals process](proposals.md) is the mechanism which developers can gain +the necessary consensus to make larger changes to the Cabal ecosystem. + +* It is light-weight, a PR is opened and discussed on [cabal-proposals](https://github.com/haskell/cabal-proposals/) repo with a fixed discussion period. +* It is developer-led, final decisions are made by developers at the Cabal developers meeting. +* It is flexible, there is no formal voting procedure, decisions are made by [rough consensus](https://datatracker.ietf.org/doc/html/rfc7282). + Ways to get the `cabal-install` binary -------------------------------- diff --git a/proposals.md b/proposals.md new file mode 100644 index 00000000000..817aec303a1 --- /dev/null +++ b/proposals.md @@ -0,0 +1,164 @@ +# Cabal Proposals Process + +## Motivation + +The Cabal project's codebase is large, complex, and has evolved over many years. No single +person fully understands how all its parts interact. As a result, maintainers +and contributors often hesitate to make significant changes, unsure whether they +are safe or whether they align with the project's long-term direction. This +has led to a delayed decision making process and a tendency to avoid +making significant decisions. One example is the removal of legacy v1- commands: although most developers agree they should be deprecated and eventually removed, no one feels confident in making that change without a clearer mandate or process. + +To address this, we propose a +lightweight, developer-led process for making and recording significant +decisions about the future of Cabal and cabal-install. The goal is to enable +contributors to move the project forward with greater confidence and +shared understanding. + +The process is designed to make developers feel empowered to make decisions. + +* It is light-weight, a PR is opened and discussed on a repo with a fixed discussion period. +* It is developer-led, final decisions are made by developers at the Cabal developers meeting. +* It is flexible, there is no formal voting procedure, decisions are made by [rough consensus](https://datatracker.ietf.org/doc/html/rfc7282). + +Overall, we hope that this will allow developers to move the cabal project forward. + +This process is an experiment. We will review it over time to ensure that it continues to serve the Cabal project well. + +## Principles + +- The process exists to support active contributors, not to control them. +- The decision-making power remains with those who contribute code and maintain the project. +- The process is deliberately kept lightweight to avoid burdening contributors. + If contributors do not want to use the process then it should be modified. +- The developers meeting will assess consensus to make a final decisions. +- Transparency and documentation make it easier for all contributors to follow significant decisions. + + +## Scope + +This process applies to: + +- Changes to the `Cabal` library. +- Changes to the `cabal-install` tool. +- Significant process or tooling changes for the Cabal project itself. + +This process does **not** apply to: + +- The broader Haskell ecosystem. +- Community-wide standards. +- Individual packages not maintained in the Cabal project. + +Routine or small changes do not require a formal proposal; they can continue to be discussed and merged via the normal PR process. + +### Examples + +* Bug fixes or small improvements do not require proposals. +* Improving features which already see wide agreement does not require proposals (for example, migrating existing commands from v1- to v2-). +* Larger features should be first discussed on a proposal (for example, a new command, Hooks, private dependencies). +* Significant changes to existing behaviour should be discussed on proposals. + +In general, most changes do not require proposals, developers are trusted to use their judgement about when seeking a broader consensus is necessary. + +## Process + +### 1. Proposal Submission + +- Proposals are submitted as **pull requests** to the [`cabal-proposals`](https://github.com/haskell/cabal-proposals) repository. +- The proposal must follow the [proposal template](#proposal-template). +- Anyone can submit a proposal. However, proposals without the support of active contributors are unlikely to be accepted. +- There is a general expectation that authors of proposals will take the lead on their implementation. If proposals remain unimplemented for an extended period, it may affect how future proposals from the same author are received. + +### 2. Discussion + +- Discussion happens on the pull request. +- There is a **minimum comment period** of 2 weeks, or until the next Cabal developers meeting (whichever is longer). Extensions may be granted for more complex topics. +- All community members are welcome to comment. Maintainers and active contributors are especially encouraged to provide feedback. +- After the minimum comment period, the author can request for the proposal to be decided on at + the Cabal developers meeting. + +### 3. Decision Meeting + +- The Cabal developers meeting is the forum for making decisions on proposals. +- The developers present at the meeting should reflect on the comments on a proposal and determine the [rough consensus](https://datatracker.ietf.org/doc/html/rfc7282) of the community. + The opinion of knowledgeable contributors regarding a particular subsystem is especially + important for the meeting to reach a decision. +- It is not necessarily expected that the participants of the meeting will offer a technical opinion. The discussion on the issue should provide enough context for a decision to be made. +- There is a quorum of three developers at the meeting. +- If quorum is not reached for 4 consecutive meetings (8 weeks) whilst a proposal is due to be discussed, the proposal process is suspended and reviewed. + +## 4. The Decision + +- A proposal is **accepted** if there is general agreement among active maintainers and contributors present at the meeting. +- A proposal is **rejected** if consensus cannot be reached or the developers do not wish to + pursue the direction of the proposal. +- A proposal is **deferred** if the developers generally agree with the direction of the proposal + but have specific and actionable points which require improvement before acceptance. + For example, the opinion of a certain expert may be additionally sought or they may wish for more consideration into the backwards compatibility story. +- The meeting chair is responsible for recording the outcome on the proposal PR. + + +### 5. Outcome and Implementation + +- **Accepted** proposals are merged into the `cabal-proposals` repository and tagged as "Accepted". +- **Rejected** proposals are closed with a clear rationale. +- **Deferred** proposals may be left open with an explicit milestone for revisiting. + +Acceptance of a proposal does not automatically result in implementation. The proposer is expected to implement the change or coordinate with others to do so. + +### Example Timeline + +* A proposal is opened. +* A two weeks discussion period follows. +* After at least two weeks, the author may ask cabal developers to consider their proposal. +* The cabal developers may decide about the proposal at the next developers' meeting or a cabal developer may ask for a longer discussion period. +* The proposal is accepted: the proposal author implements the feature. +* The proposal is rejected: the PR is closed. +* The proposal is deferred: the decision period is extended and a concrete + reason for deferral is named. Once the concern has been addressed then the + proposal is reviewed again at the meeting. + + +## Proposal Template + +Each proposal should include the following sections: + +```markdown +# Title + +## Summary + +A short, high-level summary of the proposed change. + +## Motivation + +Why is this change needed? What problem does it solve? + +## Proposed Change + +Describe the change in detail. + +## Alternatives Considered + +What other approaches were considered? Why was this one chosen? + +## Backwards Compatibility / Migration + +Does this change affect backwards compatibility? What migration path is needed? + +## Interested parties + +Who are the interested parties in the broader Haskell community? Have you contacted them? + +## Implementation Notes + +Are you willing to implement this yourself? What is the expected timeline? + +## Open Questions + +Are there any unresolved questions or areas needing further input? + +## References + +Links to related issues, discussions, or previous work. +``` From 452eead4312a5bb751c027c517a6eec6bf25c526 Mon Sep 17 00:00:00 2001 From: ffaf1 Date: Tue, 8 Jul 2025 21:48:01 +0200 Subject: [PATCH 15/39] Add 3.16 changelogs (#11033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add changelog files for 3.16 * Integrate Brandon’s suggestions. * Integrate Brandon’s suggestions /II. * Integrate Bodigrim’s suggestions. * Integrate Artem’s suggestions. * Correctly capitalize “AArch” in preparation of #11047 backport * Integrate Artem’s suggestions /II. * Remove changelog.d bits --- Cabal-hooks/CHANGELOG.md | 3 + Cabal-syntax/ChangeLog.md | 2 +- Cabal/ChangeLog.md | 3 + cabal-install-solver/ChangeLog.md | 2 +- cabal-install/ChangeLog.md | 4 + changelog.d/issue-9115.md | 26 --- changelog.d/issue-9694 | 4 - changelog.d/pr-10190 | 11 - changelog.d/pr-10254 | 16 -- changelog.d/pr-10273 | 19 -- changelog.d/pr-10356 | 11 - changelog.d/pr-10432 | 10 - changelog.d/pr-10525 | 34 --- changelog.d/pr-10549 | 11 - changelog.d/pr-10587 | 12 - changelog.d/pr-10590 | 14 -- changelog.d/pr-10629 | 13 -- changelog.d/pr-10637 | 11 - changelog.d/pr-10644.md | 165 ------------- changelog.d/pr-10646.md | 209 ----------------- changelog.d/pr-10647 | 10 - changelog.d/pr-10705 | 8 - changelog.d/pr-10805.md | 17 -- changelog.d/pr-10816.md | 23 -- changelog.d/pr-10817 | 12 - changelog.d/pr-10828.md | 15 -- changelog.d/pr-10830.md | 10 - changelog.d/pr-10840.md | 28 --- changelog.d/pr-10844.md | 14 -- changelog.d/pr-10880.md | 12 - changelog.d/pr-10908.md | 8 - changelog.d/pr-10912.md | 8 - changelog.d/pr-10936.md | 6 - changelog.d/pr-10940 | 17 -- changelog.d/pr-10941 | 8 - changelog.d/pr-10948.md | 18 -- changelog.d/pr-10949.md | 13 -- changelog.d/pr-10966.md | 20 -- changelog.d/pr-10967.md | 13 -- changelog.d/pr-10979 | 8 - changelog.d/pr-10991.md | 15 -- changelog.d/pr-10992.md | 11 - changelog.d/pr-11003.md | 8 - changelog.d/pr-11014 | 9 - changelog.d/pr-9367 | 21 -- changelog.d/pr-9744.md | 18 -- changelog.d/pr-9890 | 10 - release-notes/Cabal-3.16.0.0.md | 199 ++++++++++++++++ release-notes/WIP-Cabal-3.16.x.0.md | 18 -- release-notes/cabal-install-3.16.0.0.md | 292 ++++++++++++++++++++++++ 50 files changed, 503 insertions(+), 946 deletions(-) delete mode 100644 changelog.d/issue-9115.md delete mode 100644 changelog.d/issue-9694 delete mode 100644 changelog.d/pr-10190 delete mode 100644 changelog.d/pr-10254 delete mode 100644 changelog.d/pr-10273 delete mode 100644 changelog.d/pr-10356 delete mode 100644 changelog.d/pr-10432 delete mode 100644 changelog.d/pr-10525 delete mode 100644 changelog.d/pr-10549 delete mode 100644 changelog.d/pr-10587 delete mode 100644 changelog.d/pr-10590 delete mode 100644 changelog.d/pr-10629 delete mode 100644 changelog.d/pr-10637 delete mode 100644 changelog.d/pr-10644.md delete mode 100644 changelog.d/pr-10646.md delete mode 100644 changelog.d/pr-10647 delete mode 100644 changelog.d/pr-10705 delete mode 100644 changelog.d/pr-10805.md delete mode 100644 changelog.d/pr-10816.md delete mode 100644 changelog.d/pr-10817 delete mode 100644 changelog.d/pr-10828.md delete mode 100644 changelog.d/pr-10830.md delete mode 100644 changelog.d/pr-10840.md delete mode 100644 changelog.d/pr-10844.md delete mode 100644 changelog.d/pr-10880.md delete mode 100644 changelog.d/pr-10908.md delete mode 100644 changelog.d/pr-10912.md delete mode 100644 changelog.d/pr-10936.md delete mode 100644 changelog.d/pr-10940 delete mode 100644 changelog.d/pr-10941 delete mode 100644 changelog.d/pr-10948.md delete mode 100644 changelog.d/pr-10949.md delete mode 100644 changelog.d/pr-10966.md delete mode 100644 changelog.d/pr-10967.md delete mode 100644 changelog.d/pr-10979 delete mode 100644 changelog.d/pr-10991.md delete mode 100644 changelog.d/pr-10992.md delete mode 100644 changelog.d/pr-11003.md delete mode 100644 changelog.d/pr-11014 delete mode 100644 changelog.d/pr-9367 delete mode 100644 changelog.d/pr-9744.md delete mode 100644 changelog.d/pr-9890 create mode 100644 release-notes/Cabal-3.16.0.0.md delete mode 100644 release-notes/WIP-Cabal-3.16.x.0.md create mode 100644 release-notes/cabal-install-3.16.0.0.md diff --git a/Cabal-hooks/CHANGELOG.md b/Cabal-hooks/CHANGELOG.md index 46a825897db..b25fd04e988 100644 --- a/Cabal-hooks/CHANGELOG.md +++ b/Cabal-hooks/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog for `Cabal-hooks` +## 3.16.0 – July 2025 + * No changes + ## 3.14.2 – April 2025 * No changes diff --git a/Cabal-syntax/ChangeLog.md b/Cabal-syntax/ChangeLog.md index 15eaf05e88b..b00ea056e81 100644 --- a/Cabal-syntax/ChangeLog.md +++ b/Cabal-syntax/ChangeLog.md @@ -1 +1 @@ -Please see https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.14.2.0.md +Please see https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.16.0.0.md diff --git a/Cabal/ChangeLog.md b/Cabal/ChangeLog.md index 184d64fa338..11394be5189 100644 --- a/Cabal/ChangeLog.md +++ b/Cabal/ChangeLog.md @@ -1,3 +1,6 @@ +# 3.16.0.0 [Artem Pelenitsyn](mailto:a@pelenitsyn.top) July 2025 +* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.16.0.0.md + # 3.14.2.0 [Mikolaj Konarski](mailto:mikolaj@well-typed.com) April 2025 * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.14.2.0.md diff --git a/cabal-install-solver/ChangeLog.md b/cabal-install-solver/ChangeLog.md index e98218e072c..0f5cea55530 100644 --- a/cabal-install-solver/ChangeLog.md +++ b/cabal-install-solver/ChangeLog.md @@ -1 +1 @@ -Please see https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.14.2.0.md +Please see https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.16.0.0.md diff --git a/cabal-install/ChangeLog.md b/cabal-install/ChangeLog.md index a6b878a5298..f4e9babf7cd 100644 --- a/cabal-install/ChangeLog.md +++ b/cabal-install/ChangeLog.md @@ -1,5 +1,9 @@ # Change-log +## 3.16.0.0 [Artem Pelenitsyn](mailto:a@pelenitsyn.top) July 2025 + +* See https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.16.0.0.md + ## 3.14.2.0 [Mikolaj Konarski](mailto:mikolaj@well-typed.com) April 2025 * See https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.14.2.0.md diff --git a/changelog.d/issue-9115.md b/changelog.d/issue-9115.md deleted file mode 100644 index 139f97f8a19..00000000000 --- a/changelog.d/issue-9115.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -synopsis: Add --with-repl flag to specify alternative REPL program -packages: [cabal-install, Cabal] -prs: [10996] -issues: [9115] ---- - -Added a new `--with-repl` command-line option that allows specifying an alternative -program to use when starting a REPL session, instead of the default GHC. - -This is particularly useful for tools like `doctest` and `hie-bios` that need to -intercept the REPL session to perform their own operations. Previously, these tools -had to use `--with-ghc` which required them to proxy all GHC invocations, including -dependency compilation, making the implementation more complex. - -The `--with-repl` option only affects the final REPL invocation, simplifying the -implementation of such wrapper tools. - -Example usage: -```bash -cabal repl --with-repl=doctest -cabal repl --with-repl=/path/to/custom/ghc -``` - -This change also removes the special handling for response files with `--interactive` -mode, as tools are now expected to handle response files appropriately. diff --git a/changelog.d/issue-9694 b/changelog.d/issue-9694 deleted file mode 100644 index ac3fc2b1d88..00000000000 --- a/changelog.d/issue-9694 +++ /dev/null @@ -1,4 +0,0 @@ -synopsis: Don't imply that Haddock docs exist, when warning not installed -packages: Cabal -issues: #9694 -prs: #9695 diff --git a/changelog.d/pr-10190 b/changelog.d/pr-10190 deleted file mode 100644 index d9e4f009b94..00000000000 --- a/changelog.d/pr-10190 +++ /dev/null @@ -1,11 +0,0 @@ -synopsis: Fix `cabal clean` permissions on Windows -packages: cabal-install -prs: #10190 -issues: #10182 -significance: - -description: { - -- `cabal clean` now removes the read-only mark recursively in the `dist-newstyle` folder on Windows before attempting to delete it. - -} diff --git a/changelog.d/pr-10254 b/changelog.d/pr-10254 deleted file mode 100644 index b07b66b561f..00000000000 --- a/changelog.d/pr-10254 +++ /dev/null @@ -1,16 +0,0 @@ -synopsis: Shallow and concurrent cloning of git repos -packages: cabal-install -prs: #10254 - -description: { - -- Clone git repositories specified in source-repository-package stanzas - shallowly, since to build the package from the repository we only need to - read the commit specified. The rest of the repo is not needed. - Note that this does not change the behaviour of `cabal get -s`, which will - still clone the repository in full. -- Clone VCS repositories concurrently, with a maximum of two concurrent tasks - at the same time (just like when downloading packages asynchronously) - -} - diff --git a/changelog.d/pr-10273 b/changelog.d/pr-10273 deleted file mode 100644 index 343c871377b..00000000000 --- a/changelog.d/pr-10273 +++ /dev/null @@ -1,19 +0,0 @@ ---- -synopsis: "Show why `cabal act-as-setup configure` failed" -packages: [Cabal] -prs: 10273 ---- - -When `cabal act-as-setup configure` fails, it prints a list of "missing or -private dependencies". - -Now, it will show you if each failing dependency is missing, private, or an -incompatible version: - -``` -Error: [Cabal-8010] -Encountered missing or private dependencies: - Lib:{bar-internal,foo-internal} (missing :bar-internal), - base <=1.0 (installed: 4.18.2.1), - package-that-does-not-exist (missing) -``` diff --git a/changelog.d/pr-10356 b/changelog.d/pr-10356 deleted file mode 100644 index dbe464ae492..00000000000 --- a/changelog.d/pr-10356 +++ /dev/null @@ -1,11 +0,0 @@ -synopsis: New licence constructors for SPDX licences starting with a digit -packages: Cabal-syntax -prs: #10356 - -description: { - -- LicenseId constructor `NullBSD` is now `N_0BSD`. -- LicenseId constructor `DS389_exception` is now and `N_389_exception`. -- LicenseId constructor `X3D_Slicer_1_0` is now and `N_3D_Slicer_1_0`. - -} diff --git a/changelog.d/pr-10432 b/changelog.d/pr-10432 deleted file mode 100644 index 1bb76adb0a1..00000000000 --- a/changelog.d/pr-10432 +++ /dev/null @@ -1,10 +0,0 @@ -synopsis: Avoid partial `Data.List.last` in autogenerated `Paths_.hs` -packages: Cabal -prs: #10432 -significance: - -description: { - -- Autogenerated `Paths_.hs` now avoids use of the partial function `Data.List.last`. - -} diff --git a/changelog.d/pr-10525 b/changelog.d/pr-10525 deleted file mode 100644 index 7235d0bec74..00000000000 --- a/changelog.d/pr-10525 +++ /dev/null @@ -1,34 +0,0 @@ ---- -synopsis: "A trailing colon after a stanza name in `cabal.project` is now an error" -packages: [cabal-install] -prs: 10525 ---- - -It is now a hard error to use a trailing colon after a stanza name in -`cabal.project` or `*.cabal` files: - -``` -packages: . - -source-repository-package: - type: git - location: https://github.com/haskell/cabal - tag: f34aba976a60940295f41b6649674e9568893894 -``` - -``` -$ cabal build -Error parsing project file cabal.project:3: -'source-repository-package' is a stanza, not a field. Remove the trailing ':' to parse a stanza. -``` - -Previously, the warning message was easily ignored and somewhat misleading, as -the difference between a stanza and a field is not immediately obvious to -Haskellers used to config languages like JSON and YAML (which don't distinguish -between fields which have string or list values and stanzas which have nested -fields): - -``` -Warning: cabal.project: Unrecognized field -'source-repository-package' on line 3 -``` diff --git a/changelog.d/pr-10549 b/changelog.d/pr-10549 deleted file mode 100644 index 257fc103197..00000000000 --- a/changelog.d/pr-10549 +++ /dev/null @@ -1,11 +0,0 @@ -synopsis: Warn on `cabal format` -packages: cabal-install -prs: #10549 -issues: -significance: - -description: { - -- Despite its name, `cabal format` is not a proper formatter for cabal files. By chance users have sometimes found the command eventhough it is not mentioned in the help text, and they used it to format cabal files. This has some downsides like comments are stripped away or common stanzas are inlined, the command is more like a dump of the resolved package description. There are future plans (#7544) to make it an actual formatter so, rather than going through a deprecation cycle, we decided to keep this command for future use and in the meantime just warn the user about the fact that it is not a proper formatter. - -} diff --git a/changelog.d/pr-10587 b/changelog.d/pr-10587 deleted file mode 100644 index 92428be048b..00000000000 --- a/changelog.d/pr-10587 +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: "Quieter Git output" -packages: [cabal-install] -prs: 10587 ---- - -When `cabal` clones a Git repo for a `source-repository-package` listed in a -`cabal.project`, it will run various commands to check out the correct -revision, initialize submodules if they're present, and so on. - -Now, `cabal` will pass `--quiet` to Git in more cases to help prevent -cluttering command-line output. diff --git a/changelog.d/pr-10590 b/changelog.d/pr-10590 deleted file mode 100644 index 5121bbfb959..00000000000 --- a/changelog.d/pr-10590 +++ /dev/null @@ -1,14 +0,0 @@ ---- -synopsis: "Don't run submodule commands unless necessary" -packages: [cabal-install] -prs: 10590 ---- - -When `cabal` clones a Git repo for a `source-repository-package` listed in a -`cabal.project`, it will run various commands to check out the correct -revision, initialize submodules if they're present, and so on. - -Now, `cabal` will avoid running `git submodule` commands unless the cloned -repository contains a `.gitmodules` file. This will declutter `cabal`'s debug -output by running fewer commands. - diff --git a/changelog.d/pr-10629 b/changelog.d/pr-10629 deleted file mode 100644 index 2d8259b45ef..00000000000 --- a/changelog.d/pr-10629 +++ /dev/null @@ -1,13 +0,0 @@ ---- -synopsis: "Report trailing spaces in project import URIs" -packages: [cabal-install, cabal-install-solver] -prs: 10629 -issues: 10622 ---- - -> A string is a valid URL potentially surrounded by spaces if, after stripping -> leading and trailing whitespace from it, it is a valid URL." -> SOURCE: [W3C/HTML5/URLs](https://www.w3.org/TR/2010/WD-html5-20100624/urls.html) - -Fixes a problem of mistaking a URI for a file path when it has trailing spaces -and warn about such trailing spaces. diff --git a/changelog.d/pr-10637 b/changelog.d/pr-10637 deleted file mode 100644 index 0cff281a4d0..00000000000 --- a/changelog.d/pr-10637 +++ /dev/null @@ -1,11 +0,0 @@ ---- -synopsis: Fix Haddock CSS handling in multi-package projects -packages: [cabal-install, Cabal] -prs: 10637 -issues: [10636] ---- - -When `--css=` flag is provided to `cabal haddock-project`: - -- the Haddock index is now properly styled by the provided CSS file -- each package in the project now has their docs properly styled by the provided CSS file diff --git a/changelog.d/pr-10644.md b/changelog.d/pr-10644.md deleted file mode 100644 index f6e31264567..00000000000 --- a/changelog.d/pr-10644.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -synopsis: Show source of project parse error or warning -packages: [cabal-install] -prs: 10644 -issues: 10635 ---- - -Improves warning and error messages shown when parsing project files and their -imports. - -## Warning Messages - -To trigger these warning messages, the examples use badly formed comments that -have a single dash instead of two as is required of a line comment in `.cabal` -and `.project` files (and imported `.config` files). - - -* Before the fix: - - The `cabal.project` file name is repeated. Warnings are misattributed to - having been in the project rather than from a configuration file imported by - the project. Warnings are shown in reverse line number order. - - ``` - $ ~/.ghcup/bin/cabal-3.12.1.0 build all --dry-run - ... - Warning: - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 123 - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 3 - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 2 - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 1 - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 123 - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 3 - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 2 - /.../ParseWarningProvenance/cabal.project, - cabal.project, cabal.project, cabal.project, cabal.project: Unrecognized - section '-' on line 1 - ``` - -* After the fix: - - The warnings are shown in a list. For warnings within the same `.project` or - imported `.config` file, warnings are sorted by line number. The file that - is the source of the warning is shown. - - The warnings associated with configuration files are shown in the order - these files were imported by the project: - - ``` - $ cat cabal.project - packages: no-pkg-dir - import: dir-x/a.config - import: dir-y/a.config - import: x.config - import: y.config - ``` - - ``` - $ cabal build all --dry-run - ... - Warnings found while parsing the project file, cabal.project: - - dir-x/a.config: Unrecognized section '-' on line 1 - - dir-x/a.config: Unrecognized section '-' on line 2 - - dir-x/a.config: Unrecognized section '-' on line 3 - - dir-y/a.config: Unrecognized section '-' on line 123 - - x.config: Unrecognized section '-' on line 1 - - x.config: Unrecognized section '-' on line 2 - - x.config: Unrecognized section '-' on line 3 - - y.config: Unrecognized section '-' on line 123 - ``` - -## Error Messages from Project - -To trigger these error messages, the examples use badly formed conditions: - -``` -$ cat cabal.project --- The following failing condition is not on the first line so we can check the --- line number: -if _ -``` - -* Before the fix: - - The parse error is shown with hard line breaks. - - ``` - $ ~/.ghcup/bin/cabal-3.12.1.0 build all --dry-run - ... - Error: [Cabal-7090] - Error parsing project file /.../ParseErrorProvenance/cabal.project:3: - "" (line 1, column 1): - unexpected SecArgName (Position 1 4) "_" - ``` - -* After the fix: - - The snippet that failed to parse may be shown and the parse error is shown - as one line, with no hard line breaks. - - ``` - $ cabal build all --dry-run - ... - Error: [Cabal-7090] - Error parsing project file cabal.project:3: - - Failed to parse 'if(_)' with error: - "" (line 1, column 1): unexpected SecArgName (Position 1 4) "_" - ``` - -## Error Messages from Imported Config - -With the same setup but now with the error in an imported file: - -``` -$ cat elif.project -import: dir-elif/elif.config - -$ cat dir-elif/elif.config --- The following failing condition is not on the first line so we can check the --- line number: -if false -elif _ -``` - -* Before the fix: - - The project rather than the imported configuration file is shown as the source file. - - ``` - $ ~/.ghcup/bin/cabal-3.12.1.0 build all --dry-run - ... - Error: [Cabal-7090] - Error parsing project file /.../ParseErrorProvenance/elif.project:4: - "" (line 1, column 1): - unexpected SecArgName (Position 1 6) "_" - ``` - -* After the fix: - - The imported configuration file is shown as the source with a snippet of the error. - - ``` - $ cabal build all --dry-run - ... - Error: [Cabal-7090] - Error parsing project file dir-elif/elif.config:4: - - dir-elif/elif.config - imported by: elif.project - - Failed to parse 'elif(_)' with error: - "" (line 1, column 1): unexpected SecArgName (Position 1 6) "_" - ``` diff --git a/changelog.d/pr-10646.md b/changelog.d/pr-10646.md deleted file mode 100644 index fb7dd8c9ec7..00000000000 --- a/changelog.d/pr-10646.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -synopsis: Configuration messages without duplicates -packages: [cabal-install-solver] -prs: 10646 -issues: 10645 ---- - -The "using configuration from" message no longer has duplicates on Windows when -a `cabal.project` uses forward slashes for its imports but the message reports -the same import again with backslashes. - -```diff -$ cat cabal.project -import: dir-a/b.config - -$ cabal build all --dry-run -... -When using configuration from: -- - dir-a/b.config - - dir-a\b.config - - cabal.project -``` - -## Changed `Ord ProjectConfigPath` Instance - -For comparison purposes, path separators are normalized to the `buildOS` -platform's path separator. - -```haskell --- >>> let abFwd = ProjectConfigPath $ "a/b.config" :| [] --- >>> let abBwd = ProjectConfigPath $ "a\\b.config" :| [] --- >>> compare abFwd abBwd --- EQ -``` - -## Changes in `cabal-testsuite` - -### Reading Expected Multiline Strings Verbatim - -With `ghc-9.12.1` adding `-XMultilineStrings`, writing multiline string -expectations for `cabal-testsuite/PackageTests/**/*.test.hs` test scripts might -be have been easier but for a catch. We run these tests with older `GHC` -versions so would need to use `-XCPP` for those versions and the C preprocessor -does not play nicely with string gaps. While it is possible to encode a -multiline string as a single line with embedded LF characters or by breaking the -line up arbitrarily and using `++` concatenation or by calling unlines on a list -of lines, string gaps are the multiline strings of Haskell prior to -`-XMultilineStrings`. - -To avoid these problems and for the convenience of pasting the expected value -verbatim into a file, `readFileVerbatim` can read the expected multiline output -for tests from a text file. This has the same implementation as `readFile` from -the `strict-io` package to avoid problems at cleanup. - -``` -Warning: Windows file locking hack: hit the retry limit 3 while trying to remove -C:\Users\\AppData\Local\Temp\cabal-testsuite-8376 -cabal.test.hs: -C:\Users\\AppData\Local\Temp\cabal-testsuite-8376\errors.expect.txt: removePathForcibly:DeleteFile -"\\\\?\\C:\\Users\\\\AppData\\Local\\Temp\\cabal-testsuite-8376\\errors.expect.txt": -permission denied (The process cannot access the file because it is being used by another process.) -``` - -The other process accessing the file is `C:\WINDOWS\System32\svchost.exe` -running a `QueryDirectory` event and this problem only occurs when the test -fails. - -### Hidden Actual Value Modification - -The `assertOutputContains` function was modifying the actual value (the test -output) with `concatOutput` before checking if it contained the expected value. -This function, now renamed as `lineBreaksToSpaces`, would remove CR values and -convert LF values to spaces. - -```haskell --- | Replace line breaks with spaces, correctly handling @"\\r\\n"@. --- --- >>> lineBreaksToSpaces "foo\nbar\r\nbaz" --- "foo bar baz" --- --- >>> lineBreaksToSpaces "foo\nbar\r\nbaz\n" --- "foo bar baz" --- --- >>> lineBreaksToSpaces "\nfoo\nbar\r\nbaz\n" --- " foo bar baz" -lineBreaksToSpaces :: String -> String -``` - -With this setup, false positives were possible. An expected value using string -gaps and spaces would match a `concatOutput` modified actual value of -"foo_bar_baz", where '_' was any of space, LF or CRLF in the unmodified actual -value. The latter two are false positive matches. - -```haskell -let expect = "foo \ - \bar \ - \baz" -``` - -False negatives were also possible. An expected value set up using string gaps -with LF characters or with `-XMultilineStrings` wouldn't match an actual value -of "foo_bar_baz", where '_' was either LF or CRLF because these characters had -been replaced by spaces in the actual value, modified before the comparison. - -```haskell -let expect = "foo\n\ - \bar\n\ - \baz" -``` - -```haskell -{-# LANGUAGE MultilineStrings #-} - -let expect = """ - foo - bar - baz - """ -``` - -We had these problems: - -1. The actual value was changed before comparison and this change was not visible. -2. The expected value was not changed in the same way as the actual value. This - made it possible for equal values to become unequal (false negatives) and for - unequal values to become equal (false positives). - -### Explicit Changes and Visible Line Delimiters - -To fix these problems, an added `assertOn` function takes a `NeedleHaystack` -configuration for how the search is made, what to expect (to find the expected -value or not) and how to display the expected and actual values. - -A pilcrow ¶ is often used to visibly display line endings but our terminal -output is restricted to ASCII so lines are delimited between `^` and `$` -markers. The needle (the expected output fragment) is shown annotated this way -and the haystack (the actual output) can optionally be shown this way too. - -We can now implement `assertOutputContains` by calling `assertOn`: - -```diff - assertOutputContains :: MonadIO m => WithCallStack (String -> Result -> m ()) -- assertOutputContains needle result = -- withFrozenCallStack $ -- unless (needle `isInfixOf` (concatOutput output)) $ -- assertFailure $ " expected: " ++ needle -- where output = resultOutput result -+ assertOutputContains = assertOn -+ needleHaystack -+ {txHaystack = -+ TxContains -+ { txBwd = delimitLines -+ , txFwd = encodeLf -+ } -+ } -``` - -This is still a lenient match, allowing LF to match CRLF, but `encodeLf` doesn't -replace LF with spaces like `concatOutput` (`lineBreaksToSpaces`) did: - -```haskell --- | Replace line CRLF line breaks with LF line breaks. --- --- >>> encodeLf "foo\nbar\r\nbaz" --- "foo\nbar\nbaz" --- --- >>> encodeLf "foo\nbar\r\nbaz\n" --- "foo\nbar\nbaz\n" --- --- >>> encodeLf "\nfoo\nbar\r\nbaz\n" --- "\nfoo\nbar\nbaz\n" --- --- >>> encodeLf "\n\n\n" --- "\n\n\n" -encodeLf :: String -> String -``` - -If you choose to display the actual value by setting -`NeedleHaystack{displayHaystack = True}` then its lines will be delimited. - -```haskell --- | Mark lines with visible delimiters, @^@ at the start and @$@ at the end. --- --- >>> delimitLines "" --- "^$" --- --- >>> delimitLines "\n" --- "^$\n" --- --- >>> delimitLines "\n\n" --- "^$\n^$\n" --- --- >>> delimitLines "\n\n\n" --- "^$\n^$\n^$\n" --- --- >>> delimitLines $ encodeLf "foo\nbar\r\nbaz" --- "^foo$\n^bar$\n^baz$" --- --- >>> delimitLines $ encodeLf "foo\nbar\r\nbaz\n" --- "^foo$\n^bar$\n^baz$\n" --- --- >>> delimitLines $ encodeLf "\nfoo\nbar\r\nbaz\n" --- "^$\n^foo$\n^bar$\n^baz$\n" -delimitLines:: String -> String -``` - -With `assertOn`, supplying string transformation to both the needle and haystack -before comparison and before display can help find out why an expected value is -or isn't found in the test output. diff --git a/changelog.d/pr-10647 b/changelog.d/pr-10647 deleted file mode 100644 index 4a87939f8b6..00000000000 --- a/changelog.d/pr-10647 +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: 'Improve "Cannot read .cabal file inside ..." errors' -packages: [cabal-install] -prs: 10647 ---- - -The error message printed when Cabal fails to read a `.cabal` file inside an -index (like the Hackage index) has been greatly improved. Additionally, -warnings in `.cabal` files in indexes are printed instead of being silently -discarded. diff --git a/changelog.d/pr-10705 b/changelog.d/pr-10705 deleted file mode 100644 index bc6e2cd219e..00000000000 --- a/changelog.d/pr-10705 +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: 'Add support for Windows AArch64' -packages: [Cabal, cabal-install] -prs: 10705 ---- - -Adds to preprocessor branches the option to support `aarch64_HOST_ARCH` platform on Windows AArch64 target. -`ccall` convention is used at AArch64, same as for `x86_64_HOST_ARCH`. Introduce `zIsAArch64` to make paths generation support Windows AArch64 target. diff --git a/changelog.d/pr-10805.md b/changelog.d/pr-10805.md deleted file mode 100644 index 5b3bf8b2c05..00000000000 --- a/changelog.d/pr-10805.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -synopsis: Merge 'flags' stanzas in cabal.project files -packages: [cabal-install] -prs: 10805 -issues: [10767] ---- - -Merge 'flags' stanzas in cabal.project files. Now - -``` -flags: -foo -flags: -bar -``` - -is equivalent to `flags: -foo -bar` while before it was equivalent to `flags: -bar` -(only the latest stanza was taken into account, the other ones were silently -ignored). diff --git a/changelog.d/pr-10816.md b/changelog.d/pr-10816.md deleted file mode 100644 index 06118ecbb5b..00000000000 --- a/changelog.d/pr-10816.md +++ /dev/null @@ -1,23 +0,0 @@ -synopsis: "Add checks for Windows reserved filenames in module paths" -packages: Cabal cabal-install -prs: #10816 -issues: #10295 - -description: { - -On Windows, certain filenames are reserved by the operating system such as -"aux", "con", "prn", "nul", etc. When these names appear in module paths they -can cause build failures on Windows systems. - -`cabal check` now properly warns about module paths that contain Windows reserved -filenames, not just filepaths which contain these reserved tokens. These warnings -are controlled by the existing `invalid-win-path` category. - -For example, module paths like: -- `Exe.Aux.Test` -- `Test.Aux.Module` -- `Bench.Aux.Helpers` - -will now trigger appropriate warnings during `cabal check`. - -} diff --git a/changelog.d/pr-10817 b/changelog.d/pr-10817 deleted file mode 100644 index 4e559b36e15..00000000000 --- a/changelog.d/pr-10817 +++ /dev/null @@ -1,12 +0,0 @@ -synopsis: Fix PATH changes not triggering REPL reconfiguration -packages: cabal-install -prs: #10817 -issues: #2015 - -Previously, if you changed your PATH after initial configuration, Cabal would -continue using the old PATH settings in REPL sessions without warning. -With this fix, Cabal properly detects PATH changes and reconfigures the REPL -accordingly, ensuring tools and libraries in your current PATH are available. -While this may cause additional rebuilds when PATH changes, it prevents the -confusing errors that could occur when your REPL environment didn't match your -system configuration. diff --git a/changelog.d/pr-10828.md b/changelog.d/pr-10828.md deleted file mode 100644 index d364b7cb252..00000000000 --- a/changelog.d/pr-10828.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -synopsis: "Isolate Cabal from the GHC_ENVIRONMENT variable" -packages: [Cabal] -prs: 10828 10990 - -issues: 10759 ---- - -For GHC 8.4.4 and later, Cabal now passes the `-package-env=-` flag to GHC. -This prevents the `GHC_ENVIRONMENT` variable or any package environment files -from affecting Cabal builds. - -This change eliminates unexpected build behavior that could occur when users -had GHC environment files configured in their system that Cabal wasn't aware -of. diff --git a/changelog.d/pr-10830.md b/changelog.d/pr-10830.md deleted file mode 100644 index 21d179f0d06..00000000000 --- a/changelog.d/pr-10830.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: Set _datadir to an absolute path when running tests -packages: [Cabal] -prs: 10830 -issues: [10717] ---- - -Fix a regression where `_datadir` was set to a relative path. This -caused issues when running testsuites which changed the working directory and -accessed datafiles. diff --git a/changelog.d/pr-10840.md b/changelog.d/pr-10840.md deleted file mode 100644 index 0652ba03ca2..00000000000 --- a/changelog.d/pr-10840.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -synopsis: Fix gen-bounds command to work in multi-package projects -packages: [cabal-install] -prs: 10840 -issues: [7504] ---- - -`cabal gen-bounds` now works in multi-package projects. - -The command has been reimplemented to use the cabal.project infrastructure (similar -to other v2 commands), allowing it to be aware of all packages defined in the cabal.project -file, regardless of which directory it's executed from. - -``` -$ cat cabal.project -packages: package-a/ - package-b/ - -$ cd package-b/ -$ cabal gen-bounds -Configuration is affected by the following files: -- cabal.project -Resolving dependencies... - -The following packages need bounds and here is a suggested starting point... -For component package-b:lib:package-b: -package-a >= 0.1.0 && < 0.2, -``` diff --git a/changelog.d/pr-10844.md b/changelog.d/pr-10844.md deleted file mode 100644 index 184992f8ba2..00000000000 --- a/changelog.d/pr-10844.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -synopsis: Pass CXX and CXXFLAGS to ./configure scripts run by Configure build-type -packages: [Cabal] -prs: 10844 -issues: [10797] ---- - -./configure scripts run by build-type: Configure will now be passed the CXX and -CXXFLAGS variables. These reflect the path and flags for the C++ compiler. - -If the compiler is not available, then the flags are not passed. For GHC versions >= 9.4.*, -the CXX variable will always be set and available to be used. - -This can be useful for implementing something like `system-cxx-std-lib` in user-land. diff --git a/changelog.d/pr-10880.md b/changelog.d/pr-10880.md deleted file mode 100644 index 6b8defe7eb5..00000000000 --- a/changelog.d/pr-10880.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: 'Fix multi-repl when using reexported-modules with renaming for GHC >= 9.12' -packages: [cabal-install, Cabal] -prs: 10880 -issues: 10181 ---- - -Since GHC 9.12, the `-reexported-module` flag has supported module renaming. Therefore -we now use that functionality when starting the multi-repl if it is needed. A new -error message is added to catch the case where you attempt to load a project which -uses this complicated export form but are using < 9.12. - diff --git a/changelog.d/pr-10908.md b/changelog.d/pr-10908.md deleted file mode 100644 index 15b9efcc932..00000000000 --- a/changelog.d/pr-10908.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: Remove dead build-tool greencard -packages: [Cabal,cabal-install] -prs: 10908 ---- - -Remove knowledge of the build-tool `greencard` (non-existing since GHC 7.10) -and the connect of the `.gc` extension to this build-tool. diff --git a/changelog.d/pr-10912.md b/changelog.d/pr-10912.md deleted file mode 100644 index 01c48da42c1..00000000000 --- a/changelog.d/pr-10912.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: Remove knowledge about hmake and haskell-suite -packages: [Cabal, cabal-install] -prs: 10912 ---- - -The `hmake` tool has long been abandoned, and the `haskell-suite` compiler did not emerge. -Knowledge about these tools has been removed from `Cabal`. diff --git a/changelog.d/pr-10936.md b/changelog.d/pr-10936.md deleted file mode 100644 index f005f2015d5..00000000000 --- a/changelog.d/pr-10936.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -synopsis: Print out the "Created semaphore" message only in verbose mode (#10885) -packages: [cabal-install] -prs: 10936 -issues: 10885 ---- diff --git a/changelog.d/pr-10940 b/changelog.d/pr-10940 deleted file mode 100644 index 4023cfe3af9..00000000000 --- a/changelog.d/pr-10940 +++ /dev/null @@ -1,17 +0,0 @@ ---- -synopsis: print out project files only when run below the project root (or verbose) -packages: [cabal-install] -prs: 10940 -issues: 10885 ---- - -After #10507, cabal prints out what project files are in use on every run. -This looked too noisy for some users (#10885). In this patch, we implement a more nuanced -strategy: print out this info only when cabal is run below the root project directory. -As before, you can get this information uncoditionally if run in the verbose mode. - -Bonus: we now also print the project root directory along with the file names. -Before, we only printed, say, `cabal.project`, but it wasn't clear where this file is -coming from (can be anywhere up the directory tree). -The change tries to avoid confusion when cabal picks up stray project files in -ancestor directories, see discussion in #7930. diff --git a/changelog.d/pr-10941 b/changelog.d/pr-10941 deleted file mode 100644 index 2b4b56e5055..00000000000 --- a/changelog.d/pr-10941 +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: "Haddock documentation added to Paths_pkgname and PackageInfo_pkgname modules" -packages: [Cabal] -prs: 10941 ---- - -The automatically-generated modules `Paths_pkgname` and `PackageInfo_pkgname` -now include Haddock documentation. diff --git a/changelog.d/pr-10948.md b/changelog.d/pr-10948.md deleted file mode 100644 index 83757e5f47e..00000000000 --- a/changelog.d/pr-10948.md +++ /dev/null @@ -1,18 +0,0 @@ -synopsis: Make `Flag a` a type synonym for `Last (Maybe a)` -packages: Cabal cabal-install -prs: #10948 - -description: { - -- Replace `data Flag a = Flag a | NoFlag` with `type Flag = Data.Monoid.Last` - and provide `pattern Flag a = Last (Just a)` and `pattern NoFlag = Last Nothing` - for backward compatibility. -- The change aims to be non-breaking for the majority of clients, although - imports of form `import Distribution.Simple.Flag (Flag (..))` should be replaced - with `import Distribution.Simple.Flag (Flag, pattern Flag, pattern NoFlag)` - (the latter form is backwards compatible with older versions of Cabal). - Enable language extension `PatternSynonyms` if required. In the unlikely case - of defining instances for `Flag`, `TypeSynonymInstances` extension - is needed. - -} diff --git a/changelog.d/pr-10949.md b/changelog.d/pr-10949.md deleted file mode 100644 index ccb15cd0b36..00000000000 --- a/changelog.d/pr-10949.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -synopsis: "ghc-options added to js, cmm and asm" -packages: [Cabal] -prs: 10949 ---- - -Now we have the ability to pass ghc-options flags to all component. - -``` - js-sources: jsbits/lib.js - ghc-options: -optJSP-your_flag -``` - diff --git a/changelog.d/pr-10966.md b/changelog.d/pr-10966.md deleted file mode 100644 index 7761ddb8bda..00000000000 --- a/changelog.d/pr-10966.md +++ /dev/null @@ -1,20 +0,0 @@ -synopsis: Allow build-type: Configure -packages: cabal-install -prs: #10966 -issues: #4548 -significance: significant - -description: { - - This change allows the `build-type: Configure` field to be used in - Cabal packages in conjunction with components. (E.g. public sublibraries). - This is a significant change as it enables the use of the `Configure` build - type, which cabal bailed on previously. - - This does change the behaviour of cabal-install for packages that contain - `build-type: Configure`. We now always build them as components with - cabal-install. Previously we would turn packages with main libraries, and - executables/benchmarks/tests-suites into a single package to be built in a - compatibility mode. This is no longer the case. - -} diff --git a/changelog.d/pr-10967.md b/changelog.d/pr-10967.md deleted file mode 100644 index 96d59d6de36..00000000000 --- a/changelog.d/pr-10967.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -synopsis: Adding JavaScript Preprocessor Support -packages: [Cabal, Cabal-syntax] -prs: 10967 ---- - -Adding new syntax jspp-options that would support the -optJSP preprocessor flag for js sources. - -``` - js-sources: jsbits/lib.js - jspp-options: -DYOUR_FLAG -``` - diff --git a/changelog.d/pr-10979 b/changelog.d/pr-10979 deleted file mode 100644 index 7e3fb85502c..00000000000 --- a/changelog.d/pr-10979 +++ /dev/null @@ -1,8 +0,0 @@ -synopsis: Fix parsing the AbiTag with development versions of GHC -packages: Cabal -prs: #10979 -issues: #10170 - -description: { -- Allow parsing the AbiTag for developmement versions of GHC. -} diff --git a/changelog.d/pr-10991.md b/changelog.d/pr-10991.md deleted file mode 100644 index 19bb10d99ed..00000000000 --- a/changelog.d/pr-10991.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -synopsis: "Take --working-dir into account in runPreProcessorWithHsBootHack" -packages: [Cabal] -prs: 10991 -issues: [11000] ---- - -The preprocessor hs-boot hack handles the situation in which a file to be -preprocessed is supplied alongside an hs-boot file, e.g. Foo.x and Foo.hs-boot -are given together. - -This code now respects the --working-dir Cabal global argument. This fixes -build failures of the form: - - attempting to use module `Foo` (Foo.hs-boot) which is not loaded diff --git a/changelog.d/pr-10992.md b/changelog.d/pr-10992.md deleted file mode 100644 index de3a89df3bf..00000000000 --- a/changelog.d/pr-10992.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -synopsis: "Haddock: don't try to copy build dir if it doesn't exist" -packages: [Cabal] -prs: 10992 -issues: [11001] ---- - -This small patch fixes a little oversight in 'reusingGHCCompilationArtifacts', -which would unconditionally attempt to copy over the GHC build artifacts to be -re-used by Haddock, even when those artifacts did not exist (which caused -an error). diff --git a/changelog.d/pr-11003.md b/changelog.d/pr-11003.md deleted file mode 100644 index cdab59c146c..00000000000 --- a/changelog.d/pr-11003.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: Make lukko flag automatic and off by default -packages: [cabal-install] -prs: 11003 -issues: 10724 ---- - -Make `lukko` flag automatic and off by default, using file locking facilities from `base:GHC.IO.Handle.Lock` and not from the `lukko` package. The change is not expected to affect anyone detrimentally, but one can set the flag on in their configuration to restore the previous behaviour. diff --git a/changelog.d/pr-11014 b/changelog.d/pr-11014 deleted file mode 100644 index c24391a1d65..00000000000 --- a/changelog.d/pr-11014 +++ /dev/null @@ -1,9 +0,0 @@ -synopsis: add SPDX license data version 3.26 -packages: Cabal -prs: #11014 - -description: { - -- Update SPDX license list to version 3.26.0 2024-12-30 - -} diff --git a/changelog.d/pr-9367 b/changelog.d/pr-9367 deleted file mode 100644 index 806efcc5c11..00000000000 --- a/changelog.d/pr-9367 +++ /dev/null @@ -1,21 +0,0 @@ ---- -synopsis: "Use response files when calling `ghc`" -packages: [Cabal] -prs: 9367 ---- - -Cabal now passes GHC arguments through a response file. This prevents an error -where very large packages would fail to build due to the module names exceeding -the `ARG_MAX` command-line length limit: - -``` -ghc: createProcess: posix_spawnp: resource exhausted (Argument list too long) -``` - -Notes: -- Response files will not be used if the first argument to `ghc` is - `--interactive`. -- RTS options (like `+RTS -H32m -S -RTS`) will not be included in response - files. -- For reproducing commands after Cabal exits, `--keep-temp-files` can be used - to prevent the response files from being deleted. diff --git a/changelog.d/pr-9744.md b/changelog.d/pr-9744.md deleted file mode 100644 index 6f149d82162..00000000000 --- a/changelog.d/pr-9744.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -synopsis: Discover targets in a project -packages: [cabal-install] -prs: 9744 -issues: [4070,8953] -significance: significant ---- - -Adds a `cabal target` command that is useful for discovering targets in a -project for use with other commands taking ``[TARGETS]``. - -Any target form except for a script target can be used with ``cabal target``. - -This command, like many others, takes ``[TARGETS]``. Taken together, these will -select for a set of targets in the project. When none are supplied, the command -acts as if ``all`` was supplied. Targets in the returned subset are shown sorted -and fully-qualified with package, component type and component name such as -`Cabal-tests:test:hackage-tests`. diff --git a/changelog.d/pr-9890 b/changelog.d/pr-9890 deleted file mode 100644 index e9861aed7af..00000000000 --- a/changelog.d/pr-9890 +++ /dev/null @@ -1,10 +0,0 @@ -synopsis: `cabal init` generates explicit export lists for Main -packages: cabal-install -prs: #9890 -issues: #9889 - -description: { - -- Lack of explicit export list can degrade performance. The `Main` module in particular should always have an explicit export list that contains just the main function. Then, the compiler can do more aggressive optimizations on all the other non-exported functions. - -} diff --git a/release-notes/Cabal-3.16.0.0.md b/release-notes/Cabal-3.16.0.0.md new file mode 100644 index 00000000000..dca6d293e36 --- /dev/null +++ b/release-notes/Cabal-3.16.0.0.md @@ -0,0 +1,199 @@ +Cabal and Cabal-syntax 3.16.0.0 changelog and release notes +--- + +### Significant changes + +- Make `Flag a` a type synonym for `Last (Maybe a)` [#10948](https://github.com/haskell/cabal/pull/10948) + + Refer to the [Migration guide](#migration-guide) for an upgrade path. + +- Add support for Windows AArch64 [#10705](https://github.com/haskell/cabal/pull/10705) + + Adds to preprocessor branches the option to support the `aarch64_HOST_ARCH` platform on the Windows AArch64 target. + The `ccall` convention is used on AArch64, as with `x86_64_HOST_ARCH`. Introduce `zIsAArch64` to make paths generation support the Windows AArch64 target. + +### Other changes + +- Add `--with-repl` flag to specify alternative REPL program [#9115](https://github.com/haskell/cabal/issues/9115) [#10996](https://github.com/haskell/cabal/pull/10996) + + Added a new `--with-repl` command-line option that allows specifying an alternative + program to use when starting a REPL session, instead of the default GHC. + + This is particularly useful for tools like `doctest` and `hie-bios` that need to + intercept the REPL session to perform their own operations. Previously, these tools + had to use `--with-ghc` which required them to proxy all GHC invocations, including + dependency compilation, making the implementation more complex. + + The `--with-repl` option only affects the final REPL invocation, simplifying the + implementation of such wrapper tools. + + Example usage: + ```bash + cabal repl --with-repl=doctest + cabal repl --with-repl=/path/to/custom/ghc + ``` + + This change also removes the special handling for response files with `--interactive` + mode, as tools are now expected to handle response files appropriately. + +- In the warning about Haddock docs not being installed, don't imply that the docs exist [#9694](https://github.com/haskell/cabal/issues/9694) [#9695](https://github.com/haskell/cabal/pull/9695) + +- Show why `cabal act-as-setup configure` failed [#10273](https://github.com/haskell/cabal/pull/10273) + + When configuring fails, we print a list of "missing or private dependencies". + + Now, it will show you if each failing dependency is missing, private, or an + incompatible version: + + ``` + Error: [Cabal-8010] + Encountered missing or private dependencies: + Lib:{bar-internal,foo-internal} (missing :bar-internal), + base <=1.0 (installed: 4.18.2.1), + package-that-does-not-exist (missing) + ``` + +- Avoid partial `Data.List.last` in autogenerated `Paths_.hs` [#10432](https://github.com/haskell/cabal/pull/10432) + + - Autogenerated `Paths_.hs` now avoids use of the partial function `Data.List.last`. + +- Fix Haddock CSS handling in multi-package projects [#10636](https://github.com/haskell/cabal/issues/10636) [#10637](https://github.com/haskell/cabal/pull/10637) + + When the `--css=` flag is provided to `cabal haddock-project`: + + - the Haddock index is now properly styled by the provided CSS file + - each package in the project now has its docs properly styled by the provided CSS file + +- Add checks for Windows reserved filenames in module paths [#10295](https://github.com/haskell/cabal/issues/10295) [#10816](https://github.com/haskell/cabal/pull/10816) + + On Windows, certain filenames are reserved by the operating system such as + "aux", "con", "prn", "nul", etc. When these names appear in module paths they + can cause build failures on Windows systems. + + `cabal check` now properly warns about module paths that contain Windows reserved + filenames, not just filepaths which contain these reserved tokens. These warnings + are controlled by the existing `invalid-win-path` category. + + For example, module paths like: + - `Exe.Aux.Test` + - `Test.Aux.Module` + - `Bench.Aux.Helpers` + + will now trigger appropriate warnings during `cabal check`. + +- Isolate Cabal from the GHC_ENVIRONMENT variable [#10759](https://github.com/haskell/cabal/issues/10759) [#10828](https://github.com/haskell/cabal/pull/10828) [#10990](https://github.com/haskell/cabal/pull/10990) + + For GHC 8.4.4 and later, Cabal now passes the `-package-env=-` flag to GHC. + This prevents the `GHC_ENVIRONMENT` variable or any package environment files + from affecting Cabal builds. + + This change eliminates unexpected build behavior that could occur when users + had GHC environment files configured in their system that Cabal wasn't aware + of. + +- Set `_datadir` to an absolute path when running tests [#10717](https://github.com/haskell/cabal/issues/10717) [#10830](https://github.com/haskell/cabal/pull/10830) + + Fix a regression where `_datadir` was set to a relative path. This + caused issues when running testsuites which changed the working directory and + accessed datafiles. + +- Pass `CXX` and `CXXFLAGS` to `./configure` scripts run by Configure build-type [#10797](https://github.com/haskell/cabal/issues/10797) [#10844](https://github.com/haskell/cabal/pull/10844) + + `./configure` scripts run by `build-type: Configure` will now be passed the `CXX` and + `CXXFLAGS` variables. These reflect the path and flags for the C++ compiler respectively. + + If the compiler is not available, then the flags are not passed. For GHC versions `>= 9.4.*`, + the `CXX` variable will always be set and available to be used. + + This can be useful for implementing something like `system-cxx-std-lib` in user-land. + +- Remove dead build-tool greencard [#10908](https://github.com/haskell/cabal/pull/10908) + + Remove knowledge of the build-tool `greencard` (non-existing since GHC 7.10) + and the connect of the `.gc` extension to this build-tool. + +- Remove knowledge about hmake and haskell-suite [#10912](https://github.com/haskell/cabal/pull/10912) + + The `hmake` tool has long been abandoned, and the `haskell-suite` compiler did not emerge. + Knowledge about these tools has been removed from `Cabal`. + +- Haddock documentation added to Paths_pkgname and PackageInfo_pkgname modules [#10941](https://github.com/haskell/cabal/pull/10941) + + The automatically-generated modules `Paths_pkgname` and `PackageInfo_pkgname` + now include Haddock documentation. + +- `ghc-options` take effect for components built out of js-, cmm- and asm-sources [#10949](https://github.com/haskell/cabal/pull/10949) + + Now we have the ability to pass `ghc-options` flags to all component. + + ``` + js-sources: jsbits/lib.js + ghc-options: -optJSP-your_flag + ``` + +- Adding JavaScript Preprocessor Support [#10967](https://github.com/haskell/cabal/pull/10967) + + Adding new syntax `jspp-options` that would support the `-optJSP` preprocessor flag for js sources. + + ``` + js-sources: jsbits/lib.js + jspp-options: -DYOUR_FLAG + ``` + +- Fix parsing the AbiTag with development versions of GHC [#10170](https://github.com/haskell/cabal/issues/10170) [#10979](https://github.com/haskell/cabal/pull/10979) + +- Take `--working-dir` into account in `runPreProcessorWithHsBootHack` [#11000](https://github.com/haskell/cabal/issues/11000) [#10991](https://github.com/haskell/cabal/pull/10991) + + The preprocessor hs-boot hack handles the situation in which a file to be + preprocessed is supplied alongside an hs-boot file, e.g. Foo.x and Foo.hs-boot + are given together. + + This code now respects the `--working-dir` Cabal global argument. This fixes + build failures of the form: + + ``attempting to use module `Foo' (Foo.hs-boot) which is not loaded`` + +- Haddock: don't try to copy build dir if it doesn't exist [#11001](https://github.com/haskell/cabal/issues/11001) [#10992](https://github.com/haskell/cabal/pull/10992) + + This small patch fixes a little oversight in 'reusingGHCCompilationArtifacts', + which would unconditionally attempt to copy over the GHC build artifacts to be + re-used by Haddock, even when those artifacts did not exist (which caused + an error). + +- add SPDX license data version 3.26 [#11014](https://github.com/haskell/cabal/pull/11014) + + - Update SPDX license list to version 3.26.0 2024-12-30 + +- Use response files when calling `ghc` [#9367](https://github.com/haskell/cabal/pull/9367) + + Cabal now passes GHC arguments through a response file. This prevents an error + where very large packages would fail to build due to the module names exceeding + the `ARG_MAX` command-line length limit: + + ``` + ghc: createProcess: posix_spawnp: resource exhausted (Argument list too long) + ``` + + Notes: + - RTS options (like `+RTS -H32m -S -RTS`) will not be included in response + files. + - For reproducing commands after Cabal exits, `--keep-temp-files` can be used + to prevent the response files from being deleted. + +- New licence constructors for SPDX licences starting with a digit [#10356](https://github.com/haskell/cabal/pull/10356) + + - LicenseId constructor `NullBSD` is now `N_0BSD`. + - LicenseId constructor `DS389_exception` is now and `N_389_exception`. + - LicenseId constructor `X3D_Slicer_1_0` is now and `N_3D_Slicer_1_0`. + +### Migration guide + +- `data Flag a = Flag a | NoFlag` has been replaced with `type Flag = Data.Monoid.Last`, + while `pattern Flag a = Last (Just a)` and `pattern NoFlag = Last Nothing` are provided + for backward compatibility. + Imports of form `import Distribution.Simple.Flag (Flag (..))` should be replaced + with `import Distribution.Simple.Flag (Flag, pattern Flag, pattern NoFlag)` + (the latter form is backwards compatible with older versions of Cabal). + Enable `{-# LANGUAGE PatternSynonyms #-}` if required. In the unlikely case + of defining instances for `Flag`, `{-# LANGUAGE TypeSynonymInstances #-}` + is needed. diff --git a/release-notes/WIP-Cabal-3.16.x.0.md b/release-notes/WIP-Cabal-3.16.x.0.md deleted file mode 100644 index 1c208dbcf4d..00000000000 --- a/release-notes/WIP-Cabal-3.16.x.0.md +++ /dev/null @@ -1,18 +0,0 @@ -Cabal 3.16.x.0 changelog and release notes. - -This file will be edited and the changes incorporated into the official -3.16.x.0 Cabal and Cabal-syntax release notes. - ---- - -### Migration guide - -- `data Flag a = Flag a | NoFlag` has been replaced with `type Flag = Data.Monoid.Last`, - while `pattern Flag a = Last (Just a)` and `pattern NoFlag = Last Nothing` are provided - for backward compatibility. - Imports of form `import Distribution.Simple.Flag (Flag (..))` should be replaced - with `import Distribution.Simple.Flag (Flag, pattern Flag, pattern NoFlag)` - (the latter form is backwards compatible with older versions of Cabal). - Enable `{-# LANGUAGE PatternSynonyms #-}` if required. In the unlikely case - of defining instances for `Flag`, `{-# LANGUAGE TypeSynonymInstances #-}` - is needed. diff --git a/release-notes/cabal-install-3.16.0.0.md b/release-notes/cabal-install-3.16.0.0.md new file mode 100644 index 00000000000..ee98d7ead6b --- /dev/null +++ b/release-notes/cabal-install-3.16.0.0.md @@ -0,0 +1,292 @@ +cabal-install 3.16.0.0 changelog and release notes. +--- + + +### Significant changes + +- Allow `build-type: Configure` to work with components (including public sub-libraries) [#4548](https://github.com/haskell/cabal/issues/4548) [#10966](https://github.com/haskell/cabal/pull/10966) + + This change allows the `build-type: Configure` field to be used in + Cabal packages in conjunction with components. (E.g. public sublibraries). + This is a significant change as it enables the use of the `Configure` build + type, which cabal bailed on previously. + + This does change the behaviour of cabal-install for packages that contain + `build-type: Configure`. We now always build them as components with + cabal-install. Previously we would turn packages with main libraries, and + executables/benchmarks/tests-suites into a single package to be built in a + compatibility mode. This is no longer the case. + +- Discover targets in a project [#4070](https://github.com/haskell/cabal/issues/4070) [#8953](https://github.com/haskell/cabal/issues/8953) [#9744](https://github.com/haskell/cabal/pull/9744) + + Adds a `cabal target` command that is useful for discovering targets in a + project for use with other commands taking ``[TARGETS]``. + + Any target form except for a script target can be used with ``cabal target``. + + This command, like many others, takes ``[TARGETS]``. Taken together, these will + select for a set of targets in the project. When none are supplied, the command + acts as if ``all`` was supplied. Targets in the returned subset are shown sorted + and fully-qualified with package, component type and component name such as + `Cabal-tests:test:hackage-tests`. + +- Add `--with-repl` flag to specify alternative REPL program [#9115](https://github.com/haskell/cabal/issues/9115) [#10996](https://github.com/haskell/cabal/pull/10996) + + Added a new `--with-repl` command-line option that allows specifying an alternative + program to use when starting a REPL session, instead of the default GHC. + + This is particularly useful for tools like `doctest` and `hie-bios` that need to + intercept the REPL session to perform their own operations. Previously, these tools + had to use `--with-ghc` which required them to proxy all GHC invocations, including + dependency compilation, making the implementation more complex. + + The `--with-repl` option only affects the final REPL invocation, simplifying the + implementation of such wrapper tools. + + Example usage: + ```bash + cabal repl --with-repl=doctest + cabal repl --with-repl=/path/to/custom/ghc + ``` + + This change also removes the special handling for response files with `--interactive` + mode, as tools are now expected to handle response files appropriately. + +### Other changes + +- Fix `cabal clean` permissions on Windows [#10182](https://github.com/haskell/cabal/issues/10182) [#10190](https://github.com/haskell/cabal/pull/10190) + + - `cabal clean` now removes the read-only mark recursively in the `dist-newstyle` folder on Windows before attempting to delete it. + +- Shallow and concurrent cloning of git repositories [#10254](https://github.com/haskell/cabal/pull/10254) + + - Clone git repositories specified in `source-repository-package` stanzas + shallowly, since to build the package from the repository we only need to + read the commit specified. The rest of the repo is not needed. + Note that this does not change the behaviour of `cabal get -s`, which will + still clone the repository in full. + - Clone VCS repositories concurrently, with a maximum of two concurrent tasks + at the same time (just like when downloading packages asynchronously) + +- Don't run submodule commands unless necessary [#10590](https://github.com/haskell/cabal/pull/10590) + + When `cabal` clones a Git repo for a `source-repository-package` listed in a + `cabal.project`, it will run various commands to check out the correct + revision, initialize submodules if they're present, and so on. + + Now, `cabal` will avoid running `git submodule` commands unless the cloned + repository contains a `.gitmodules` file. This will declutter `cabal`'s debug + output by running fewer commands. + +- A trailing colon after a stanza name in `cabal.project` is now an error [#10525](https://github.com/haskell/cabal/pull/10525) + + It is now a hard error to use a trailing colon after a stanza name in + `cabal.project` or `*.cabal` files: + + ``` + packages: . + + source-repository-package: + type: git + location: https://github.com/haskell/cabal + tag: f34aba976a60940295f41b6649674e9568893894 + ``` + + ``` + $ cabal build + Error parsing project file cabal.project:3: + 'source-repository-package' is a stanza, not a field. Remove the trailing ':' to parse a stanza. + ``` + + Previously, the warning message was easily ignored and somewhat misleading, as + the difference between a stanza and a field is not immediately obvious to + Haskellers used to config languages like JSON and YAML (which don't distinguish + between fields which have string or list values and stanzas which have nested + fields): + + ``` + Warning: cabal.project: Unrecognized field + 'source-repository-package' on line 3 + ``` + +- Warn on `cabal format` [#10549](https://github.com/haskell/cabal/pull/10549) + + - Despite its name, `cabal format` is not a proper formatter for cabal files. By chance users have sometimes found the command even though it is not mentioned in the help text, and they used it to format cabal files. This has some downsides like comments are stripped away or common stanzas are inlined, the command is more like a dump of the resolved package description. There are future plans ([#7544](https://github.com/haskell/cabal/issues/7544)) to make it an actual formatter so, rather than going through a deprecation cycle, we decided to keep this command for future use and in the meantime just warn the user about the fact that it is not a proper formatter. + +- Quieter Git output [#10587](https://github.com/haskell/cabal/pull/10587) + + When `cabal` clones a Git repo for a `source-repository-package` listed in a + `cabal.project`, it will run various commands to check out the correct + revision, initialize submodules if they're present, and so on. + + Now, `cabal` will pass `--quiet` to Git in more cases to help prevent + cluttering command-line output. + +- Report trailing spaces in project import URIs [#10622](https://github.com/haskell/cabal/issues/10622) [#10629](https://github.com/haskell/cabal/pull/10629) + + > A string is a valid URL potentially surrounded by spaces if, after stripping + > leading and trailing whitespace from it, it is a valid URL." + > SOURCE: [W3C/HTML5/URLs](https://www.w3.org/TR/2010/WD-html5-20100624/urls.html) + + Fixes a problem of mistaking a URI for a file path when it has trailing spaces + and warn about such trailing spaces. + +- Fix Haddock CSS handling in multi-package projects [#10636](https://github.com/haskell/cabal/issues/10636) [#10637](https://github.com/haskell/cabal/pull/10637) + + When `--css=` flag is provided to `cabal haddock-project`: + + - the Haddock index is now properly styled by the provided CSS file + - each package in the project now has their docs properly styled by the provided CSS file + +- Show source of project parse error or warning [#10635](https://github.com/haskell/cabal/issues/10635) [#10644](https://github.com/haskell/cabal/pull/10644) + + Improves warning and error messages shown when parsing project files and their + imports. + +- Improve "Cannot read .cabal file inside ..." errors [#10647](https://github.com/haskell/cabal/pull/10647) + + The error message printed when Cabal fails to read a `.cabal` file inside an + index (like the Hackage index) has been greatly improved. Additionally, + warnings in `.cabal` files in indexes are printed instead of being silently + discarded. + +- Add support for Windows AArch64 [#10705](https://github.com/haskell/cabal/pull/10705) + + Adds to preprocessor branches the option to support `aarch64_HOST_ARCH` platform on Windows AArch64 target. + `ccall` convention is used at AArch64, same as for `x86_64_HOST_ARCH`. Introduce `zIsAArch64` to make paths generation support Windows AArch64 target. + +- Merge `flags` stanzas in cabal.project files instead of taking the last [#10767](https://github.com/haskell/cabal/issues/10767) [#10805](https://github.com/haskell/cabal/pull/10805) + + Now + + ``` + flags: -foo + flags: -bar + ``` + + is equivalent to `flags: -foo -bar` while before it was equivalent to `flags: -bar` + (only the latest stanza was taken into account, the other ones were silently + ignored). + +- Add checks for Windows reserved filenames in module paths [#10295](https://github.com/haskell/cabal/issues/10295) [#10816](https://github.com/haskell/cabal/pull/10816) + + On Windows, certain filenames are reserved by the operating system such as + "aux", "con", "prn", "nul", etc. When these names appear in module paths they + can cause build failures on Windows systems. + + `cabal check` now properly warns about module paths that contain Windows reserved + filenames, not just filepaths which contain these reserved tokens. These warnings + are controlled by the existing `invalid-win-path` category. + + For example, module paths like: + - `Exe.Aux.Test` + - `Test.Aux.Module` + - `Bench.Aux.Helpers` + + will now trigger appropriate warnings during `cabal check`. + +- Fix `gen-bounds` command to work in multi-package projects [#7504](https://github.com/haskell/cabal/issues/7504) [#10840](https://github.com/haskell/cabal/pull/10840) + + The command has been reimplemented to use the cabal.project infrastructure (similar + to other v2 commands), allowing it to be aware of all packages defined in the cabal.project + file, regardless of which directory it's executed from. + + ``` + $ cat cabal.project + packages: package-a/ + package-b/ + + $ cd package-b/ + $ cabal gen-bounds + Configuration is affected by the following files: + - cabal.project + Resolving dependencies... + + The following packages need bounds and here is a suggested starting point... + For component package-b:lib:package-b: + package-a >= 0.1.0 && < 0.2, + ``` + +- Fix multi-repl when using reexported-modules with renaming for GHC >= 9.12 [#10181](https://github.com/haskell/cabal/issues/10181) [#10880](https://github.com/haskell/cabal/pull/10880) + + Since GHC 9.12, the `-reexported-module` flag has supported module renaming. Therefore + we now use that functionality when starting the multi-repl if it is needed. A new + error message is added to catch the case where you attempt to load a project which + uses this complicated export form but are using < 9.12. + +- Remove dead build-tool `greencard` [#10908](https://github.com/haskell/cabal/pull/10908) + + Remove knowledge of the build-tool `greencard` (non-existing since GHC 7.10) + and the connect of the `.gc` extension to this build-tool. + +- Remove knowledge about `hmake` and `haskell-suite` [#10912](https://github.com/haskell/cabal/pull/10912) + + The `hmake` tool has long been abandoned, and the `haskell-suite` compiler did not emerge. + Knowledge about these tools has been removed from `Cabal`. + +- Print out the "Created semaphore" message only in verbose mode [#10885](https://github.com/haskell/cabal/issues/10885) [#10936](https://github.com/haskell/cabal/pull/10936) + +- Print out project files only when run below the project root (or verbose) [#10885](https://github.com/haskell/cabal/issues/10885) [#10940](https://github.com/haskell/cabal/pull/10940) + + After [#10507](https://github.com/haskell/cabal/pull/10507) released in + cabal-install-3.14, cabal prints out what project files are in use on every run. + + This looked too noisy for some users + ([#10885]((https://github.com/haskell/cabal/issues/10885))). In this patch, + we implement a more nuanced strategy: print out this info only when cabal is + run below the root project directory. + As before, you can get this information unconditionally if run in the verbose mode. + + Bonus: we now also print the project root directory along with the file names. + Before, we only printed, say, `cabal.project`, but it wasn't clear where this file is + coming from (can be anywhere up the directory tree). + The change tries to avoid confusion when cabal picks up stray project files in + ancestor directories, see discussion in + [#7930](https://github.com/haskell/cabal/issues/7930). + +- Make `lukko` flag automatic and off by default [#10724](https://github.com/haskell/cabal/issues/10724) [#11003](https://github.com/haskell/cabal/pull/11003) + + Make `lukko` flag automatic and off by default, using file locking facilities from `base:GHC.IO.Handle.Lock` and not from the `lukko` package. The change is not expected to affect anyone detrimentally, but one can set the flag on in their configuration to restore the previous behaviour. + +- `cabal init` generates explicit export lists for Main [#9889](https://github.com/haskell/cabal/issues/9889) [#9890](https://github.com/haskell/cabal/pull/9890) + + - Lack of explicit export list can degrade performance. The `Main` module in particular should always have an explicit export list that contains just the main function. Then, the compiler can do more aggressive optimizations on all the other non-exported functions. + +- Fix PATH changes not triggering REPL reconfiguration [#2015](https://github.com/haskell/cabal/issues/2015) [#10817](https://github.com/haskell/cabal/pull/10817) + +- Report trailing spaces in project import URIs [#10622](https://github.com/haskell/cabal/issues/10622) [#10629](https://github.com/haskell/cabal/pull/10629) + + > A string is a valid URL potentially surrounded by spaces if, after stripping + > leading and trailing whitespace from it, it is a valid URL." + + Source: [W3C/HTML5/URLs](https://www.w3.org/TR/2010/WD-html5-20100624/urls.html) + + Fixes a problem of mistaking a URI for a file path when it has trailing spaces + and warn about such trailing spaces. + +- Configuration messages without duplicates [#10645](https://github.com/haskell/cabal/issues/10645) [#10646](https://github.com/haskell/cabal/pull/10646) + + The "using configuration from" message no longer has duplicates on Windows when + a `cabal.project` uses forward slashes for its imports but the message reports + the same import again with backslashes. + +### Unresolved + +- curl error with `cabal upload -d` [#10920](https://github.com/haskell/cabal/issues/10920) + + Uploading documentation to Hackage can result in a + `chunk hex-length char not a hex digit` error. + +- New git invocation changes the behaviour of source-repository-package [#10605](https://github.com/haskell/cabal/issues/10605) + + Invoking `cabal build` with `source-repository-package` leads to + errors when the `tag` is specified with abbreviated commit hashes. + +- `extra-libraries` is overwritten by configure script [#10961](https://github.com/haskell/cabal/issues/10961) + + If you have two `extra-libraries`, one in the `.cabal` file and the + second in the configure script, the latter will be appended on the + right. + + If the correct library is picked in the configure script, it is advisable + to remove `extra-libraries` from the `.cabal` file. From a49146a4a4f9abe0abf9888fcd3d775e148fe96d Mon Sep 17 00:00:00 2001 From: Artem Pelenitsyn Date: Fri, 11 Jul 2025 09:41:45 -0400 Subject: [PATCH 16/39] cabal update: don't show comment with builddir in the default config When calling `cabal update` with an empty state (empty CABAL_DIR or new machine), cabal writes out a default config file with many fields commented out to show their defaults. This suggests that the user can uncomment the field and override the default. This is wrong for fields that aren't allowed in the config file like `builddir`. This patch avoids generating a comment with `builddir` in the default config. Fix #11050. --- cabal-install/src/Distribution/Client/Config.hs | 13 ++++++++++--- cabal-install/tests/IntegrationTests2.hs | 1 - changelog.d/pr-11067 | 12 ++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 changelog.d/pr-11067 diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index 9254b2fae9b..77348dbc638 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -1782,9 +1782,16 @@ showConfigWithComments comment vals = (fmap (field . savedConfigureFlags) mcomment) ((field . savedConfigureFlags) vals) - -- skip fields based on field name. currently only skips "remote-repo", - -- because that is rendered as a section. (see 'ppRemoteRepoSection'.) - skipSomeFields = filter ((/= "remote-repo") . fieldName) + -- Skip fields based on field name. + skipSomeFields = + filter + ( ( `notElem` + [ "remote-repo" -- rendered as a section (see 'ppRemoteRepoSection') + , "builddir" -- no effect in config file (see Note [reading project configuration]) + ] + ) + . fieldName + ) -- | Fields for the 'install-dirs' sections. installDirsFields :: [FieldDescr (InstallDirs (Flag PathTemplate))] diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs index 69c4134f142..95f04b81a4c 100644 --- a/cabal-install/tests/IntegrationTests2.hs +++ b/cabal-install/tests/IntegrationTests2.hs @@ -2675,7 +2675,6 @@ testConfigOptionComments = do "-- username" `assertHasCommentLine` "username" "-- password" `assertHasCommentLine` "password" "-- password-command" `assertHasCommentLine` "password-command" - "-- builddir" `assertHasCommentLine` "builddir" " -- hoogle" `assertHasCommentLine` "hoogle" " -- html" `assertHasCommentLine` "html" diff --git a/changelog.d/pr-11067 b/changelog.d/pr-11067 new file mode 100644 index 00000000000..ac735f98f98 --- /dev/null +++ b/changelog.d/pr-11067 @@ -0,0 +1,12 @@ +synopsis: `cabal update`: create default config without comment about `builddir` +packages: cabal-install +prs: #11067 +issues: #11050 +description: { +When calling `cabal update` with an empty state (empty `CABAL_DIR` or new +machine), cabal writes out a default config file with many fields +commented out to show their defaults. This suggests that the user can +uncomment the field and override the default. This is wrong for fields +that aren't allowed in the config file like `builddir`. This patch +avoids generating a comment with `builddir` in the default config. +} From 500505cde66c3b0bb0f6adf318b9820da4d74d66 Mon Sep 17 00:00:00 2001 From: Sergey Vinokurov Date: Wed, 18 Jun 2025 09:46:49 +0100 Subject: [PATCH 17/39] Add test --- .../PreProcess/TooManyCommandLineArgs/Foo.hsc | 99 +++++++++++++++++++ .../PreProcess/TooManyCommandLineArgs/Main.hs | 6 ++ .../TooManyCommandLineArgs/cabal.project | 34 +++++++ .../deps/my-dep-dep01/FooDepDep01.hs | 4 + .../include/include_FooDepDep01.h | 1 + .../my-dep-dep01/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep01/my-dep-dep01.cabal | 10 ++ .../deps/my-dep-dep02/FooDepDep02.hs | 4 + .../include/include_FooDepDep02.h | 1 + .../my-dep-dep02/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep02/my-dep-dep02.cabal | 10 ++ .../deps/my-dep-dep03/FooDepDep03.hs | 4 + .../include/include_FooDepDep03.h | 1 + .../my-dep-dep03/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep03/my-dep-dep03.cabal | 10 ++ .../deps/my-dep-dep04/FooDepDep04.hs | 4 + .../include/include_FooDepDep04.h | 1 + .../my-dep-dep04/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep04/my-dep-dep04.cabal | 10 ++ .../deps/my-dep-dep05/FooDepDep05.hs | 4 + .../include/include_FooDepDep05.h | 1 + .../my-dep-dep05/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep05/my-dep-dep05.cabal | 10 ++ .../deps/my-dep-dep06/FooDepDep06.hs | 4 + .../include/include_FooDepDep06.h | 1 + .../my-dep-dep06/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep06/my-dep-dep06.cabal | 10 ++ .../deps/my-dep-dep07/FooDepDep07.hs | 4 + .../include/include_FooDepDep07.h | 1 + .../my-dep-dep07/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep07/my-dep-dep07.cabal | 10 ++ .../deps/my-dep-dep08/FooDepDep08.hs | 4 + .../include/include_FooDepDep08.h | 1 + .../my-dep-dep08/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep08/my-dep-dep08.cabal | 10 ++ .../deps/my-dep-dep09/FooDepDep09.hs | 4 + .../include/include_FooDepDep09.h | 1 + .../my-dep-dep09/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep09/my-dep-dep09.cabal | 10 ++ .../deps/my-dep-dep10/FooDepDep10.hs | 4 + .../include/include_FooDepDep10.h | 1 + .../my-dep-dep10/lib/libdummy-unused-lib.a | 0 .../deps/my-dep-dep10/my-dep-dep10.cabal | 10 ++ .../deps/my-dep01/FooDep01.hs | 27 +++++ .../deps/my-dep01/include/include_FooDep01.h | 1 + .../deps/my-dep01/lib/libdummy-unused-lib.a | 0 .../deps/my-dep01/my-dep01.cabal | 21 ++++ .../deps/my-dep02/FooDep02.hs | 27 +++++ .../deps/my-dep02/include/include_FooDep02.h | 1 + .../deps/my-dep02/lib/libdummy-unused-lib.a | 0 .../deps/my-dep02/my-dep02.cabal | 21 ++++ .../deps/my-dep03/FooDep03.hs | 27 +++++ .../deps/my-dep03/include/include_FooDep03.h | 1 + .../deps/my-dep03/lib/libdummy-unused-lib.a | 0 .../deps/my-dep03/my-dep03.cabal | 21 ++++ .../deps/my-dep04/FooDep04.hs | 27 +++++ .../deps/my-dep04/include/include_FooDep04.h | 1 + .../deps/my-dep04/lib/libdummy-unused-lib.a | 0 .../deps/my-dep04/my-dep04.cabal | 21 ++++ .../deps/my-dep05/FooDep05.hs | 27 +++++ .../deps/my-dep05/include/include_FooDep05.h | 1 + .../deps/my-dep05/lib/libdummy-unused-lib.a | 0 .../deps/my-dep05/my-dep05.cabal | 21 ++++ .../deps/my-dep06/FooDep06.hs | 27 +++++ .../deps/my-dep06/include/include_FooDep06.h | 1 + .../deps/my-dep06/lib/libdummy-unused-lib.a | 0 .../deps/my-dep06/my-dep06.cabal | 21 ++++ .../deps/my-dep07/FooDep07.hs | 27 +++++ .../deps/my-dep07/include/include_FooDep07.h | 1 + .../deps/my-dep07/lib/libdummy-unused-lib.a | 0 .../deps/my-dep07/my-dep07.cabal | 21 ++++ .../deps/my-dep08/FooDep08.hs | 27 +++++ .../deps/my-dep08/include/include_FooDep08.h | 1 + .../deps/my-dep08/lib/libdummy-unused-lib.a | 0 .../deps/my-dep08/my-dep08.cabal | 21 ++++ .../deps/my-dep09/FooDep09.hs | 27 +++++ .../deps/my-dep09/include/include_FooDep09.h | 1 + .../deps/my-dep09/lib/libdummy-unused-lib.a | 0 .../deps/my-dep09/my-dep09.cabal | 21 ++++ .../deps/my-dep10/FooDep10.hs | 27 +++++ .../deps/my-dep10/include/include_FooDep10.h | 1 + .../deps/my-dep10/lib/libdummy-unused-lib.a | 0 .../deps/my-dep10/my-dep10.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my01/Foo01.hs | 27 +++++ .../deps/my01/include/include_Foo01.h | 1 + .../deps/my01/lib/libdummy-unused-lib.a | 0 .../deps/my01/my01.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my02/Foo02.hs | 27 +++++ .../deps/my02/include/include_Foo02.h | 1 + .../deps/my02/lib/libdummy-unused-lib.a | 0 .../deps/my02/my02.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my03/Foo03.hs | 27 +++++ .../deps/my03/include/include_Foo03.h | 1 + .../deps/my03/lib/libdummy-unused-lib.a | 0 .../deps/my03/my03.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my04/Foo04.hs | 27 +++++ .../deps/my04/include/include_Foo04.h | 1 + .../deps/my04/lib/libdummy-unused-lib.a | 0 .../deps/my04/my04.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my05/Foo05.hs | 27 +++++ .../deps/my05/include/include_Foo05.h | 1 + .../deps/my05/lib/libdummy-unused-lib.a | 0 .../deps/my05/my05.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my06/Foo06.hs | 27 +++++ .../deps/my06/include/include_Foo06.h | 1 + .../deps/my06/lib/libdummy-unused-lib.a | 0 .../deps/my06/my06.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my07/Foo07.hs | 27 +++++ .../deps/my07/include/include_Foo07.h | 1 + .../deps/my07/lib/libdummy-unused-lib.a | 0 .../deps/my07/my07.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my08/Foo08.hs | 27 +++++ .../deps/my08/include/include_Foo08.h | 1 + .../deps/my08/lib/libdummy-unused-lib.a | 0 .../deps/my08/my08.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my09/Foo09.hs | 27 +++++ .../deps/my09/include/include_Foo09.h | 1 + .../deps/my09/lib/libdummy-unused-lib.a | 0 .../deps/my09/my09.cabal | 21 ++++ .../TooManyCommandLineArgs/deps/my10/Foo10.hs | 27 +++++ .../deps/my10/include/include_Foo10.h | 1 + .../deps/my10/lib/libdummy-unused-lib.a | 0 .../deps/my10/my10.cabal | 21 ++++ .../TooManyCommandLineArgs/my-toplevel.cabal | 33 +++++++ .../TooManyCommandLineArgs/setup.out | 2 + .../TooManyCommandLineArgs/setup.test.hs | 96 ++++++++++++++++++ 126 files changed, 1400 insertions(+) create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Foo.hsc create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Main.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/cabal.project create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/FooDepDep01.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/include/include_FooDepDep01.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/my-dep-dep01.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/FooDepDep02.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/include/include_FooDepDep02.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/my-dep-dep02.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/FooDepDep03.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/include/include_FooDepDep03.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/my-dep-dep03.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/FooDepDep04.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/include/include_FooDepDep04.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/my-dep-dep04.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/FooDepDep05.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/include/include_FooDepDep05.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/my-dep-dep05.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/FooDepDep06.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/include/include_FooDepDep06.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/my-dep-dep06.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/FooDepDep07.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/include/include_FooDepDep07.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/my-dep-dep07.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/FooDepDep08.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/include/include_FooDepDep08.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/my-dep-dep08.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/FooDepDep09.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/include/include_FooDepDep09.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/my-dep-dep09.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/FooDepDep10.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/include/include_FooDepDep10.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/my-dep-dep10.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/FooDep01.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/include/include_FooDep01.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/my-dep01.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/FooDep02.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/include/include_FooDep02.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/my-dep02.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/FooDep03.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/include/include_FooDep03.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/my-dep03.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/FooDep04.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/include/include_FooDep04.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/my-dep04.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/FooDep05.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/include/include_FooDep05.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/my-dep05.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/FooDep06.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/include/include_FooDep06.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/my-dep06.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/FooDep07.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/include/include_FooDep07.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/my-dep07.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/FooDep08.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/include/include_FooDep08.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/my-dep08.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/FooDep09.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/include/include_FooDep09.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/my-dep09.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/FooDep10.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/include/include_FooDep10.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/my-dep10.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/Foo01.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/include/include_Foo01.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/my01.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/Foo02.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/include/include_Foo02.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/my02.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/Foo03.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/include/include_Foo03.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/my03.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/Foo04.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/include/include_Foo04.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/my04.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/Foo05.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/include/include_Foo05.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/my05.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/Foo06.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/include/include_Foo06.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/my06.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/Foo07.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/include/include_Foo07.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/my07.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/Foo08.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/include/include_Foo08.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/my08.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/Foo09.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/include/include_Foo09.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/my09.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/Foo10.hs create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/include/include_Foo10.h create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/lib/libdummy-unused-lib.a create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/my10.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/my-toplevel.cabal create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.out create mode 100644 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Foo.hsc b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Foo.hsc new file mode 100644 index 00000000000..d1e0418a5e8 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Foo.hsc @@ -0,0 +1,99 @@ +-- | +-- Module: Foo +-- Copyright: (c) Sergey Vinokurov 2025 +-- License: Apache-2.0 (see LICENSE) +-- Maintainer: serg.foo@gmail.com + +{-# LANGUAGE CPP #-} + +module Foo (foo) where + +import Foo01 +import Foo02 +import Foo03 +import Foo04 +import Foo05 +import Foo06 +import Foo07 +import Foo08 +import Foo09 +import Foo10 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +foo :: Int +foo = sum + [ #{const TEST_OPTION} + , foo01 + , foo02 + , foo03 + , foo04 + , foo05 + , foo06 + , foo07 + , foo08 + , foo09 + , foo10 + + , #{const DEF_foo01} + , #{const DEF_foo02} + , #{const DEF_foo03} + , #{const DEF_foo04} + , #{const DEF_foo05} + , #{const DEF_foo06} + , #{const DEF_foo07} + , #{const DEF_foo08} + , #{const DEF_foo09} + , #{const DEF_foo10} + + , #{const DEF_fooDep01} + , #{const DEF_fooDep02} + , #{const DEF_fooDep03} + , #{const DEF_fooDep04} + , #{const DEF_fooDep05} + , #{const DEF_fooDep06} + , #{const DEF_fooDep07} + , #{const DEF_fooDep08} + , #{const DEF_fooDep09} + , #{const DEF_fooDep10} + + , #{const DEF_fooDepDep01} + , #{const DEF_fooDepDep02} + , #{const DEF_fooDepDep03} + , #{const DEF_fooDepDep04} + , #{const DEF_fooDepDep05} + , #{const DEF_fooDepDep06} + , #{const DEF_fooDepDep07} + , #{const DEF_fooDepDep08} + , #{const DEF_fooDepDep09} + , #{const DEF_fooDepDep10} + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Main.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Main.hs new file mode 100644 index 00000000000..fb4e301094d --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/Main.hs @@ -0,0 +1,6 @@ +module Main (main) where + +import Foo + +main :: IO () +main = putStrLn $ "Result = " ++ show foo diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/cabal.project b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/cabal.project new file mode 100644 index 00000000000..004271c651b --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/cabal.project @@ -0,0 +1,34 @@ +packages: + my-toplevel.cabal + deps/my01/my01.cabal + deps/my02/my02.cabal + deps/my03/my03.cabal + deps/my04/my04.cabal + deps/my05/my05.cabal + deps/my06/my06.cabal + deps/my07/my07.cabal + deps/my08/my08.cabal + deps/my09/my09.cabal + deps/my10/my10.cabal + + deps/my-dep01/my-dep01.cabal + deps/my-dep02/my-dep02.cabal + deps/my-dep03/my-dep03.cabal + deps/my-dep04/my-dep04.cabal + deps/my-dep05/my-dep05.cabal + deps/my-dep06/my-dep06.cabal + deps/my-dep07/my-dep07.cabal + deps/my-dep08/my-dep08.cabal + deps/my-dep09/my-dep09.cabal + deps/my-dep10/my-dep10.cabal + + deps/my-dep-dep01/my-dep-dep01.cabal + deps/my-dep-dep02/my-dep-dep02.cabal + deps/my-dep-dep03/my-dep-dep03.cabal + deps/my-dep-dep04/my-dep-dep04.cabal + deps/my-dep-dep05/my-dep-dep05.cabal + deps/my-dep-dep06/my-dep-dep06.cabal + deps/my-dep-dep07/my-dep-dep07.cabal + deps/my-dep-dep08/my-dep-dep08.cabal + deps/my-dep-dep09/my-dep-dep09.cabal + deps/my-dep-dep10/my-dep-dep10.cabal diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/FooDepDep01.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/FooDepDep01.hs new file mode 100644 index 00000000000..9e6837fb409 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/FooDepDep01.hs @@ -0,0 +1,4 @@ +module FooDepDep01 (fooDepDep01) where + +fooDepDep01 :: Int +fooDepDep01 = 01 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/include/include_FooDepDep01.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/include/include_FooDepDep01.h new file mode 100644 index 00000000000..1e2a0c2d3fd --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/include/include_FooDepDep01.h @@ -0,0 +1 @@ +#define DEF_fooDepDep01 1 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/my-dep-dep01.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/my-dep-dep01.cabal new file mode 100644 index 00000000000..075c6dadcf6 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep01/my-dep-dep01.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep01 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep01 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/FooDepDep02.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/FooDepDep02.hs new file mode 100644 index 00000000000..564798ff0e7 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/FooDepDep02.hs @@ -0,0 +1,4 @@ +module FooDepDep02 (fooDepDep02) where + +fooDepDep02 :: Int +fooDepDep02 = 02 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/include/include_FooDepDep02.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/include/include_FooDepDep02.h new file mode 100644 index 00000000000..a05f788abf5 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/include/include_FooDepDep02.h @@ -0,0 +1 @@ +#define DEF_fooDepDep02 2 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/my-dep-dep02.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/my-dep-dep02.cabal new file mode 100644 index 00000000000..a21e8fa002f --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep02/my-dep-dep02.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep02 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep02 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/FooDepDep03.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/FooDepDep03.hs new file mode 100644 index 00000000000..90d7ab99948 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/FooDepDep03.hs @@ -0,0 +1,4 @@ +module FooDepDep03 (fooDepDep03) where + +fooDepDep03 :: Int +fooDepDep03 = 03 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/include/include_FooDepDep03.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/include/include_FooDepDep03.h new file mode 100644 index 00000000000..716d517d278 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/include/include_FooDepDep03.h @@ -0,0 +1 @@ +#define DEF_fooDepDep03 3 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/my-dep-dep03.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/my-dep-dep03.cabal new file mode 100644 index 00000000000..84b24170e2a --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep03/my-dep-dep03.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep03 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep03 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/FooDepDep04.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/FooDepDep04.hs new file mode 100644 index 00000000000..9101849a9c6 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/FooDepDep04.hs @@ -0,0 +1,4 @@ +module FooDepDep04 (fooDepDep04) where + +fooDepDep04 :: Int +fooDepDep04 = 04 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/include/include_FooDepDep04.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/include/include_FooDepDep04.h new file mode 100644 index 00000000000..79fd9374863 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/include/include_FooDepDep04.h @@ -0,0 +1 @@ +#define DEF_fooDepDep04 4 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/my-dep-dep04.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/my-dep-dep04.cabal new file mode 100644 index 00000000000..9617302f33b --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep04/my-dep-dep04.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep04 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep04 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/FooDepDep05.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/FooDepDep05.hs new file mode 100644 index 00000000000..1e0a4cf206f --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/FooDepDep05.hs @@ -0,0 +1,4 @@ +module FooDepDep05 (fooDepDep05) where + +fooDepDep05 :: Int +fooDepDep05 = 05 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/include/include_FooDepDep05.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/include/include_FooDepDep05.h new file mode 100644 index 00000000000..2f305c8d829 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/include/include_FooDepDep05.h @@ -0,0 +1 @@ +#define DEF_fooDepDep05 5 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/my-dep-dep05.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/my-dep-dep05.cabal new file mode 100644 index 00000000000..18a94b86684 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep05/my-dep-dep05.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep05 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep05 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/FooDepDep06.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/FooDepDep06.hs new file mode 100644 index 00000000000..50b2b0d3f0f --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/FooDepDep06.hs @@ -0,0 +1,4 @@ +module FooDepDep06 (fooDepDep06) where + +fooDepDep06 :: Int +fooDepDep06 = 06 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/include/include_FooDepDep06.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/include/include_FooDepDep06.h new file mode 100644 index 00000000000..a005ad17115 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/include/include_FooDepDep06.h @@ -0,0 +1 @@ +#define DEF_fooDepDep06 6 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/my-dep-dep06.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/my-dep-dep06.cabal new file mode 100644 index 00000000000..492161e6751 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep06/my-dep-dep06.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep06 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep06 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/FooDepDep07.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/FooDepDep07.hs new file mode 100644 index 00000000000..4ee00b0d8e9 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/FooDepDep07.hs @@ -0,0 +1,4 @@ +module FooDepDep07 (fooDepDep07) where + +fooDepDep07 :: Int +fooDepDep07 = 07 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/include/include_FooDepDep07.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/include/include_FooDepDep07.h new file mode 100644 index 00000000000..cd4d9c62a90 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/include/include_FooDepDep07.h @@ -0,0 +1 @@ +#define DEF_fooDepDep07 7 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/my-dep-dep07.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/my-dep-dep07.cabal new file mode 100644 index 00000000000..d5e129e224d --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep07/my-dep-dep07.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep07 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep07 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/FooDepDep08.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/FooDepDep08.hs new file mode 100644 index 00000000000..bff75a8857e --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/FooDepDep08.hs @@ -0,0 +1,4 @@ +module FooDepDep08 (fooDepDep08) where + +fooDepDep08 :: Int +fooDepDep08 = 08 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/include/include_FooDepDep08.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/include/include_FooDepDep08.h new file mode 100644 index 00000000000..1a81f6ecc2c --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/include/include_FooDepDep08.h @@ -0,0 +1 @@ +#define DEF_fooDepDep08 8 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/my-dep-dep08.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/my-dep-dep08.cabal new file mode 100644 index 00000000000..5070df765be --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep08/my-dep-dep08.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep08 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep08 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/FooDepDep09.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/FooDepDep09.hs new file mode 100644 index 00000000000..df01fa36f21 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/FooDepDep09.hs @@ -0,0 +1,4 @@ +module FooDepDep09 (fooDepDep09) where + +fooDepDep09 :: Int +fooDepDep09 = 09 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/include/include_FooDepDep09.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/include/include_FooDepDep09.h new file mode 100644 index 00000000000..7e009d23532 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/include/include_FooDepDep09.h @@ -0,0 +1 @@ +#define DEF_fooDepDep09 9 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/my-dep-dep09.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/my-dep-dep09.cabal new file mode 100644 index 00000000000..e8ca1a6f25b --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep09/my-dep-dep09.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep09 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep09 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/FooDepDep10.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/FooDepDep10.hs new file mode 100644 index 00000000000..f2230e5ad60 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/FooDepDep10.hs @@ -0,0 +1,4 @@ +module FooDepDep10 (fooDepDep10) where + +fooDepDep10 :: Int +fooDepDep10 = 10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/include/include_FooDepDep10.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/include/include_FooDepDep10.h new file mode 100644 index 00000000000..94ec0123768 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/include/include_FooDepDep10.h @@ -0,0 +1 @@ +#define DEF_fooDepDep10 10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/my-dep-dep10.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/my-dep-dep10.cabal new file mode 100644 index 00000000000..fc873543421 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep-dep10/my-dep-dep10.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: my-dep-dep10 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDepDep10 + default-language: Haskell2010 + build-depends: base diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/FooDep01.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/FooDep01.hs new file mode 100644 index 00000000000..4f4e5022a05 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/FooDep01.hs @@ -0,0 +1,27 @@ +module FooDep01 (fooDep01) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep01 :: Int +fooDep01 = sum + [ 01 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/include/include_FooDep01.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/include/include_FooDep01.h new file mode 100644 index 00000000000..a2beee766db --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/include/include_FooDep01.h @@ -0,0 +1 @@ +#define DEF_fooDep01 1 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/my-dep01.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/my-dep01.cabal new file mode 100644 index 00000000000..04f1112df0d --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep01/my-dep01.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep01 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep01 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/FooDep02.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/FooDep02.hs new file mode 100644 index 00000000000..d3768a947dd --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/FooDep02.hs @@ -0,0 +1,27 @@ +module FooDep02 (fooDep02) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep02 :: Int +fooDep02 = sum + [ 02 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/include/include_FooDep02.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/include/include_FooDep02.h new file mode 100644 index 00000000000..26bb3371de3 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/include/include_FooDep02.h @@ -0,0 +1 @@ +#define DEF_fooDep02 2 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/my-dep02.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/my-dep02.cabal new file mode 100644 index 00000000000..7c5050f6804 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep02/my-dep02.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep02 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep02 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/FooDep03.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/FooDep03.hs new file mode 100644 index 00000000000..0e05fcb1662 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/FooDep03.hs @@ -0,0 +1,27 @@ +module FooDep03 (fooDep03) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep03 :: Int +fooDep03 = sum + [ 03 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/include/include_FooDep03.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/include/include_FooDep03.h new file mode 100644 index 00000000000..400b14465ba --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/include/include_FooDep03.h @@ -0,0 +1 @@ +#define DEF_fooDep03 3 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/my-dep03.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/my-dep03.cabal new file mode 100644 index 00000000000..fca04e84901 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep03/my-dep03.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep03 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep03 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/FooDep04.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/FooDep04.hs new file mode 100644 index 00000000000..99ad7dd90ba --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/FooDep04.hs @@ -0,0 +1,27 @@ +module FooDep04 (fooDep04) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep04 :: Int +fooDep04 = sum + [ 04 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/include/include_FooDep04.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/include/include_FooDep04.h new file mode 100644 index 00000000000..705321a768a --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/include/include_FooDep04.h @@ -0,0 +1 @@ +#define DEF_fooDep04 4 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/my-dep04.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/my-dep04.cabal new file mode 100644 index 00000000000..0443c45392b --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep04/my-dep04.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep04 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep04 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/FooDep05.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/FooDep05.hs new file mode 100644 index 00000000000..ba763dc729f --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/FooDep05.hs @@ -0,0 +1,27 @@ +module FooDep05 (fooDep05) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep05 :: Int +fooDep05 = sum + [ 05 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/include/include_FooDep05.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/include/include_FooDep05.h new file mode 100644 index 00000000000..cb4ef9e3b90 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/include/include_FooDep05.h @@ -0,0 +1 @@ +#define DEF_fooDep05 5 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/my-dep05.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/my-dep05.cabal new file mode 100644 index 00000000000..13525f75522 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep05/my-dep05.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep05 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep05 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/FooDep06.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/FooDep06.hs new file mode 100644 index 00000000000..877571f3625 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/FooDep06.hs @@ -0,0 +1,27 @@ +module FooDep06 (fooDep06) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep06 :: Int +fooDep06 = sum + [ 06 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/include/include_FooDep06.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/include/include_FooDep06.h new file mode 100644 index 00000000000..121a0893a6d --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/include/include_FooDep06.h @@ -0,0 +1 @@ +#define DEF_fooDep06 6 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/my-dep06.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/my-dep06.cabal new file mode 100644 index 00000000000..8f4fc14e383 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep06/my-dep06.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep06 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep06 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/FooDep07.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/FooDep07.hs new file mode 100644 index 00000000000..c0102c376f0 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/FooDep07.hs @@ -0,0 +1,27 @@ +module FooDep07 (fooDep07) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep07 :: Int +fooDep07 = sum + [ 07 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/include/include_FooDep07.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/include/include_FooDep07.h new file mode 100644 index 00000000000..890388e96f4 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/include/include_FooDep07.h @@ -0,0 +1 @@ +#define DEF_fooDep07 7 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/my-dep07.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/my-dep07.cabal new file mode 100644 index 00000000000..26f852fd4eb --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep07/my-dep07.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep07 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep07 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/FooDep08.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/FooDep08.hs new file mode 100644 index 00000000000..a867fe7416f --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/FooDep08.hs @@ -0,0 +1,27 @@ +module FooDep08 (fooDep08) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep08 :: Int +fooDep08 = sum + [ 08 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/include/include_FooDep08.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/include/include_FooDep08.h new file mode 100644 index 00000000000..a7959662c0b --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/include/include_FooDep08.h @@ -0,0 +1 @@ +#define DEF_fooDep08 8 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/my-dep08.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/my-dep08.cabal new file mode 100644 index 00000000000..6f65812163d --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep08/my-dep08.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep08 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep08 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/FooDep09.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/FooDep09.hs new file mode 100644 index 00000000000..c2cd57e98b7 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/FooDep09.hs @@ -0,0 +1,27 @@ +module FooDep09 (fooDep09) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep09 :: Int +fooDep09 = sum + [ 09 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/include/include_FooDep09.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/include/include_FooDep09.h new file mode 100644 index 00000000000..aa38edd5b88 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/include/include_FooDep09.h @@ -0,0 +1 @@ +#define DEF_fooDep09 9 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/my-dep09.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/my-dep09.cabal new file mode 100644 index 00000000000..a2393c87226 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep09/my-dep09.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep09 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep09 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/FooDep10.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/FooDep10.hs new file mode 100644 index 00000000000..3cc38207580 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/FooDep10.hs @@ -0,0 +1,27 @@ +module FooDep10 (fooDep10) where + +import FooDepDep01 +import FooDepDep02 +import FooDepDep03 +import FooDepDep04 +import FooDepDep05 +import FooDepDep06 +import FooDepDep07 +import FooDepDep08 +import FooDepDep09 +import FooDepDep10 + +fooDep10 :: Int +fooDep10 = sum + [ 10 + , fooDepDep01 + , fooDepDep02 + , fooDepDep03 + , fooDepDep04 + , fooDepDep05 + , fooDepDep06 + , fooDepDep07 + , fooDepDep08 + , fooDepDep09 + , fooDepDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/include/include_FooDep10.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/include/include_FooDep10.h new file mode 100644 index 00000000000..2c4e63c62ed --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/include/include_FooDep10.h @@ -0,0 +1 @@ +#define DEF_fooDep10 10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/my-dep10.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/my-dep10.cabal new file mode 100644 index 00000000000..dfe7156d032 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my-dep10/my-dep10.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my-dep10 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: FooDep10 + default-language: Haskell2010 + build-depends: + , base + , my-dep-dep01 + , my-dep-dep02 + , my-dep-dep03 + , my-dep-dep04 + , my-dep-dep05 + , my-dep-dep06 + , my-dep-dep07 + , my-dep-dep08 + , my-dep-dep09 + , my-dep-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/Foo01.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/Foo01.hs new file mode 100644 index 00000000000..007e3df90b6 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/Foo01.hs @@ -0,0 +1,27 @@ +module Foo01 (foo01) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo01 :: Int +foo01 = sum + [ 01 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/include/include_Foo01.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/include/include_Foo01.h new file mode 100644 index 00000000000..6ce0ec798ac --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/include/include_Foo01.h @@ -0,0 +1 @@ +#define DEF_foo01 1 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/my01.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/my01.cabal new file mode 100644 index 00000000000..3aa3c5b1f12 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my01/my01.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my01 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo01 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/Foo02.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/Foo02.hs new file mode 100644 index 00000000000..daeb7e06689 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/Foo02.hs @@ -0,0 +1,27 @@ +module Foo02 (foo02) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo02 :: Int +foo02 = sum + [ 02 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/include/include_Foo02.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/include/include_Foo02.h new file mode 100644 index 00000000000..016f6956b67 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/include/include_Foo02.h @@ -0,0 +1 @@ +#define DEF_foo02 2 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/my02.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/my02.cabal new file mode 100644 index 00000000000..63dc442984e --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my02/my02.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my02 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo02 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/Foo03.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/Foo03.hs new file mode 100644 index 00000000000..bf9cc8c495b --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/Foo03.hs @@ -0,0 +1,27 @@ +module Foo03 (foo03) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo03 :: Int +foo03 = sum + [ 03 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/include/include_Foo03.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/include/include_Foo03.h new file mode 100644 index 00000000000..1ed4f567d95 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/include/include_Foo03.h @@ -0,0 +1 @@ +#define DEF_foo03 3 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/my03.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/my03.cabal new file mode 100644 index 00000000000..534c5e914fb --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my03/my03.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my03 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo03 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/Foo04.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/Foo04.hs new file mode 100644 index 00000000000..654a7e0c661 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/Foo04.hs @@ -0,0 +1,27 @@ +module Foo04 (foo04) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo04 :: Int +foo04 = sum + [ 04 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/include/include_Foo04.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/include/include_Foo04.h new file mode 100644 index 00000000000..f973f5e2e28 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/include/include_Foo04.h @@ -0,0 +1 @@ +#define DEF_foo04 4 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/my04.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/my04.cabal new file mode 100644 index 00000000000..b3e7cb025e9 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my04/my04.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my04 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo04 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/Foo05.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/Foo05.hs new file mode 100644 index 00000000000..6f011299199 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/Foo05.hs @@ -0,0 +1,27 @@ +module Foo05 (foo05) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo05 :: Int +foo05 = sum + [ 05 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/include/include_Foo05.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/include/include_Foo05.h new file mode 100644 index 00000000000..d1554139188 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/include/include_Foo05.h @@ -0,0 +1 @@ +#define DEF_foo05 5 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/my05.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/my05.cabal new file mode 100644 index 00000000000..4d55d71db4d --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my05/my05.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my05 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo05 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/Foo06.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/Foo06.hs new file mode 100644 index 00000000000..4ba7567a5b2 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/Foo06.hs @@ -0,0 +1,27 @@ +module Foo06 (foo06) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo06 :: Int +foo06 = sum + [ 06 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/include/include_Foo06.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/include/include_Foo06.h new file mode 100644 index 00000000000..b27035afce2 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/include/include_Foo06.h @@ -0,0 +1 @@ +#define DEF_foo06 6 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/my06.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/my06.cabal new file mode 100644 index 00000000000..7e4a1f00028 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my06/my06.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my06 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo06 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/Foo07.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/Foo07.hs new file mode 100644 index 00000000000..6701e0b2619 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/Foo07.hs @@ -0,0 +1,27 @@ +module Foo07 (foo07) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo07 :: Int +foo07 = sum + [ 07 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/include/include_Foo07.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/include/include_Foo07.h new file mode 100644 index 00000000000..64d8cc6a7b9 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/include/include_Foo07.h @@ -0,0 +1 @@ +#define DEF_foo07 7 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/my07.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/my07.cabal new file mode 100644 index 00000000000..8f77521878d --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my07/my07.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my07 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo07 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/Foo08.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/Foo08.hs new file mode 100644 index 00000000000..294c7db3384 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/Foo08.hs @@ -0,0 +1,27 @@ +module Foo08 (foo08) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo08 :: Int +foo08 = sum + [ 08 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/include/include_Foo08.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/include/include_Foo08.h new file mode 100644 index 00000000000..361e7a289a6 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/include/include_Foo08.h @@ -0,0 +1 @@ +#define DEF_foo08 8 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/my08.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/my08.cabal new file mode 100644 index 00000000000..2bf62465840 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my08/my08.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my08 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo08 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/Foo09.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/Foo09.hs new file mode 100644 index 00000000000..74b5d2ecb7f --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/Foo09.hs @@ -0,0 +1,27 @@ +module Foo09 (foo09) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo09 :: Int +foo09 = sum + [ 09 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/include/include_Foo09.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/include/include_Foo09.h new file mode 100644 index 00000000000..7d61d6507e2 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/include/include_Foo09.h @@ -0,0 +1 @@ +#define DEF_foo09 9 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/my09.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/my09.cabal new file mode 100644 index 00000000000..c47b759958e --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my09/my09.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my09 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo09 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/Foo10.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/Foo10.hs new file mode 100644 index 00000000000..896730ca8bd --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/Foo10.hs @@ -0,0 +1,27 @@ +module Foo10 (foo10) where + +import FooDep01 +import FooDep02 +import FooDep03 +import FooDep04 +import FooDep05 +import FooDep06 +import FooDep07 +import FooDep08 +import FooDep09 +import FooDep10 + +foo10 :: Int +foo10 = sum + [ 10 + , fooDep01 + , fooDep02 + , fooDep03 + , fooDep04 + , fooDep05 + , fooDep06 + , fooDep07 + , fooDep08 + , fooDep09 + , fooDep10 + ] diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/include/include_Foo10.h b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/include/include_Foo10.h new file mode 100644 index 00000000000..d8d5ad21d36 --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/include/include_Foo10.h @@ -0,0 +1 @@ +#define DEF_foo10 10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/lib/libdummy-unused-lib.a b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/lib/libdummy-unused-lib.a new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/my10.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/my10.cabal new file mode 100644 index 00000000000..fe9d8542e1b --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/deps/my10/my10.cabal @@ -0,0 +1,21 @@ +cabal-version: 3.0 +name: my10 +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + exposed-modules: Foo10 + default-language: Haskell2010 + build-depends: + , base + , my-dep01 + , my-dep02 + , my-dep03 + , my-dep04 + , my-dep05 + , my-dep06 + , my-dep07 + , my-dep08 + , my-dep09 + , my-dep10 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/my-toplevel.cabal b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/my-toplevel.cabal new file mode 100644 index 00000000000..62f16048d3e --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/my-toplevel.cabal @@ -0,0 +1,33 @@ +cabal-version: 3.6 +name: my-toplevel +description: Test that extra-include-dirs and extra-lib-dirs are not duplicated unnecessarily when e.g. gcc gets ultimately invoked +version: 0.1 +license: BSD-3-Clause +build-type: Simple + +library + default-language: + Haskell2010 + exposed-modules: + Foo + -- build-tool-depends: + -- , hsc2hs:hsc2hs + hsc2hs-options: + -DTEST_OPTION=42 + build-depends: + , base + , my01 + , my02 + , my03 + , my04 + , my05 + , my06 + , my07 + , my08 + , my09 + , my10 + +executable my-executable + main-is: Main.hs + build-depends: base, my-toplevel + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.out b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.out new file mode 100644 index 00000000000..485946cb20a --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.out @@ -0,0 +1,2 @@ +# my-toplevel my-executable +Result = 6312 diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs new file mode 100644 index 00000000000..0a89a85e6fa --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs @@ -0,0 +1,96 @@ + +import Test.Cabal.Prelude + +-- --extra-include-dirs have to be absolute paths so this cannot +-- be specified in the cabal.project nor cabal.config. +includeDirs :: [String] +includeDirs = + [ "deps/my01/include" + , "deps/my02/include" + , "deps/my03/include" + , "deps/my04/include" + , "deps/my05/include" + , "deps/my06/include" + , "deps/my07/include" + , "deps/my08/include" + , "deps/my09/include" + , "deps/my10/include" + , "deps/my-dep01/include" + , "deps/my-dep02/include" + , "deps/my-dep03/include" + , "deps/my-dep04/include" + , "deps/my-dep05/include" + , "deps/my-dep06/include" + , "deps/my-dep07/include" + , "deps/my-dep08/include" + , "deps/my-dep09/include" + , "deps/my-dep10/include" + , "deps/my-dep-dep01/include" + , "deps/my-dep-dep02/include" + , "deps/my-dep-dep03/include" + , "deps/my-dep-dep04/include" + , "deps/my-dep-dep05/include" + , "deps/my-dep-dep06/include" + , "deps/my-dep-dep07/include" + , "deps/my-dep-dep08/include" + , "deps/my-dep-dep09/include" + , "deps/my-dep-dep10/include" + ] + +-- --extra-lib-dirs have to be absolute paths so this cannot +-- be specified in the cabal.project nor cabal.config. +libDirs :: [String] +libDirs = + [ "deps/my01/lib" + , "deps/my02/lib" + , "deps/my03/lib" + , "deps/my04/lib" + , "deps/my05/lib" + , "deps/my06/lib" + , "deps/my07/lib" + , "deps/my08/lib" + , "deps/my09/lib" + , "deps/my10/lib" + , "deps/my-dep01/lib" + , "deps/my-dep02/lib" + , "deps/my-dep03/lib" + , "deps/my-dep04/lib" + , "deps/my-dep05/lib" + , "deps/my-dep06/lib" + , "deps/my-dep07/lib" + , "deps/my-dep08/lib" + , "deps/my-dep09/lib" + , "deps/my-dep10/lib" + , "deps/my-dep-dep01/lib" + , "deps/my-dep-dep02/lib" + , "deps/my-dep-dep03/lib" + , "deps/my-dep-dep04/lib" + , "deps/my-dep-dep05/lib" + , "deps/my-dep-dep06/lib" + , "deps/my-dep-dep07/lib" + , "deps/my-dep-dep08/lib" + , "deps/my-dep-dep09/lib" + , "deps/my-dep-dep10/lib" + ] + + +main :: IO () +main = cabalTest $ do + cwd <- testSourceDir <$> getTestEnv + + -- The intention is to pass these flags via cabal.project or cabal.config, + -- but these directories must be absolute paths so we cannot commit + -- necessary cabal.project in the repository and have to resort to + -- the command-line arguments instead. + let includeFlags = + concatMap (\x -> ["--extra-include-dirs", cwd x]) includeDirs + libFlags = + concatMap (\x -> ["--extra-lib-dirs", cwd x]) libDirs + + -- Parallel flag means output of this test is nondeterministic + _<- recordMode DoNotRecord $ + cabal "v2-build" $ ["-j", "my-toplevel:exe:my-executable"] ++ includeFlags ++ libFlags + r <- withPlan $ runPlanExe' "my-toplevel" "my-executable" [] + assertOutputContains + ("Result = " ++ show (42 + 55 + 10 * (55 + 10 * 55) + 3 * 55)) + r From 2317a6708d64cc131a36973f482ae68d317c4ae9 Mon Sep 17 00:00:00 2001 From: Sergey Vinokurov Date: Thu, 19 Jun 2025 23:02:00 +0100 Subject: [PATCH 18/39] Rearrange genPureArgs for hsc2hs without changing semantics --- Cabal/src/Distribution/Simple/PreProcess.hs | 178 ++++++++++---------- 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/Cabal/src/Distribution/Simple/PreProcess.hs b/Cabal/src/Distribution/Simple/PreProcess.hs index 697a0e490dd..7e146c9a391 100644 --- a/Cabal/src/Distribution/Simple/PreProcess.hs +++ b/Cabal/src/Distribution/Simple/PreProcess.hs @@ -518,44 +518,74 @@ ppHsc2hs bi lbi clbi = -- directly, or via a response file. genPureArgs :: Version -> ConfiguredProgram -> String -> String -> [String] genPureArgs hsc2hsVersion gccProg inFile outFile = - -- Additional gcc options - [ "--cflag=" ++ opt - | opt <- - programDefaultArgs gccProg - ++ programOverrideArgs gccProg - ] - ++ [ "--lflag=" ++ opt - | opt <- - programDefaultArgs gccProg - ++ programOverrideArgs gccProg - ] - -- OSX frameworks: - ++ [ what ++ "=-F" ++ opt - | isOSX - , opt <- nub (concatMap Installed.frameworkDirs pkgs) - , what <- ["--cflag", "--lflag"] - ] - ++ [ "--lflag=" ++ arg - | isOSX - , opt <- map getSymbolicPath (PD.frameworks bi) ++ concatMap Installed.frameworks pkgs - , arg <- ["-framework", opt] - ] - -- Note that on ELF systems, wherever we use -L, we must also use -R - -- because presumably that -L dir is not on the normal path for the - -- system's dynamic linker. This is needed because hsc2hs works by - -- compiling a C program and then running it. - - ++ ["--cflag=" ++ opt | opt <- platformDefines lbi] - -- Options from the current package: - ++ ["--cflag=-I" ++ u dir | dir <- PD.includeDirs bi] - ++ [ "--cflag=-I" ++ u (buildDir lbi unsafeCoerceSymbolicPath relDir) - | relDir <- mapMaybe symbolicPathRelative_maybe $ PD.includeDirs bi - ] - ++ [ "--cflag=" ++ opt - | opt <- - PD.ccOptions bi - ++ PD.cppOptions bi - -- hsc2hs uses the C ABI + cflags + ++ ldflags + ++ preccldFlags + ++ hsc2hsOptions bi + ++ postccldFlags + ++ ["-o", outFile, inFile] + where + ldflags = + map ("--lflag=" ++) $ + concat + [ programDefaultArgs gccProg ++ programOverrideArgs gccProg + , osxFrameworkDirs + , [ arg + | isOSX + , opt <- map getSymbolicPath (PD.frameworks bi) ++ concatMap Installed.frameworks pkgs + , arg <- ["-framework", opt] + ] + , -- Note that on ELF systems, wherever we use -L, we must also use -R + -- because presumably that -L dir is not on the normal path for the + -- system's dynamic linker. This is needed because hsc2hs works by + -- compiling a C program and then running it. + + -- Options from the current package: + [ "-L" ++ u opt + | opt <- + if withFullyStaticExe lbi + then PD.extraLibDirsStatic bi + else PD.extraLibDirs bi + ] + , [ "-Wl,-R," ++ u opt + | isELF + , opt <- + if withFullyStaticExe lbi + then PD.extraLibDirsStatic bi + else PD.extraLibDirs bi + ] + , ["-l" ++ opt | opt <- PD.extraLibs bi] + , PD.ldOptions bi + , -- Options from dependent packages + [ opt + | pkg <- pkgs + , opt <- + ["-L" ++ opt | opt <- Installed.libraryDirs pkg] + ++ [ "-Wl,-R," ++ opt | isELF, opt <- Installed.libraryDirs pkg + ] + ++ [ "-l" ++ opt + | opt <- + if withFullyStaticExe lbi + then Installed.extraLibrariesStatic pkg + else Installed.extraLibraries pkg + ] + ++ Installed.ldOptions pkg + ] + ] + + cflags = + map ("--cflag=" ++) $ + concat + [ programDefaultArgs gccProg ++ programOverrideArgs gccProg + , osxFrameworkDirs + , platformDefines lbi + , -- Options from the current package: + ["-I" ++ u dir | dir <- PD.includeDirs bi] + , [ "-I" ++ u (buildDir lbi unsafeCoerceSymbolicPath relDir) + | relDir <- mapMaybe symbolicPathRelative_maybe $ PD.includeDirs bi + ] + + , -- hsc2hs uses the C ABI -- We assume that there are only C sources -- and C++ functions are exported via a C -- interface and wrapped in a C source file. @@ -563,56 +593,30 @@ ppHsc2hs bi lbi clbi = -- because there will not be C++ sources. -- -- DO NOT add PD.cxxOptions unless this changes! - ] - ++ [ "--cflag=" ++ opt - | opt <- - [ "-I" ++ u (autogenComponentModulesDir lbi clbi) - , "-I" ++ u (autogenPackageModulesDir lbi) - , "-include" - , u $ autogenComponentModulesDir lbi clbi makeRelativePathEx cppHeaderName + PD.ccOptions bi ++ PD.cppOptions bi + + , + [ "-I" ++ u (autogenComponentModulesDir lbi clbi) + , "-I" ++ u (autogenPackageModulesDir lbi) + , "-include" + , u $ autogenComponentModulesDir lbi clbi makeRelativePathEx cppHeaderName + ] + + , -- Options from dependent packages + [ opt + | pkg <- pkgs + , opt <- + ["-I" ++ opt | opt <- Installed.includeDirs pkg] + ++ Installed.ccOptions pkg + ] ] - ] - ++ [ "--lflag=-L" ++ u opt - | opt <- - if withFullyStaticExe lbi - then PD.extraLibDirsStatic bi - else PD.extraLibDirs bi - ] - ++ [ "--lflag=-Wl,-R," ++ u opt - | isELF - , opt <- - if withFullyStaticExe lbi - then PD.extraLibDirsStatic bi - else PD.extraLibDirs bi - ] - ++ ["--lflag=-l" ++ opt | opt <- PD.extraLibs bi] - ++ ["--lflag=" ++ opt | opt <- PD.ldOptions bi] - -- Options from dependent packages - ++ [ "--cflag=" ++ opt - | pkg <- pkgs - , opt <- - ["-I" ++ opt | opt <- Installed.includeDirs pkg] - ++ Installed.ccOptions pkg - ] - ++ [ "--lflag=" ++ opt - | pkg <- pkgs - , opt <- - ["-L" ++ opt | opt <- Installed.libraryDirs pkg] - ++ [ "-Wl,-R," ++ opt | isELF, opt <- Installed.libraryDirs pkg - ] - ++ [ "-l" ++ opt - | opt <- - if withFullyStaticExe lbi - then Installed.extraLibrariesStatic pkg - else Installed.extraLibraries pkg - ] - ++ Installed.ldOptions pkg - ] - ++ preccldFlags - ++ hsc2hsOptions bi - ++ postccldFlags - ++ ["-o", outFile, inFile] - where + + osxFrameworkDirs = + [ "-F" ++ opt + | isOSX + , opt <- ordNub (concatMap Installed.frameworkDirs pkgs) + ] + -- hsc2hs flag parsing was wrong -- (see -- https://github.com/haskell/hsc2hs/issues/35) -- so we need to put -- --cc/--ld *after* hsc2hsOptions, From c917edcda11412aaf459308d4b62e518394a8626 Mon Sep 17 00:00:00 2001 From: Sergey Vinokurov Date: Thu, 19 Jun 2025 23:02:29 +0100 Subject: [PATCH 19/39] Deduplicate arguments that hsc2hs passes to gcc and ld MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If extra-lib-dirs and/or extra-include-dirs are specified via either command line or project files, they’re going to be added for each package in the dendency graph. With enough long enough directories the command-line length limits for gcc may be hit and calls to the C compiler made from hsc2hs will start failing. Ideally hsc2hs should be using repsonse files, but needlessly specifying myriad of command-line parameters is redundant anyway. --- Cabal/src/Distribution/Simple/PreProcess.hs | 155 ++++++++++---------- 1 file changed, 77 insertions(+), 78 deletions(-) diff --git a/Cabal/src/Distribution/Simple/PreProcess.hs b/Cabal/src/Distribution/Simple/PreProcess.hs index 7e146c9a391..4db235065c7 100644 --- a/Cabal/src/Distribution/Simple/PreProcess.hs +++ b/Cabal/src/Distribution/Simple/PreProcess.hs @@ -527,89 +527,88 @@ ppHsc2hs bi lbi clbi = where ldflags = map ("--lflag=" ++) $ - concat - [ programDefaultArgs gccProg ++ programOverrideArgs gccProg - , osxFrameworkDirs - , [ arg - | isOSX - , opt <- map getSymbolicPath (PD.frameworks bi) ++ concatMap Installed.frameworks pkgs - , arg <- ["-framework", opt] - ] - , -- Note that on ELF systems, wherever we use -L, we must also use -R - -- because presumably that -L dir is not on the normal path for the - -- system's dynamic linker. This is needed because hsc2hs works by - -- compiling a C program and then running it. - - -- Options from the current package: - [ "-L" ++ u opt - | opt <- - if withFullyStaticExe lbi - then PD.extraLibDirsStatic bi - else PD.extraLibDirs bi - ] - , [ "-Wl,-R," ++ u opt - | isELF - , opt <- - if withFullyStaticExe lbi - then PD.extraLibDirsStatic bi - else PD.extraLibDirs bi - ] - , ["-l" ++ opt | opt <- PD.extraLibs bi] - , PD.ldOptions bi - , -- Options from dependent packages - [ opt - | pkg <- pkgs - , opt <- - ["-L" ++ opt | opt <- Installed.libraryDirs pkg] - ++ [ "-Wl,-R," ++ opt | isELF, opt <- Installed.libraryDirs pkg - ] - ++ [ "-l" ++ opt - | opt <- - if withFullyStaticExe lbi - then Installed.extraLibrariesStatic pkg - else Installed.extraLibraries pkg - ] - ++ Installed.ldOptions pkg + ordNub $ + concat + [ programDefaultArgs gccProg ++ programOverrideArgs gccProg + , osxFrameworkDirs + , [ arg + | isOSX + , opt <- map getSymbolicPath (PD.frameworks bi) ++ concatMap Installed.frameworks pkgs + , arg <- ["-framework", opt] + ] + , -- Note that on ELF systems, wherever we use -L, we must also use -R + -- because presumably that -L dir is not on the normal path for the + -- system's dynamic linker. This is needed because hsc2hs works by + -- compiling a C program and then running it. + + -- Options from the current package: + [ "-L" ++ u opt + | opt <- + if withFullyStaticExe lbi + then PD.extraLibDirsStatic bi + else PD.extraLibDirs bi + ] + , [ "-Wl,-R," ++ u opt + | isELF + , opt <- + if withFullyStaticExe lbi + then PD.extraLibDirsStatic bi + else PD.extraLibDirs bi + ] + , ["-l" ++ opt | opt <- PD.extraLibs bi] + , PD.ldOptions bi + , -- Options from dependent packages + [ opt + | pkg <- pkgs + , opt <- + ["-L" ++ opt | opt <- Installed.libraryDirs pkg] + ++ [ "-Wl,-R," ++ opt | isELF, opt <- Installed.libraryDirs pkg + ] + ++ [ "-l" ++ opt + | opt <- + if withFullyStaticExe lbi + then Installed.extraLibrariesStatic pkg + else Installed.extraLibraries pkg + ] + ++ Installed.ldOptions pkg + ] ] - ] cflags = map ("--cflag=" ++) $ - concat - [ programDefaultArgs gccProg ++ programOverrideArgs gccProg - , osxFrameworkDirs - , platformDefines lbi - , -- Options from the current package: - ["-I" ++ u dir | dir <- PD.includeDirs bi] - , [ "-I" ++ u (buildDir lbi unsafeCoerceSymbolicPath relDir) - | relDir <- mapMaybe symbolicPathRelative_maybe $ PD.includeDirs bi - ] - - , -- hsc2hs uses the C ABI - -- We assume that there are only C sources - -- and C++ functions are exported via a C - -- interface and wrapped in a C source file. - -- Therefore we do not supply C++ flags - -- because there will not be C++ sources. - -- - -- DO NOT add PD.cxxOptions unless this changes! - PD.ccOptions bi ++ PD.cppOptions bi - - , - [ "-I" ++ u (autogenComponentModulesDir lbi clbi) - , "-I" ++ u (autogenPackageModulesDir lbi) - , "-include" - , u $ autogenComponentModulesDir lbi clbi makeRelativePathEx cppHeaderName - ] - - , -- Options from dependent packages - [ opt - | pkg <- pkgs - , opt <- - ["-I" ++ opt | opt <- Installed.includeDirs pkg] - ++ Installed.ccOptions pkg + ordNub $ + concat + [ programDefaultArgs gccProg ++ programOverrideArgs gccProg + , osxFrameworkDirs + , platformDefines lbi + , -- Options from the current package: + ["-I" ++ u dir | dir <- PD.includeDirs bi] + , [ "-I" ++ u (buildDir lbi unsafeCoerceSymbolicPath relDir) + | relDir <- mapMaybe symbolicPathRelative_maybe $ PD.includeDirs bi + ] + , -- hsc2hs uses the C ABI + -- We assume that there are only C sources + -- and C++ functions are exported via a C + -- interface and wrapped in a C source file. + -- Therefore we do not supply C++ flags + -- because there will not be C++ sources. + -- + -- DO NOT add PD.cxxOptions unless this changes! + PD.ccOptions bi ++ PD.cppOptions bi + , + [ "-I" ++ u (autogenComponentModulesDir lbi clbi) + , "-I" ++ u (autogenPackageModulesDir lbi) + , "-include" + , u $ autogenComponentModulesDir lbi clbi makeRelativePathEx cppHeaderName + ] + , -- Options from dependent packages + [ opt + | pkg <- pkgs + , opt <- + ["-I" ++ opt | opt <- Installed.includeDirs pkg] + ++ Installed.ccOptions pkg + ] ] - ] osxFrameworkDirs = [ "-F" ++ opt From e38f45d9c8ef9d40df3424299774a87868adb9df Mon Sep 17 00:00:00 2001 From: Sergey Vinokurov Date: Thu, 19 Jun 2025 23:32:32 +0100 Subject: [PATCH 20/39] Add changelog --- changelog.d/pr-11005.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 changelog.d/pr-11005.md diff --git a/changelog.d/pr-11005.md b/changelog.d/pr-11005.md new file mode 100644 index 00000000000..42bcd334c78 --- /dev/null +++ b/changelog.d/pr-11005.md @@ -0,0 +1,9 @@ +--- +synopsis: Deduplicate hsc2hs command-line arguments +packages: [Cabal] +prs: 11005 +--- + +Fix a problem where `hsc2hs` becomes non-operational on packages with +lots of dependencies and projects that specify lots of +`extra-include-dirs` or `extra-lib-dirs`. From c3b3b82aca0f46c6066360908c84a7d17f09857b Mon Sep 17 00:00:00 2001 From: Sergey Vinokurov Date: Fri, 20 Jun 2025 00:01:26 +0100 Subject: [PATCH 21/39] Override C compiler for testing that no duplicate arguments are passed --- .../TooManyCommandLineArgs/custom-cc | 72 +++++++++++++++++++ .../TooManyCommandLineArgs/setup.test.hs | 13 +++- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100755 cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/custom-cc diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/custom-cc b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/custom-cc new file mode 100755 index 00000000000..79054cac89f --- /dev/null +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/custom-cc @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +# NB only check for duplicated -L or -I arguments because that’s the +# primary source of duplication. Some minor duplicates can also occur, +# like -Wl,--no-as-needed but there’s not too many of them yet to +# cause command line overflow. + +# Quadratic algorithm because darwin CI doesn’t seem to have bash that +# supports associative arrays. +check_duplicate() { + local test_arg="$1" + local occurs="0" + + if ! [[ "$test_arg" == -L* || "$test_arg" == -I* ]]; then + return 0 + fi + + shift + for tmp in "${@}"; do + if [[ "$tmp" == @* ]]; then + while IFS= read -d $'\n' -r arg ; do + if [[ "$arg" == "$test_arg" ]]; then + occurs=$((occurs + 1)) + fi + done <"${tmp#@}" + else + if [[ "$tmp" == "$test_arg" ]]; then + occurs=$((occurs + 1)) + fi + fi + done + + if [[ "$occurs" -gt 1 ]]; then + return 1 + else + return 0 + fi +} + +i=1 +for x in "${@}"; do + if [[ "$x" == @* ]]; then + file= + while IFS= read -d $'\n' -r arg ; do + y="$arg" + if ! check_duplicate "$arg" "${@:$i}"; then + echo "Duplicate argument detected: '$y'" + echo "All args: ${@}" + exit 1 + fi + done <"${x#@}" + else + if ! check_duplicate "$x" "${@:$i}"; then + echo "Duplicate argument detected: '$x'" + echo "All args: ${@}" + exit 1 + fi + fi + + i=$((i + 1)) +done + +if which cc >/dev/null 2>&1; then + cc -DNOERROR6 "${@}" +elif which gcc >/dev/null 2>&1; then + gcc -DNOERROR6 "${@}" +elif which clang >/dev/null 2>&1; then + clang -DNOERROR6 "${@}" +else + echo "Cannot find C compiler" >&2 + exit 1 +fi diff --git a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs index 0a89a85e6fa..036d10cbdce 100644 --- a/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs +++ b/cabal-testsuite/PackageTests/PreProcess/TooManyCommandLineArgs/setup.test.hs @@ -87,9 +87,20 @@ main = cabalTest $ do libFlags = concatMap (\x -> ["--extra-lib-dirs", cwd x]) libDirs + let customCC = cwd "custom-cc" + -- Parallel flag means output of this test is nondeterministic _<- recordMode DoNotRecord $ - cabal "v2-build" $ ["-j", "my-toplevel:exe:my-executable"] ++ includeFlags ++ libFlags + cabal "v2-build" $ + ["-j", "my-toplevel:exe:my-executable"] + -- Don’t validate absence of duplicates on Windows because + -- it’s too inconvenient to do in bat files. Let’s just + -- assume that deduplication will happen on Windows but + -- still try to run the test to see whether command-line + -- argument length limit is not hit. + ++ ["--with-gcc=" ++ customCC | not isWindows] + ++ includeFlags + ++ libFlags r <- withPlan $ runPlanExe' "my-toplevel" "my-executable" [] assertOutputContains ("Result = " ++ show (42 + 55 + 10 * (55 + 10 * 55) + 3 * 55)) From 20460e94c4080e83a6322be00c4df709cceac50a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 16 Jul 2025 08:52:48 -0400 Subject: [PATCH 22/39] Fix typo exsting --- doc/cabaldomain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cabaldomain.py b/doc/cabaldomain.py index 9e16aefa6d6..8646c7453c9 100644 --- a/doc/cabaldomain.py +++ b/doc/cabaldomain.py @@ -384,7 +384,7 @@ def run(self): else: return result - # find exsting field list and add to it + # find existing field list and add to it # or create new one for item in contents: if isinstance(item, nodes.field_list): From a6e4162adb5a0ab8946d4c9fe1d33af8ccd98fa7 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 17 Jul 2025 21:42:33 -0400 Subject: [PATCH 23/39] Bump counts with hlint-3.8 --- .hlint.yaml | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index 6c5d86998d3..00468f57200 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -1,13 +1,12 @@ # Warnings currently triggered by your code - ignore: {name: "Avoid NonEmpty.unzip"} # 1 hint -- ignore: {name: "Avoid lambda"} # 47 hints +- ignore: {name: "Avoid lambda"} # 50 hints - ignore: {name: "Avoid lambda using `infix`"} # 23 hints -- ignore: {name: "Eta reduce"} # 124 hints +- ignore: {name: "Eta reduce"} # 132 hints - ignore: {name: "Evaluate"} # 10 hints - ignore: {name: "Functor law"} # 10 hints - ignore: {name: "Fuse concatMap/map"} # 3 hints - ignore: {name: "Fuse foldr/map"} # 3 hints -- ignore: {name: "Fuse mapMaybe/map"} # 1 hint - ignore: {name: "Fuse traverse_/fmap"} # 1 hint - ignore: {name: "Fuse traverse_/map"} # 1 hint - ignore: {name: "Hoist not"} # 16 hints @@ -16,11 +15,11 @@ - ignore: {name: "Monoid law, right identity"} # 3 hints - ignore: {name: "Move filter"} # 4 hints - ignore: {name: "Move guards forward"} # 4 hints -- ignore: {name: "Redundant $"} # 178 hints +- ignore: {name: "Redundant $"} # 192 hints - ignore: {name: "Redundant $!"} # 1 hint -- ignore: {name: "Redundant <$>"} # 16 hints +- ignore: {name: "Redundant <$>"} # 17 hints - ignore: {name: "Redundant =="} # 1 hint -- ignore: {name: "Redundant bracket"} # 240 hints +- ignore: {name: "Redundant bracket"} # 260 hints - ignore: {name: "Redundant fmap"} # 1 hint - ignore: {name: "Redundant guard"} # 2 hints - ignore: {name: "Redundant if"} # 6 hints @@ -28,13 +27,13 @@ - ignore: {name: "Redundant multi-way if"} # 1 hint - ignore: {name: "Redundant return"} # 9 hints - ignore: {name: "Replace case with fromMaybe"} # 6 hints -- ignore: {name: "Replace case with maybe"} # 10 hints +- ignore: {name: "Replace case with maybe"} # 11 hints - ignore: {name: "Use $>"} # 5 hints - ignore: {name: "Use ++"} # 4 hints -- ignore: {name: "Use :"} # 30 hints +- ignore: {name: "Use :"} # 28 hints - ignore: {name: "Use <$"} # 2 hints -- ignore: {name: "Use <$>"} # 87 hints -- ignore: {name: "Use <&>"} # 14 hints +- ignore: {name: "Use <$>"} # 86 hints +- ignore: {name: "Use <&>"} # 15 hints - ignore: {name: "Use <=<"} # 4 hints - ignore: {name: "Use =<<"} # 7 hints - ignore: {name: "Use =="} # 3 hints @@ -43,19 +42,19 @@ - ignore: {name: "Use Down"} # 3 hints - ignore: {name: "Use Just"} # 2 hints - ignore: {name: "Use bimap"} # 7 hints -- ignore: {name: "Use camelCase"} # 98 hints +- ignore: {name: "Use camelCase"} # 94 hints - ignore: {name: "Use catMaybes"} # 3 hints - ignore: {name: "Use concatMap"} # 2 hints -- ignore: {name: "Use const"} # 36 hints +- ignore: {name: "Use const"} # 37 hints - ignore: {name: "Use elem"} # 2 hints -- ignore: {name: "Use first"} # 4 hints -- ignore: {name: "Use fmap"} # 25 hints +- ignore: {name: "Use first"} # 5 hints +- ignore: {name: "Use fmap"} # 24 hints - ignore: {name: "Use fold"} # 1 hint - ignore: {name: "Use for"} # 1 hint - ignore: {name: "Use forM_"} # 1 hint -- ignore: {name: "Use fromMaybe"} # 4 hints +- ignore: {name: "Use fromMaybe"} # 5 hints - ignore: {name: "Use fromRight"} # 1 hint -- ignore: {name: "Use fst"} # 1 hint +- ignore: {name: "Use fst"} # 2 hints - ignore: {name: "Use if"} # 2 hints - ignore: {name: "Use infix"} # 20 hints - ignore: {name: "Use isAsciiLower"} # 2 hints @@ -63,7 +62,7 @@ - ignore: {name: "Use isDigit"} # 2 hints - ignore: {name: "Use isJust"} # 1 hint - ignore: {name: "Use isNothing"} # 1 hint -- ignore: {name: "Use lambda-case"} # 55 hints +- ignore: {name: "Use lambda-case"} # 58 hints - ignore: {name: "Use lefts"} # 1 hint - ignore: {name: "Use list comprehension"} # 19 hints - ignore: {name: "Use list literal"} # 3 hints @@ -74,7 +73,7 @@ - ignore: {name: "Use max"} # 2 hints - ignore: {name: "Use maybe"} # 8 hints - ignore: {name: "Use minimumBy"} # 1 hint -- ignore: {name: "Use newtype instead of data"} # 26 hints +- ignore: {name: "Use newtype instead of data"} # 29 hints - ignore: {name: "Use notElem"} # 9 hints - ignore: {name: "Use null"} # 2 hints - ignore: {name: "Use record patterns"} # 16 hints @@ -82,12 +81,12 @@ - ignore: {name: "Use replicateM_"} # 2 hints - ignore: {name: "Use rights"} # 2 hints - ignore: {name: "Use second"} # 7 hints -- ignore: {name: "Use section"} # 17 hints +- ignore: {name: "Use section"} # 18 hints - ignore: {name: "Use traverse"} # 1 hint -- ignore: {name: "Use tuple-section"} # 28 hints +- ignore: {name: "Use tuple-section"} # 27 hints - ignore: {name: "Use typeRep"} # 2 hints - ignore: {name: "Use uncurry"} # 1 hint -- ignore: {name: "Use unless"} # 22 hints +- ignore: {name: "Use unless"} # 23 hints - ignore: {name: "Use unwords"} # 8 hints - ignore: {name: "Use void"} # 23 hints - ignore: {name: "Use when"} # 1 hint From 46099cf30d132bca61f2c8123dc29d2fbc2c40d6 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 20 Jul 2025 06:58:17 -0400 Subject: [PATCH 24/39] Typo, seach, in comment --- cabal-testsuite/src/Test/Cabal/NeedleHaystack.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal-testsuite/src/Test/Cabal/NeedleHaystack.hs b/cabal-testsuite/src/Test/Cabal/NeedleHaystack.hs index 2ba8f93f41e..b77c43ff48f 100644 --- a/cabal-testsuite/src/Test/Cabal/NeedleHaystack.hs +++ b/cabal-testsuite/src/Test/Cabal/NeedleHaystack.hs @@ -145,7 +145,7 @@ data TxFwdBwd = txFwdBwdId :: TxFwdBwd txFwdBwdId = TxFwdBwd id id --- | Conversions of the needle and haystack strings, the seach string and the +-- | Conversions of the needle and haystack strings, the search string and the -- text to search in. data NeedleHaystack = NeedleHaystack From e55b86618240c594be3151e3321a70343aa275b6 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 17 Jul 2025 21:51:50 -0400 Subject: [PATCH 25/39] Bump to hlint-3.10 - Bump to hlint-3.10 in the gh-action --- .github/workflows/lint.yml | 2 +- .hlint.yaml | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b16a5ef0925..102be090d04 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - uses: haskell-actions/hlint-setup@v2 with: - version: "3.8" + version: "3.10" - uses: haskell-actions/hlint-run@v2 with: path: "." diff --git a/.hlint.yaml b/.hlint.yaml index 00468f57200..c827093b84f 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -24,10 +24,11 @@ - ignore: {name: "Redundant guard"} # 2 hints - ignore: {name: "Redundant if"} # 6 hints - ignore: {name: "Redundant lambda"} # 19 hints +- ignore: {name: "Redundant maybe"} # 2 hints - ignore: {name: "Redundant multi-way if"} # 1 hint - ignore: {name: "Redundant return"} # 9 hints -- ignore: {name: "Replace case with fromMaybe"} # 6 hints -- ignore: {name: "Replace case with maybe"} # 11 hints +- ignore: {name: "Replace case with fromMaybe"} # 4 hints +- ignore: {name: "Replace case with maybe"} # 10 hints - ignore: {name: "Use $>"} # 5 hints - ignore: {name: "Use ++"} # 4 hints - ignore: {name: "Use :"} # 28 hints @@ -40,7 +41,6 @@ - ignore: {name: "Use >=>"} # 3 hints - ignore: {name: "Use ?~"} # 1 hint - ignore: {name: "Use Down"} # 3 hints -- ignore: {name: "Use Just"} # 2 hints - ignore: {name: "Use bimap"} # 7 hints - ignore: {name: "Use camelCase"} # 94 hints - ignore: {name: "Use catMaybes"} # 3 hints @@ -55,7 +55,6 @@ - ignore: {name: "Use fromMaybe"} # 5 hints - ignore: {name: "Use fromRight"} # 1 hint - ignore: {name: "Use fst"} # 2 hints -- ignore: {name: "Use if"} # 2 hints - ignore: {name: "Use infix"} # 20 hints - ignore: {name: "Use isAsciiLower"} # 2 hints - ignore: {name: "Use isAsciiUpper"} # 2 hints From 86c71533aa4f42687af6b649eb6233779a28c3d8 Mon Sep 17 00:00:00 2001 From: jgotoh <18658140+jgotoh@users.noreply.github.com> Date: Fri, 31 May 2024 09:33:42 +0200 Subject: [PATCH 26/39] Replace cabal project parsing with parsec implementation The new parser replicates the grammar of the legacy parser while providing better error reporting and more maintainable code structure. The fallback strategy ensures smooth transition while the legacy parser is phased out. The flag `--project-file-parser` allows you to select which project file parser to use. * `legacy` - the old parser (will be removed in a future release) * `default` - the default parser (uses `fallback` unless compiled with `-f+legacy-comparison`) * `parsec` - the new parser using Parsec * `fallback` - the new parser using Parsec, but falling back to the old parser if it fails * `compare` - the new parser using Parsec, but comparing the results with the old parser When `cabal-install` is compiled, then the `-f+legacy-comparision` flag can be passed which changes the default parser mode to `compare`. Fixes #6101 #7748 #10611 --- Cabal-syntax/Cabal-syntax.cabal | 1 + Cabal-syntax/src/Distribution/FieldGrammar.hs | 15 + .../src/Distribution/FieldGrammar/Parsec.hs | 35 +- Cabal-syntax/src/Distribution/Fields.hs | 5 + .../src/Distribution/Fields/ConfVar.hs | 22 +- .../src/Distribution/Fields/ParseResult.hs | 141 ++-- .../src/Distribution/InstalledPackageInfo.hs | 10 +- .../PackageDescription/FieldGrammar.hs | 4 +- .../Distribution/PackageDescription/Parsec.hs | 82 +-- Cabal-syntax/src/Distribution/Parsec.hs | 17 +- Cabal-syntax/src/Distribution/Parsec/Error.hs | 14 +- .../src/Distribution/Parsec/Source.hs | 45 ++ .../src/Distribution/Parsec/Warning.hs | 21 +- Cabal-tests/tests/CheckTests.hs | 9 +- Cabal-tests/tests/HackageTests.hs | 14 +- Cabal-tests/tests/NoThunks.hs | 5 +- Cabal-tests/tests/ParserTests.hs | 28 +- Cabal/src/Distribution/Simple/Compiler.hs | 59 +- Cabal/src/Distribution/Simple/InstallDirs.hs | 90 +++ .../Distribution/Simple/PackageDescription.hs | 41 +- Cabal/src/Distribution/Simple/Setup/Config.hs | 9 - Cabal/src/Distribution/Types/DumpBuildInfo.hs | 10 + .../Solver/Types/ProjectConfigPath.hs | 12 + .../src/Distribution/Solver/Types/Settings.hs | 23 + cabal-install/cabal-install.cabal | 33 + cabal-install/parser-tests/Tests.hs | 7 + .../parser-tests/Tests/ParserTests.hs | 624 ++++++++++++++++++ .../files/all-packages-concat/cabal.project | 8 + .../Tests/files/empty/cabal.project | 0 .../Tests/files/extra-packages/cabal.project | 3 + .../haddock-all-overwrite-false/cabal.project | 2 + .../haddock-all-overwrite-true/cabal.project | 2 + .../Tests/files/haddock-all/cabal.project | 1 + .../Tests/files/install-dirs/cabal.project | 13 + .../files/library-coverage/cabal.project | 2 + .../files/local-no-index-repos/cabal.project | 5 + .../files/optional-packages/cabal.project | 1 + .../Tests/files/packages/cabal.project | 1 + .../program-locations-concat/cabal.project | 5 + .../program-options-concat/cabal.project | 9 + .../project-config-all-packages/cabal.project | 3 + .../project-config-build-only/cabal.project | 22 + .../cabal.project | 70 ++ .../files/project-config-shared/cabal.project | 39 ++ .../cabal.project | 12 + .../files/relax-deps-concat/cabal.project | 7 + .../Tests/files/remote-repos/cabal.project | 18 + .../source-repository-packages/cabal.project | 10 + .../specific-packages-concat/cabal.project | 7 + .../Client/BuildReports/Anonymous.hs | 4 +- .../src/Distribution/Client/Check.hs | 23 +- .../src/Distribution/Client/CmdConfigure.hs | 6 +- .../Client/CmdInstall/ClientInstallFlags.hs | 46 ++ .../src/Distribution/Client/Config.hs | 1 + .../src/Distribution/Client/Errors.hs | 27 +- .../src/Distribution/Client/Errors/Parser.hs | 277 ++++++++ .../src/Distribution/Client/ParseUtils.hs | 10 +- .../src/Distribution/Client/ProjectConfig.hs | 277 +++++--- .../Client/ProjectConfig/FieldGrammar.hs | 265 ++++++++ .../Client/ProjectConfig/Legacy.hs | 2 + .../Distribution/Client/ProjectConfig/Lens.hs | 557 ++++++++++++++++ .../Client/ProjectConfig/Parsec.hs | 427 ++++++++++++ .../Client/ProjectConfig/Types.hs | 21 + .../src/Distribution/Client/ProjectFlags.hs | 32 +- .../Distribution/Client/ProjectPlanning.hs | 8 +- .../src/Distribution/Client/ScriptUtils.hs | 2 +- .../Distribution/Client/Types/AllowNewer.hs | 6 + .../src/Distribution/Client/Types/Repo.hs | 34 +- .../Types/WriteGhcEnvironmentFilesPolicy.hs | 15 + .../src/Distribution/Client/Utils/Newtypes.hs | 120 ++++ .../src/Distribution/Client/Utils/Parsec.hs | 183 +++-- .../Distribution/Client/ProjectConfig.hs | 5 + .../Distribution/Client/TreeDiffInstances.hs | 1 + .../ConfiguredPackage/Sanity/NoName/cabal.out | 8 +- .../Sanity/NoVersion/cabal.out | 8 +- .../ConditionalAndImport/cabal.out | 198 ++++-- .../IndexCabalFileParseError/cabal.out | 3 +- .../FieldStanzaConfusion/cabal.out | 15 +- .../FieldStanzaConfusion/cabal.test.hs | 2 +- .../ParseErrorProvenance/cabal.out | 50 +- .../ParseErrorProvenance/cabal.test.hs | 8 +- .../ParseWarningProvenance/cabal.out | 16 +- .../PackageTests/Regression/T5213/cabal.out | 2 + .../Regression/T5213/cabal.project | 2 +- cabal-validate/src/Main.hs | 8 + cabal.validate.project | 6 +- changelog.d/pr-8889 | 27 + doc/cabal-project-description-file.rst | 18 +- 88 files changed, 3828 insertions(+), 508 deletions(-) create mode 100644 Cabal-syntax/src/Distribution/Parsec/Source.hs create mode 100644 cabal-install/parser-tests/Tests.hs create mode 100644 cabal-install/parser-tests/Tests/ParserTests.hs create mode 100644 cabal-install/parser-tests/Tests/files/all-packages-concat/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/empty/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/extra-packages/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/haddock-all-overwrite-false/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/haddock-all-overwrite-true/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/haddock-all/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/install-dirs/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/library-coverage/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/local-no-index-repos/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/optional-packages/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/packages/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/program-locations-concat/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/program-options-concat/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/project-config-all-packages/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/project-config-build-only/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/project-config-local-packages/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/project-config-shared/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/project-config-specific-packages/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/relax-deps-concat/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/remote-repos/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/source-repository-packages/cabal.project create mode 100644 cabal-install/parser-tests/Tests/files/specific-packages-concat/cabal.project create mode 100644 cabal-install/src/Distribution/Client/Errors/Parser.hs create mode 100644 cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs create mode 100644 cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs create mode 100644 cabal-install/src/Distribution/Client/ProjectConfig/Parsec.hs create mode 100644 cabal-install/src/Distribution/Client/Utils/Newtypes.hs create mode 100644 changelog.d/pr-8889 diff --git a/Cabal-syntax/Cabal-syntax.cabal b/Cabal-syntax/Cabal-syntax.cabal index dd3137786e2..1d651252b45 100644 --- a/Cabal-syntax/Cabal-syntax.cabal +++ b/Cabal-syntax/Cabal-syntax.cabal @@ -107,6 +107,7 @@ library Distribution.Parsec.FieldLineStream Distribution.Parsec.Position Distribution.Parsec.Warning + Distribution.Parsec.Source Distribution.Pretty Distribution.SPDX Distribution.SPDX.License diff --git a/Cabal-syntax/src/Distribution/FieldGrammar.hs b/Cabal-syntax/src/Distribution/FieldGrammar.hs index e41dd6350c2..78739a37cfa 100644 --- a/Cabal-syntax/src/Distribution/FieldGrammar.hs +++ b/Cabal-syntax/src/Distribution/FieldGrammar.hs @@ -15,6 +15,7 @@ module Distribution.FieldGrammar , ParsecFieldGrammar , ParsecFieldGrammar' , parseFieldGrammar + , parseFieldGrammarCheckingStanzas , fieldGrammarKnownFieldList , PrettyFieldGrammar , PrettyFieldGrammar' @@ -65,6 +66,20 @@ x ^^^ f = f x data PS ann = PS (Fields ann) [Section ann] [[Section ann]] -- | Partition field list into field map and groups of sections. +-- Groups sections between fields. This means that the following snippet contains +-- two section groups: +-- +-- @ +-- -- first group +-- some-section +-- field: value +-- another-section +-- field: value +-- foo: bar +-- -- second group +-- yet-another-section +-- field: value +-- @ partitionFields :: [Field ann] -> (Fields ann, [[Section ann]]) partitionFields = finalize . foldl' f (PS mempty mempty mempty) where diff --git a/Cabal-syntax/src/Distribution/FieldGrammar/Parsec.hs b/Cabal-syntax/src/Distribution/FieldGrammar/Parsec.hs index 4721aa4ad08..461a76cdd44 100644 --- a/Cabal-syntax/src/Distribution/FieldGrammar/Parsec.hs +++ b/Cabal-syntax/src/Distribution/FieldGrammar/Parsec.hs @@ -1,6 +1,7 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -- | This module provides a 'FieldGrammarParser', one way to parse @@ -54,6 +55,7 @@ module Distribution.FieldGrammar.Parsec ( ParsecFieldGrammar , parseFieldGrammar + , parseFieldGrammarCheckingStanzas , fieldGrammarKnownFieldList -- * Auxiliary @@ -112,24 +114,35 @@ data Section ann = MkSection !(Name ann) [SectionArg ann] [Field ann] data ParsecFieldGrammar s a = ParsecFG { fieldGrammarKnownFields :: !(Set FieldName) , fieldGrammarKnownPrefixes :: !(Set FieldName) - , fieldGrammarParser :: !(CabalSpecVersion -> Fields Position -> ParseResult a) + , fieldGrammarParser :: forall src. (CabalSpecVersion -> Fields Position -> ParseResult src a) } deriving (Functor) -parseFieldGrammar :: CabalSpecVersion -> Fields Position -> ParsecFieldGrammar s a -> ParseResult a +parseFieldGrammar :: CabalSpecVersion -> Fields Position -> ParsecFieldGrammar s a -> ParseResult src a parseFieldGrammar v fields grammar = do - for_ (Map.toList (Map.filterWithKey isUnknownField fields)) $ \(name, nfields) -> + for_ (Map.toList (Map.filterWithKey (isUnknownField grammar) fields)) $ \(name, nfields) -> for_ nfields $ \(MkNamelessField pos _) -> parseWarning pos PWTUnknownField $ "Unknown field: " ++ show name -- TODO: fields allowed in this section -- parse fieldGrammarParser grammar v fields - where - isUnknownField k _ = - not $ - k `Set.member` fieldGrammarKnownFields grammar - || any (`BS.isPrefixOf` k) (fieldGrammarKnownPrefixes grammar) + +isUnknownField :: ParsecFieldGrammar s a -> FieldName -> [NamelessField Position] -> Bool +isUnknownField grammar k _ = + not $ + k `Set.member` fieldGrammarKnownFields grammar + || any (`BS.isPrefixOf` k) (fieldGrammarKnownPrefixes grammar) + +-- | Parse a ParsecFieldGrammar and check for fields that should be stanzas. +parseFieldGrammarCheckingStanzas :: CabalSpecVersion -> Fields Position -> ParsecFieldGrammar s a -> Set BS.ByteString -> ParseResult src a +parseFieldGrammarCheckingStanzas v fields grammar sections = do + for_ (Map.toList (Map.filterWithKey (isUnknownField grammar) fields)) $ \(name, nfields) -> + for_ nfields $ \(MkNamelessField pos _) -> + if name `Set.member` sections + then parseFailure pos $ "'" ++ fromUTF8BS name ++ "' is a stanza, not a field. Remove the trailing ':' to parse a stanza." + else parseWarning pos PWTUnknownField $ "Unknown field: " ++ show name + fieldGrammarParser grammar v fields fieldGrammarKnownFieldList :: ParsecFieldGrammar s a -> [FieldName] fieldGrammarKnownFieldList = Set.toList . fieldGrammarKnownFields @@ -145,7 +158,7 @@ instance Applicative (ParsecFieldGrammar s) where (\v fields -> f'' v fields <*> x'' v fields) {-# INLINE (<*>) #-} -warnMultipleSingularFields :: FieldName -> [NamelessField Position] -> ParseResult () +warnMultipleSingularFields :: FieldName -> [NamelessField Position] -> ParseResult src () warnMultipleSingularFields _ [] = pure () warnMultipleSingularFields fn (x : xs) = do let pos = namelessFieldAnn x @@ -349,7 +362,7 @@ instance FieldGrammar Parsec ParsecFieldGrammar where -- Parsec ------------------------------------------------------------------------------- -runFieldParser' :: [Position] -> ParsecParser a -> CabalSpecVersion -> FieldLineStream -> ParseResult a +runFieldParser' :: [Position] -> ParsecParser a -> CabalSpecVersion -> FieldLineStream -> ParseResult src a runFieldParser' inputPoss p v str = case P.runParser p' [] "" str of Right (pok, ws) -> do traverse_ (\(PWarning t pos w) -> parseWarning (mapPosition pos) t w) ws @@ -378,7 +391,7 @@ runFieldParser' inputPoss p v str = case P.runParser p' [] "" str of go n (Position row col : _) | n <= 0 = Position row (col + pcol - 1) go n (_ : ps) = go (n - 1) ps -runFieldParser :: Position -> ParsecParser a -> CabalSpecVersion -> [FieldLine Position] -> ParseResult a +runFieldParser :: Position -> ParsecParser a -> CabalSpecVersion -> [FieldLine Position] -> ParseResult src a runFieldParser pp p v ls = runFieldParser' poss p v (fieldLinesToStream ls) where poss = map (\(FieldLine pos _) -> pos) ls ++ [pp] -- add "default" position diff --git a/Cabal-syntax/src/Distribution/Fields.hs b/Cabal-syntax/src/Distribution/Fields.hs index 4688bf547d9..9aabb9ff7fe 100644 --- a/Cabal-syntax/src/Distribution/Fields.hs +++ b/Cabal-syntax/src/Distribution/Fields.hs @@ -25,11 +25,16 @@ module Distribution.Fields -- ** Warnings , PWarnType (..) , PWarning (..) + , PWarningWithSource (..) + , PSource (..) , showPWarning + , showPWarningWithSource -- ** Errors , PError (..) + , PErrorWithSource (..) , showPError + , showPErrorWithSource -- * Pretty printing , CommentPosition (..) diff --git a/Cabal-syntax/src/Distribution/Fields/ConfVar.hs b/Cabal-syntax/src/Distribution/Fields/ConfVar.hs index 760e2335143..e5878db3df7 100644 --- a/Cabal-syntax/src/Distribution/Fields/ConfVar.hs +++ b/Cabal-syntax/src/Distribution/Fields/ConfVar.hs @@ -4,11 +4,12 @@ module Distribution.Fields.ConfVar (parseConditionConfVar, parseConditionConfVar import Distribution.Compat.CharParsing (char, integral) import Distribution.Compat.Prelude -import Distribution.Fields.Field (Field (..), SectionArg (..)) +import Distribution.Fields.Field (Field (..), SectionArg (..), sectionArgAnn) import Distribution.Fields.ParseResult import Distribution.Fields.Parser (readFields) -import Distribution.Parsec (Parsec (..), Position (..), runParsecParser) +import Distribution.Parsec (Parsec (..), runParsecParser) import Distribution.Parsec.FieldLineStream (fieldLineStreamFromBS) +import Distribution.Parsec.Position import Distribution.Types.Condition import Distribution.Types.ConfVar (ConfVar (..)) import Distribution.Version @@ -40,10 +41,10 @@ parseConditionConfVarFromClause x = -- | Parse @'Condition' 'ConfVar'@ from section arguments provided by parsec -- based outline parser. -parseConditionConfVar :: [SectionArg Position] -> ParseResult (Condition ConfVar) -parseConditionConfVar args = +parseConditionConfVar :: Position -> [SectionArg Position] -> ParseResult src (Condition ConfVar) +parseConditionConfVar start_pos args = -- The name of the input file is irrelevant, as we reformat the error message. - case P.runParser (parser <* P.eof) () "" args of + case P.runParser (P.setPosition startPos >> parser <* P.eof) () "" args of Right x -> pure x Left err -> do -- Mangle the position to the actual one @@ -59,7 +60,10 @@ parseConditionConfVar args = (P.errorMessages err) parseFailure epos msg pure $ Lit True + where + startPos = P.newPos "" (positionRow start_pos) (positionCol start_pos) +-- | Parser for 'Condition' 'ConfVar' type Parser = P.Parsec [SectionArg Position] () sepByNonEmpty :: Parser a -> Parser sep -> Parser (NonEmpty a) @@ -132,6 +136,7 @@ parser = condOr | s == "false" -> Just False _ -> Nothing + string :: B8.ByteString -> Parser () string s = tokenPrim $ \t -> case t of SecArgName _ s' | s == s' -> Just () _ -> Nothing @@ -142,9 +147,12 @@ parser = condOr parens = P.between (oper "(") (oper ")") + tokenPrim :: (SectionArg Position -> Maybe a) -> Parser a tokenPrim = P.tokenPrim prettySectionArg updatePosition - -- TODO: check where the errors are reported - updatePosition x _ _ = x + updatePosition :: P.SourcePos -> SectionArg Position -> [SectionArg Position] -> P.SourcePos + updatePosition x s _ = + let Position line col = sectionArgAnn s + in P.setSourceLine (P.setSourceColumn x col) (line) prettySectionArg = show fromParsec :: Parsec a => Parser a diff --git a/Cabal-syntax/src/Distribution/Fields/ParseResult.hs b/Cabal-syntax/src/Distribution/Fields/ParseResult.hs index 912ea7fb02f..6c2dbe5e552 100644 --- a/Cabal-syntax/src/Distribution/Fields/ParseResult.hs +++ b/Cabal-syntax/src/Distribution/Fields/ParseResult.hs @@ -6,6 +6,8 @@ module Distribution.Fields.ParseResult ( ParseResult , runParseResult + , PSource (..) + , CabalFileSource (..) , recoverWith , parseWarning , parseWarnings @@ -15,131 +17,160 @@ module Distribution.Fields.ParseResult , getCabalSpecVersion , setCabalSpecVersion , withoutWarnings + , liftParseResult + , withSource ) where import Distribution.Compat.Prelude -import Distribution.Parsec.Error (PError (..)) +import Distribution.Parsec.Error (PError (..), PErrorWithSource (..)) import Distribution.Parsec.Position (Position (..), zeroPos) -import Distribution.Parsec.Warning (PWarnType (..), PWarning (..)) +import Distribution.Parsec.Source +import Distribution.Parsec.Warning import Distribution.Version (Version) -- | A monad with failure and accumulating errors and warnings. -newtype ParseResult a = PR +newtype ParseResult src a = PR { unPR :: forall r - . PRState - -> (PRState -> r) -- failure, but we were able to recover a new-style spec-version declaration - -> (PRState -> a -> r) -- success + . PRState src + -> PRContext src + -> (PRState src -> r) -- failure, but we were able to recover a new-style spec-version declaration + -> (PRState src -> a -> r) -- success -> r } +data PRContext src = PRContext + { prContextSource :: PSource src + -- ^ The file we are parsing, if known. This field is parametric because we + -- use the same parser for cabal files and project files. + } + -- Note: we have version here, as we could get any version. -data PRState = PRState ![PWarning] ![PError] !(Maybe Version) +data PRState src = PRState ![PWarningWithSource src] ![PErrorWithSource src] !(Maybe Version) -emptyPRState :: PRState +emptyPRState :: PRState src emptyPRState = PRState [] [] Nothing -- | Forget 'ParseResult's warnings. -- -- @since 3.4.0.0 -withoutWarnings :: ParseResult a -> ParseResult a -withoutWarnings m = PR $ \s failure success -> - unPR m s failure $ \ !s1 -> success (s1 `withWarningsOf` s) +withoutWarnings :: ParseResult src a -> ParseResult src a +withoutWarnings m = PR $ \s ctx failure success -> + unPR m s ctx failure $ \ !s1 -> success (s1 `withWarningsOf` s) where withWarningsOf (PRState _ e v) (PRState w _ _) = PRState w e v -- | Destruct a 'ParseResult' into the emitted warnings and either -- a successful value or -- list of errors and possibly recovered a spec-version declaration. -runParseResult :: ParseResult a -> ([PWarning], Either (Maybe Version, NonEmpty PError) a) -runParseResult pr = unPR pr emptyPRState failure success +runParseResult :: ParseResult src a -> ([PWarningWithSource src], Either (Maybe Version, NonEmpty (PErrorWithSource src)) a) +runParseResult pr = unPR pr emptyPRState initialCtx failure success where - failure (PRState warns [] v) = (warns, Left (v, PError zeroPos "panic" :| [])) + initialCtx = PRContext PUnknownSource + + failure (PRState warns [] v) = (warns, Left (v, PErrorWithSource PUnknownSource (PError zeroPos "panic") :| [])) failure (PRState warns (err : errs) v) = (warns, Left (v, err :| errs)) success (PRState warns [] _) x = (warns, Right x) -- If there are any errors, don't return the result success (PRState warns (err : errs) v) _ = (warns, Left (v, err :| errs)) -instance Functor ParseResult where - fmap f (PR pr) = PR $ \ !s failure success -> - pr s failure $ \ !s' a -> +-- | Chain parsing operations that involve 'IO' actions. +liftParseResult :: (a -> IO (ParseResult src b)) -> ParseResult src a -> IO (ParseResult src b) +liftParseResult f pr = unPR pr emptyPRState initialCtx failure success + where + initialCtx = PRContext PUnknownSource + + failure s = return $ PR $ \s' _ctx failure' _ -> failure' (concatPRState s s') + success s a = do + pr' <- f a + return $ PR $ \s' ctx failure' success' -> unPR pr' (concatPRState s s') ctx failure' success' + concatPRState (PRState warnings errors version) (PRState warnings' errors' version') = + (PRState (warnings ++ warnings') (toList errors ++ errors') (version <|> version')) + +withSource :: src -> ParseResult src a -> ParseResult src a +withSource source (PR pr) = PR $ \s ctx failure success -> + pr s (ctx{prContextSource = PKnownSource source}) failure success + +instance Functor (ParseResult src) where + fmap f (PR pr) = PR $ \ !s fp failure success -> + pr s fp failure $ \ !s' a -> success s' (f a) {-# INLINE fmap #-} -instance Applicative ParseResult where - pure x = PR $ \ !s _ success -> success s x +instance Applicative (ParseResult src) where + pure x = PR $ \ !s _ _ success -> success s x {-# INLINE pure #-} - f <*> x = PR $ \ !s0 failure success -> - unPR f s0 failure $ \ !s1 f' -> - unPR x s1 failure $ \ !s2 x' -> + f <*> x = PR $ \ !s0 fp failure success -> + unPR f s0 fp failure $ \ !s1 f' -> + unPR x s1 fp failure $ \ !s2 x' -> success s2 (f' x') {-# INLINE (<*>) #-} - x *> y = PR $ \ !s0 failure success -> - unPR x s0 failure $ \ !s1 _ -> - unPR y s1 failure success + x *> y = PR $ \ !s0 fp failure success -> + unPR x s0 fp failure $ \ !s1 _ -> + unPR y s1 fp failure success {-# INLINE (*>) #-} - x <* y = PR $ \ !s0 failure success -> - unPR x s0 failure $ \ !s1 x' -> - unPR y s1 failure $ \ !s2 _ -> + x <* y = PR $ \ !s0 fp failure success -> + unPR x s0 fp failure $ \ !s1 x' -> + unPR y s1 fp failure $ \ !s2 _ -> success s2 x' {-# INLINE (<*) #-} -instance Monad ParseResult where +instance Monad (ParseResult src) where return = pure (>>) = (*>) - m >>= k = PR $ \ !s failure success -> - unPR m s failure $ \ !s' a -> - unPR (k a) s' failure success + m >>= k = PR $ \ !s fp failure success -> + unPR m s fp failure $ \ !s' a -> + unPR (k a) s' fp failure success {-# INLINE (>>=) #-} -- | "Recover" the parse result, so we can proceed parsing. -- 'runParseResult' will still result in 'Nothing', if there are recorded errors. -recoverWith :: ParseResult a -> a -> ParseResult a -recoverWith (PR pr) x = PR $ \ !s _failure success -> - pr s (\ !s' -> success s' x) success +recoverWith :: ParseResult src a -> a -> ParseResult src a +recoverWith (PR pr) x = PR $ \ !s fp _failure success -> + pr s fp (\ !s' -> success s' x) success -- | Set cabal spec version. -setCabalSpecVersion :: Maybe Version -> ParseResult () -setCabalSpecVersion v = PR $ \(PRState warns errs _) _failure success -> +setCabalSpecVersion :: Maybe Version -> ParseResult src () +setCabalSpecVersion v = PR $ \(PRState warns errs _) _fp _failure success -> success (PRState warns errs v) () -- | Get cabal spec version. -getCabalSpecVersion :: ParseResult (Maybe Version) -getCabalSpecVersion = PR $ \s@(PRState _ _ v) _failure success -> +getCabalSpecVersion :: ParseResult src (Maybe Version) +getCabalSpecVersion = PR $ \s@(PRState _ _ v) _fp _failure success -> success s v -- | Add a warning. This doesn't fail the parsing process. -parseWarning :: Position -> PWarnType -> String -> ParseResult () -parseWarning pos t msg = PR $ \(PRState warns errs v) _failure success -> - success (PRState (PWarning t pos msg : warns) errs v) () +parseWarning :: Position -> PWarnType -> String -> ParseResult src () +parseWarning pos t msg = PR $ \(PRState warns errs v) ctx _failure success -> + success (PRState (PWarningWithSource (prContextSource ctx) (PWarning t pos msg) : warns) errs v) () -- | Add multiple warnings at once. -parseWarnings :: [PWarning] -> ParseResult () -parseWarnings newWarns = PR $ \(PRState warns errs v) _failure success -> - success (PRState (newWarns ++ warns) errs v) () +parseWarnings :: [PWarning] -> ParseResult src () +parseWarnings newWarns = PR $ \(PRState warns errs v) ctx _failure success -> + success (PRState (map (PWarningWithSource (prContextSource ctx)) newWarns ++ warns) errs v) () -- | Add an error, but not fail the parser yet. -- -- For fatal failure use 'parseFatalFailure' -parseFailure :: Position -> String -> ParseResult () -parseFailure pos msg = PR $ \(PRState warns errs v) _failure success -> - success (PRState warns (PError pos msg : errs) v) () +parseFailure :: Position -> String -> ParseResult src () +parseFailure pos msg = PR $ \(PRState warns errs v) ctx _failure success -> + success (PRState warns (PErrorWithSource (prContextSource ctx) (PError pos msg) : errs) v) () -- | Add an fatal error. -parseFatalFailure :: Position -> String -> ParseResult a -parseFatalFailure pos msg = PR $ \(PRState warns errs v) failure _success -> - failure (PRState warns (PError pos msg : errs) v) +parseFatalFailure :: Position -> String -> ParseResult src a +parseFatalFailure pos msg = PR $ \(PRState warns errs v) ctx failure _success -> + failure (PRState warns (PErrorWithSource (prContextSource ctx) (PError pos msg) : errs) v) -- | A 'mzero'. -parseFatalFailure' :: ParseResult a +parseFatalFailure' :: ParseResult src a parseFatalFailure' = PR pr where - pr (PRState warns [] v) failure _success = failure (PRState warns [err] v) - pr s failure _success = failure s + pr (PRState warns [] v) _ctx failure _success = failure (PRState warns [err] v) + pr s _ctx failure _success = failure s - err = PError zeroPos "Unknown fatal error" + err = PErrorWithSource PUnknownSource (PError zeroPos "Unknown fatal error") diff --git a/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs b/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs index 2c15d678335..350b9fee757 100644 --- a/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs +++ b/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs @@ -47,9 +47,11 @@ import Distribution.Backpack import Distribution.CabalSpecVersion (cabalSpecLatest) import Distribution.FieldGrammar import Distribution.FieldGrammar.FieldDescrs +import Distribution.Fields.ParseResult import Distribution.Fields.Pretty import Distribution.ModuleName import Distribution.Package hiding (installedUnitId) +import Distribution.Parsec.Source import Distribution.Types.ComponentName import Distribution.Utils.Generic (toUTF8BS) @@ -98,18 +100,18 @@ parseInstalledPackageInfo parseInstalledPackageInfo s = case P.readFields s of Left err -> Left (show err :| []) Right fs -> case partitionFields fs of - (fs', _) -> case P.runParseResult $ parseFieldGrammar cabalSpecLatest fs' ipiFieldGrammar of + (fs', _) -> case P.runParseResult $ withSource PInstalledPackageInfo $ parseFieldGrammar cabalSpecLatest fs' ipiFieldGrammar of (ws, Right x) -> x `deepseq` Right (ws', x) where ws' = - [ P.showPWarning "" w - | w@(P.PWarning wt _ _) <- ws + [ P.showPWarningWithSource (fmap renderInstalledPackageInfoSource w) + | w@(P.PWarningWithSource _ (P.PWarning wt _ _)) <- ws , -- filter out warnings about experimental features wt /= P.PWTExperimental ] (_, Left (_, errs)) -> Left errs' where - errs' = fmap (P.showPError "") errs + errs' = fmap (P.showPErrorWithSource . fmap renderInstalledPackageInfoSource) errs -- ----------------------------------------------------------------------------- -- Pretty-printing diff --git a/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs b/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs index 0b04b446c04..24861389b8f 100644 --- a/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs +++ b/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs @@ -361,7 +361,7 @@ testSuiteFieldGrammar = <*> monoidalFieldAla "code-generators" (alaList' CommaFSep Token) testStanzaCodeGenerators ^^^ availableSince CabalSpecV3_8 [] -validateTestSuite :: CabalSpecVersion -> Position -> TestSuiteStanza -> ParseResult TestSuite +validateTestSuite :: CabalSpecVersion -> Position -> TestSuiteStanza -> ParseResult src TestSuite validateTestSuite cabalSpecVersion pos stanza = case testSuiteType of Nothing -> pure basicTestSuite Just tt@(TestTypeUnknown _ _) -> @@ -503,7 +503,7 @@ benchmarkFieldGrammar = <*> optionalField "benchmark-module" benchmarkStanzaBenchmarkModule <*> blurFieldGrammar benchmarkStanzaBuildInfo buildInfoFieldGrammar -validateBenchmark :: CabalSpecVersion -> Position -> BenchmarkStanza -> ParseResult Benchmark +validateBenchmark :: CabalSpecVersion -> Position -> BenchmarkStanza -> ParseResult src Benchmark validateBenchmark cabalSpecVersion pos stanza = case benchmarkStanzaType of Nothing -> pure diff --git a/Cabal-syntax/src/Distribution/PackageDescription/Parsec.hs b/Cabal-syntax/src/Distribution/PackageDescription/Parsec.hs index 92256c98be5..c7e327ddb7f 100644 --- a/Cabal-syntax/src/Distribution/PackageDescription/Parsec.hs +++ b/Cabal-syntax/src/Distribution/PackageDescription/Parsec.hs @@ -20,6 +20,8 @@ module Distribution.PackageDescription.Parsec -- ** Parsing , ParseResult , runParseResult + , PSource (..) + , withSource -- * New-style spec-version , scanSpecVersion @@ -38,7 +40,7 @@ import Distribution.Compat.Lens import Distribution.FieldGrammar import Distribution.FieldGrammar.Parsec (NamelessField (..)) import Distribution.Fields.ConfVar (parseConditionConfVar) -import Distribution.Fields.Field (FieldName, getName) +import Distribution.Fields.Field (FieldName, getName, sectionArgAnn) import Distribution.Fields.LexerMonad (LexWarning, toPWarnings) import Distribution.Fields.ParseResult import Distribution.Fields.Parser @@ -48,7 +50,7 @@ import Distribution.PackageDescription.FieldGrammar import Distribution.PackageDescription.Quirks (patchQuirks) import Distribution.Parsec (parsec, simpleParsecBS) import Distribution.Parsec.FieldLineStream (fieldLineStreamFromBS) -import Distribution.Parsec.Position (Position (..), zeroPos) +import Distribution.Parsec.Position (Position (..), incPos, zeroPos) import Distribution.Parsec.Warning (PWarnType (..)) import Distribution.Pretty (prettyShow) import Distribution.Utils.Generic (breakMaybe, fromUTF8BS, toUTF8BS, unfoldrM, validateUTF8) @@ -74,7 +76,7 @@ import qualified Text.Parsec as P -- -- In Cabal 1.2 the syntax for package descriptions was changed to a format -- with sections and possibly indented property descriptions. -parseGenericPackageDescription :: BS.ByteString -> ParseResult GenericPackageDescription +parseGenericPackageDescription :: BS.ByteString -> ParseResult src GenericPackageDescription parseGenericPackageDescription bs = do -- set scanned version setCabalSpecVersion ver @@ -122,7 +124,7 @@ fieldlinesToBS :: [FieldLine ann] -> BS.ByteString fieldlinesToBS = BS.intercalate "\n" . map (\(FieldLine _ bs) -> bs) -- Monad in which sections are parsed -type SectionParser = StateT SectionS ParseResult +type SectionParser src = StateT SectionS (ParseResult src) -- | State of section parser data SectionS = SectionS @@ -150,7 +152,7 @@ parseGenericPackageDescription' -> [LexWarning] -> Maybe Int -> [Field Position] - -> ParseResult GenericPackageDescription + -> ParseResult src GenericPackageDescription parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fs = do parseWarnings (toPWarnings lexWarnings) for_ utf8WarnPos $ \pos -> @@ -220,7 +222,7 @@ parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fs = do newSyntaxVersion :: CabalSpecVersion newSyntaxVersion = CabalSpecV1_2 - maybeWarnCabalVersion :: Syntax -> PackageDescription -> ParseResult () + maybeWarnCabalVersion :: Syntax -> PackageDescription -> ParseResult src () maybeWarnCabalVersion syntax pkg | syntax == NewSyntax && specVersion pkg < newSyntaxVersion = parseWarning zeroPos PWTNewSyntax $ @@ -238,7 +240,7 @@ parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fs = do cabalFormatVersionsDesc :: String cabalFormatVersionsDesc = "Current cabal-version values are listed at https://cabal.readthedocs.io/en/stable/file-format-changelog.html." -goSections :: CabalSpecVersion -> [Field Position] -> SectionParser () +goSections :: CabalSpecVersion -> [Field Position] -> SectionParser src () goSections specVer = traverse_ process where process (Field (Name pos name) _) = @@ -261,10 +263,10 @@ goSections specVer = traverse_ process -> Map String CondTreeBuildInfo -- \^ common stanzas -> [Field Position] - -> ParseResult (CondTree ConfVar [Dependency] a) + -> ParseResult src (CondTree ConfVar [Dependency] a) parseCondTree' = parseCondTreeWithCommonStanzas specVer - parseSection :: Name Position -> [SectionArg Position] -> [Field Position] -> SectionParser () + parseSection :: Name Position -> [SectionArg Position] -> [Field Position] -> SectionParser src () parseSection (Name pos name) args fields | hasCommonStanzas == NoCommonStanzas , name == "common" = lift $ do @@ -413,10 +415,10 @@ goSections specVer = traverse_ process parseWarning pos PWTUnknownSection $ "Ignoring section: " ++ show name -parseName :: Position -> [SectionArg Position] -> SectionParser String +parseName :: Position -> [SectionArg Position] -> SectionParser src String parseName pos args = fromUTF8BS <$> parseNameBS pos args -parseNameBS :: Position -> [SectionArg Position] -> SectionParser BS.ByteString +parseNameBS :: Position -> [SectionArg Position] -> SectionParser src BS.ByteString -- TODO: use strict parser parseNameBS pos args = case args of [SecArgName _pos secName] -> @@ -431,7 +433,7 @@ parseNameBS pos args = case args of lift $ parseFailure pos $ "Invalid name " ++ show args pure "" -parseCommonName :: Position -> [SectionArg Position] -> ParseResult String +parseCommonName :: Position -> [SectionArg Position] -> ParseResult src String parseCommonName pos args = case args of [SecArgName _pos secName] -> pure $ fromUTF8BS secName @@ -446,7 +448,7 @@ parseCommonName pos args = case args of pure "" -- TODO: avoid conversion to 'String'. -parseUnqualComponentName :: Position -> [SectionArg Position] -> SectionParser UnqualComponentName +parseUnqualComponentName :: Position -> [SectionArg Position] -> SectionParser src UnqualComponentName parseUnqualComponentName pos args = mkUnqualComponentName <$> parseName pos args -- | Parse a non-recursive list of fields. @@ -455,18 +457,18 @@ parseFields -> [Field Position] -- ^ fields to be parsed -> ParsecFieldGrammar' a - -> ParseResult a + -> ParseResult src a parseFields v fields grammar = do let (fs0, ss) = partitionFields fields traverse_ (traverse_ warnInvalidSubsection) ss parseFieldGrammar v fs0 grammar -warnInvalidSubsection :: Section Position -> ParseResult () +warnInvalidSubsection :: Section Position -> ParseResult src () warnInvalidSubsection (MkSection (Name pos name) _ _) = void $ parseFailure pos $ "invalid subsection " ++ show name parseCondTree - :: forall a + :: forall src a . L.HasBuildInfo a => CabalSpecVersion -> HasElif @@ -480,7 +482,7 @@ parseCondTree -> (a -> [Dependency]) -- ^ condition extractor -> [Field Position] - -> ParseResult (CondTree ConfVar [Dependency] a) + -> ParseResult src (CondTree ConfVar [Dependency] a) parseCondTree v hasElif grammar commonStanzas fromBuildInfo cond = go where go fields0 = do @@ -494,10 +496,10 @@ parseCondTree v hasElif grammar commonStanzas fromBuildInfo cond = go branches <- concat <$> traverse parseIfs ss return $ endo $ CondNode x (cond x) branches - parseIfs :: [Section Position] -> ParseResult [CondBranch ConfVar [Dependency] a] + parseIfs :: [Section Position] -> ParseResult src [CondBranch ConfVar [Dependency] a] parseIfs [] = return [] - parseIfs (MkSection (Name _ name) test fields : sections) | name == "if" = do - test' <- parseConditionConfVar test + parseIfs (MkSection (Name pos name) test fields : sections) | name == "if" = do + test' <- parseConditionConfVar (startOfSection (incPos 2 pos) test) test fields' <- go fields (elseFields, sections') <- parseElseIfs sections return (CondBranch test' fields' elseFields : sections') @@ -507,7 +509,7 @@ parseCondTree v hasElif grammar commonStanzas fromBuildInfo cond = go parseElseIfs :: [Section Position] - -> ParseResult (Maybe (CondTree ConfVar [Dependency] a), [CondBranch ConfVar [Dependency] a]) + -> ParseResult src (Maybe (CondTree ConfVar [Dependency] a), [CondBranch ConfVar [Dependency] a]) parseElseIfs [] = return (Nothing, []) parseElseIfs (MkSection (Name pos name) args fields : sections) | name == "else" = do unless (null args) $ @@ -516,10 +518,10 @@ parseCondTree v hasElif grammar commonStanzas fromBuildInfo cond = go elseFields <- go fields sections' <- parseIfs sections return (Just elseFields, sections') - parseElseIfs (MkSection (Name _ name) test fields : sections) + parseElseIfs (MkSection (Name pos name) test fields : sections) | hasElif == HasElif , name == "elif" = do - test' <- parseConditionConfVar test + test' <- parseConditionConfVar (startOfSection (incPos 4 pos) test) test fields' <- go fields (elseFields, sections') <- parseElseIfs sections -- we parse an empty 'Fields', to get empty value for a node @@ -530,6 +532,12 @@ parseCondTree v hasElif grammar commonStanzas fromBuildInfo cond = go (,) Nothing <$> parseIfs sections parseElseIfs sections = (,) Nothing <$> parseIfs sections +startOfSection :: Position -> [SectionArg Position] -> Position +-- The case where we have no args is the start of the section +startOfSection defaultPos [] = defaultPos +-- Otherwise the start of the section is the position of the first argument. +startOfSection _ (cond : _) = sectionArgAnn cond + {- Note [Accumulating parser] Note: Outdated a bit @@ -618,7 +626,7 @@ instance FromBuildInfo BenchmarkStanza where fromBuildInfo' _ bi = BenchmarkStanza Nothing Nothing Nothing bi parseCondTreeWithCommonStanzas - :: forall a + :: forall src a . L.HasBuildInfo a => CabalSpecVersion -> ParsecFieldGrammar' a @@ -628,7 +636,7 @@ parseCondTreeWithCommonStanzas -> Map String CondTreeBuildInfo -- ^ common stanzas -> [Field Position] - -> ParseResult (CondTree ConfVar [Dependency] a) + -> ParseResult src (CondTree ConfVar [Dependency] a) parseCondTreeWithCommonStanzas v grammar fromBuildInfo commonStanzas fields = do (fields', endo) <- processImports v fromBuildInfo commonStanzas fields x <- parseCondTree v hasElif grammar commonStanzas fromBuildInfo (view L.targetBuildDepends) fields' @@ -637,7 +645,7 @@ parseCondTreeWithCommonStanzas v grammar fromBuildInfo commonStanzas fields = do hasElif = specHasElif v processImports - :: forall a + :: forall src a . L.HasBuildInfo a => CabalSpecVersion -> (BuildInfo -> a) @@ -645,7 +653,7 @@ processImports -> Map String CondTreeBuildInfo -- ^ common stanzas -> [Field Position] - -> ParseResult ([Field Position], CondTree ConfVar [Dependency] a -> CondTree ConfVar [Dependency] a) + -> ParseResult src ([Field Position], CondTree ConfVar [Dependency] a -> CondTree ConfVar [Dependency] a) processImports v fromBuildInfo commonStanzas = go [] where hasCommonStanzas = specHasCommonStanzas v @@ -677,7 +685,7 @@ processImports v fromBuildInfo commonStanzas = go [] pure $ (fields', \x -> foldr (mergeCommonStanza fromBuildInfo) x acc) -- | Warn on "import" fields, also map to Maybe, so erroneous fields can be filtered -warnImport :: CabalSpecVersion -> Field Position -> ParseResult (Maybe (Field Position)) +warnImport :: CabalSpecVersion -> Field Position -> ParseResult src (Maybe (Field Position)) warnImport v (Field (Name pos name) _) | name == "import" = do if specHasCommonStanzas v == NoCommonStanzas then parseWarning pos PWTUnknownField "Unknown field: import. You should set cabal-version: 2.2 or larger to use common stanzas" @@ -731,7 +739,7 @@ onAllBranches p = go mempty -- -- * don't use undefined flags (very bad) -- * define flags which are unused (just bad) -checkForUndefinedFlags :: GenericPackageDescription -> ParseResult () +checkForUndefinedFlags :: GenericPackageDescription -> ParseResult src () checkForUndefinedFlags gpd = do let definedFlags, usedFlags :: Set.Set FlagName definedFlags = toSetOf (L.genPackageFlags . traverse . getting flagName) gpd @@ -748,7 +756,7 @@ checkForUndefinedFlags gpd = do -- | Since @cabal-version: 1.24@ one can specify @custom-setup@. -- Let us require it. -checkForUndefinedCustomSetup :: GenericPackageDescription -> ParseResult () +checkForUndefinedCustomSetup :: GenericPackageDescription -> ParseResult src () checkForUndefinedCustomSetup gpd = do let pd = packageDescription gpd let csv = specVersion pd @@ -923,7 +931,7 @@ libFieldNames = fieldGrammarKnownFieldList (libraryFieldGrammar LMainLibName) -- Supplementary build information ------------------------------------------------------------------------------- -parseHookedBuildInfo :: BS.ByteString -> ParseResult HookedBuildInfo +parseHookedBuildInfo :: BS.ByteString -> ParseResult src HookedBuildInfo parseHookedBuildInfo bs = case readFields' bs of Right (fs, lexWarnings) -> do parseHookedBuildInfo' lexWarnings fs @@ -933,7 +941,7 @@ parseHookedBuildInfo bs = case readFields' bs of parseHookedBuildInfo' :: [LexWarning] -> [Field Position] - -> ParseResult HookedBuildInfo + -> ParseResult src HookedBuildInfo parseHookedBuildInfo' lexWarnings fs = do parseWarnings (toPWarnings lexWarnings) (mLibFields, exes) <- stanzas fs @@ -941,24 +949,24 @@ parseHookedBuildInfo' lexWarnings fs = do biExes <- traverse parseExe exes return (mLib, biExes) where - parseLib :: Fields Position -> ParseResult (Maybe BuildInfo) + parseLib :: Fields Position -> ParseResult src (Maybe BuildInfo) parseLib fields | Map.null fields = pure Nothing | otherwise = Just <$> parseFieldGrammar cabalSpecLatest fields buildInfoFieldGrammar - parseExe :: (UnqualComponentName, Fields Position) -> ParseResult (UnqualComponentName, BuildInfo) + parseExe :: (UnqualComponentName, Fields Position) -> ParseResult src (UnqualComponentName, BuildInfo) parseExe (n, fields) = do bi <- parseFieldGrammar cabalSpecLatest fields buildInfoFieldGrammar pure (n, bi) - stanzas :: [Field Position] -> ParseResult (Fields Position, [(UnqualComponentName, Fields Position)]) + stanzas :: [Field Position] -> ParseResult src (Fields Position, [(UnqualComponentName, Fields Position)]) stanzas fields = do let (hdr0, exes0) = breakMaybe isExecutableField fields hdr <- toFields hdr0 exes <- unfoldrM (traverse toExe) exes0 pure (hdr, exes) - toFields :: [Field Position] -> ParseResult (Fields Position) + toFields :: [Field Position] -> ParseResult src (Fields Position) toFields fields = do let (fields', ss) = partitionFields fields traverse_ (traverse_ warnInvalidSubsection) ss @@ -966,7 +974,7 @@ parseHookedBuildInfo' lexWarnings fs = do toExe :: ([FieldLine Position], [Field Position]) - -> ParseResult ((UnqualComponentName, Fields Position), Maybe ([FieldLine Position], [Field Position])) + -> ParseResult src ((UnqualComponentName, Fields Position), Maybe ([FieldLine Position], [Field Position])) toExe (fss, fields) = do name <- runFieldParser zeroPos parsec cabalSpecLatest fss let (hdr0, rest) = breakMaybe isExecutableField fields diff --git a/Cabal-syntax/src/Distribution/Parsec.hs b/Cabal-syntax/src/Distribution/Parsec.hs index d0197616fd6..a3f695bc95b 100644 --- a/Cabal-syntax/src/Distribution/Parsec.hs +++ b/Cabal-syntax/src/Distribution/Parsec.hs @@ -24,11 +24,16 @@ module Distribution.Parsec -- ** Warnings , PWarnType (..) , PWarning (..) + , PWarningWithSource (..) + , PSource (..) , showPWarning + , showPWarningWithSource -- ** Errors , PError (..) + , PErrorWithSource (..) , showPError + , showPErrorWithSource -- * Position , Position (..) @@ -58,10 +63,12 @@ import Data.Char (digitToInt, intToDigit) import Data.List (transpose) import Distribution.CabalSpecVersion import Distribution.Compat.Prelude -import Distribution.Parsec.Error (PError (..), showPError) +import Distribution.Parsec.Error (PError (..), PErrorWithSource (..), showPError, showPErrorWithSource) + +import Data.Monoid (Last (..)) import Distribution.Parsec.FieldLineStream (FieldLineStream, fieldLineStreamFromBS, fieldLineStreamFromString) import Distribution.Parsec.Position (Position (..), incPos, retPos, showPos, zeroPos) -import Distribution.Parsec.Warning (PWarnType (..), PWarning (..), showPWarning) +import Distribution.Parsec.Warning import Numeric (showIntAtBase) import Prelude () @@ -255,6 +262,12 @@ instance Parsec Bool where caseWarning = "Boolean values are case sensitive, use 'True' or 'False'." +instance Parsec a => Parsec (Last a) where + parsec = parsecLast + +parsecLast :: (Parsec a, CabalParsing m) => m (Last a) +parsecLast = (Last . Just <$> parsec) <|> pure mempty + -- | @[^ ,]@ parsecToken :: CabalParsing m => m String parsecToken = parsecHaskellString <|> ((P.munch1 (\x -> not (isSpace x) && x /= ',') P. "identifier") >>= checkNotDoubleDash) diff --git a/Cabal-syntax/src/Distribution/Parsec/Error.hs b/Cabal-syntax/src/Distribution/Parsec/Error.hs index 46114c3ea98..8c1d7326b33 100644 --- a/Cabal-syntax/src/Distribution/Parsec/Error.hs +++ b/Cabal-syntax/src/Distribution/Parsec/Error.hs @@ -1,22 +1,34 @@ +{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} module Distribution.Parsec.Error ( PError (..) + , PErrorWithSource (..) , showPError + , showPErrorWithSource ) where import Distribution.Compat.Prelude import Distribution.Parsec.Position +import Distribution.Parsec.Source +import Distribution.Parsec.Warning -- TODO: Move PSource into own module import System.FilePath (normalise) import Prelude () -- | Parser error. -data PError = PError Position String +data PError = PError {perrorPosition :: Position, perrorMessage :: String} deriving (Show, Generic) +data PErrorWithSource src = PErrorWithSource {perrorSource :: !(PSource src), perror :: !PError} + deriving (Show, Generic, Functor) + instance Binary PError instance NFData PError where rnf = genericRnf showPError :: FilePath -> PError -> String showPError fpath (PError pos msg) = normalise fpath ++ ":" ++ showPos pos ++ ": " ++ msg + +showPErrorWithSource :: PErrorWithSource String -> String +showPErrorWithSource (PErrorWithSource source (PError pos msg)) = + showPError (showPSourceAsFilePath source) (PError pos msg) diff --git a/Cabal-syntax/src/Distribution/Parsec/Source.hs b/Cabal-syntax/src/Distribution/Parsec/Source.hs new file mode 100644 index 00000000000..5d662679680 --- /dev/null +++ b/Cabal-syntax/src/Distribution/Parsec/Source.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveGeneric #-} + +module Distribution.Parsec.Source + ( PSource (..) + , CabalFileSource (..) + , InstalledPackageInfoSource (..) + , renderCabalFileSource + , renderInstalledPackageInfoSource + ) where + +import qualified Data.ByteString as BS +import Distribution.Compat.Prelude +import Prelude () + +-- | The source of a parse error +data PSource src + = PKnownSource src + | PUnknownSource + deriving (Ord, Show, Generic, Functor) + +newtype CabalFileSource + = PCabalFile (FilePath, BS.ByteString) + deriving (Ord, Show, Generic) + +data InstalledPackageInfoSource + = PInstalledPackageInfo + deriving (Eq, Ord, Show, Generic) + +renderCabalFileSource :: CabalFileSource -> String +renderCabalFileSource (PCabalFile (path, _)) = path + +renderInstalledPackageInfoSource :: InstalledPackageInfoSource -> String +renderInstalledPackageInfoSource PInstalledPackageInfo = "" + +instance Eq CabalFileSource where + PCabalFile (path, _) == PCabalFile (path', _) = path == path' + +instance Eq src => Eq (PSource src) where + PKnownSource src == PKnownSource src' = src == src' + PUnknownSource == PUnknownSource = True + _ == _ = False + +instance Binary src => Binary (PSource src) +instance NFData src => NFData (PSource src) where rnf = genericRnf diff --git a/Cabal-syntax/src/Distribution/Parsec/Warning.hs b/Cabal-syntax/src/Distribution/Parsec/Warning.hs index ce7ffc4d6e9..65b1471b054 100644 --- a/Cabal-syntax/src/Distribution/Parsec/Warning.hs +++ b/Cabal-syntax/src/Distribution/Parsec/Warning.hs @@ -1,13 +1,19 @@ +{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} module Distribution.Parsec.Warning ( PWarning (..) + , PWarningWithSource (..) + , PSource (..) + , showPSourceAsFilePath , PWarnType (..) , showPWarning + , showPWarningWithSource ) where import Distribution.Compat.Prelude import Distribution.Parsec.Position +import Distribution.Parsec.Source import System.FilePath (normalise) import Prelude () @@ -67,12 +73,25 @@ instance Binary PWarnType instance NFData PWarnType where rnf = genericRnf -- | Parser warning. -data PWarning = PWarning !PWarnType !Position String +data PWarning = PWarning {pwarningType :: !PWarnType, pwarningPosition :: !Position, pwarningMessage :: !String} deriving (Eq, Ord, Show, Generic) +data PWarningWithSource src = PWarningWithSource {pwarningSource :: !(PSource src), pwarning :: !PWarning} + deriving (Eq, Ord, Show, Generic, Functor) + instance Binary PWarning instance NFData PWarning where rnf = genericRnf showPWarning :: FilePath -> PWarning -> String showPWarning fpath (PWarning _ pos msg) = normalise fpath ++ ":" ++ showPos pos ++ ": " ++ msg + +showPWarningWithSource :: PWarningWithSource String -> String +showPWarningWithSource (PWarningWithSource source pwarn) = + showPWarning (showPSourceAsFilePath source) pwarn + +showPSourceAsFilePath :: PSource String -> String +showPSourceAsFilePath source = + case source of + PKnownSource src -> src + PUnknownSource -> "???" diff --git a/Cabal-tests/tests/CheckTests.hs b/Cabal-tests/tests/CheckTests.hs index aa2f1e9b041..dfc716f6e33 100644 --- a/Cabal-tests/tests/CheckTests.hs +++ b/Cabal-tests/tests/CheckTests.hs @@ -6,10 +6,11 @@ import Test.Tasty import Test.Tasty.Golden.Advanced (goldenTest) import Data.Algorithm.Diff (PolyDiff (..), getGroupedDiff) -import Distribution.Fields (runParseResult) +import Distribution.Fields.ParseResult import Distribution.PackageDescription.Check (checkPackage) import Distribution.PackageDescription.Parsec (parseGenericPackageDescription) import Distribution.Parsec +import Distribution.Parsec.Source import Distribution.Utils.Generic (fromUTF8BS, toUTF8BS) import System.Directory (setCurrentDirectory) import System.Environment (getArgs, withArgs) @@ -62,16 +63,16 @@ checkTests = testGroup "regressions" checkTest :: FilePath -> TestTree checkTest fp = cabalGoldenTest fp correct $ do contents <- BS.readFile input - let res = parseGenericPackageDescription contents + let res = withSource (PCabalFile (fp, contents)) $ parseGenericPackageDescription contents let (ws, x) = runParseResult res return $ toUTF8BS $ case x of Right gpd -> -- Note: parser warnings are reported by `cabal check`, but not by -- D.PD.Check functionality. - unlines (map (showPWarning fp) ws) ++ + unlines (map (showPWarningWithSource . fmap renderCabalFileSource) ws) ++ unlines (map show (checkPackage gpd)) - Left (_, errs) -> unlines $ map (("ERROR: " ++) . showPError fp) $ NE.toList errs + Left (_, errs) -> unlines $ map (("ERROR: " ++) . showPErrorWithSource . fmap renderCabalFileSource) $ NE.toList errs where input = "tests" "ParserTests" "regressions" fp correct = replaceExtension input "check" diff --git a/Cabal-tests/tests/HackageTests.hs b/Cabal-tests/tests/HackageTests.hs index dbad37c30f4..1265c6cb13e 100644 --- a/Cabal-tests/tests/HackageTests.hs +++ b/Cabal-tests/tests/HackageTests.hs @@ -24,6 +24,8 @@ import Distribution.PackageDescription.Check (PackageCheck (..), checkPack import Distribution.PackageDescription.PrettyPrint (showGenericPackageDescription) import Distribution.PackageDescription.Quirks (patchQuirks) import Distribution.Simple.Utils (fromUTF8BS, toUTF8BS) +import Distribution.Fields.ParseResult +import Distribution.Parsec.Source import Numeric (showFFloat) import System.Directory (getXdgDirectory, XdgDirectory(XdgCache, XdgConfig), getAppUserDataDirectory, doesDirectoryExist) import System.Environment (lookupEnv) @@ -152,7 +154,7 @@ readFieldTest fpath bs = case Parsec.readFields bs' of parseParsecTest :: Bool -> FilePath -> B.ByteString -> IO ParsecResult parseParsecTest keepGoing fpath bs = do let (warnings, result) = Parsec.runParseResult $ - Parsec.parseGenericPackageDescription bs + withSource (PCabalFile (fpath, bs)) $ Parsec.parseGenericPackageDescription bs let w | null warnings = 0 | otherwise = 1 @@ -163,10 +165,10 @@ parseParsecTest keepGoing fpath bs = do return (ParsecResult 1 w 0) Left (_, errors) | keepGoing -> do - traverse_ (putStrLn . Parsec.showPError fpath) errors + traverse_ (putStrLn . Parsec.showPErrorWithSource . fmap renderCabalFileSource) errors return (ParsecResult 1 w 1) | otherwise -> do - traverse_ (putStrLn . Parsec.showPError fpath) errors + traverse_ (putStrLn . Parsec.showPErrorWithSource . fmap renderCabalFileSource) errors exitFailure -- | A hook to make queries on Hackage @@ -197,7 +199,7 @@ instance NFData ParsecResult where parseCheckTest :: FilePath -> B.ByteString -> IO CheckResult parseCheckTest fpath bs = do let (warnings, parsec) = Parsec.runParseResult $ - Parsec.parseGenericPackageDescription bs + withSource (PCabalFile (fpath, bs)) $ Parsec.parseGenericPackageDescription bs case parsec of Right gpd -> do let checks = checkPackage gpd @@ -210,7 +212,7 @@ parseCheckTest fpath bs = do -- one for file, many checks return (CheckResult 1 (w warnings) 0 0 0 0 0 0 <> foldMap toCheckResult checks) Left (_, errors) -> do - traverse_ (putStrLn . Parsec.showPError fpath) errors + traverse_ (putStrLn . Parsec.showPErrorWithSource . fmap renderCabalFileSource) errors exitFailure -- checkCppFlags :: BuildInfo -> IO BuildInfo @@ -304,7 +306,7 @@ roundtripTest testFieldsTransform fpath bs = do {- FOURMOLU_DISABLE -} parse phase c = do let (_, x') = Parsec.runParseResult $ - Parsec.parseGenericPackageDescription c + withSource (PCabalFile (fpath, c)) $ Parsec.parseGenericPackageDescription c case x' of Right gpd -> pure gpd Left (_, errs) -> do diff --git a/Cabal-tests/tests/NoThunks.hs b/Cabal-tests/tests/NoThunks.hs index 6a81475dc03..a53d404dd1e 100644 --- a/Cabal-tests/tests/NoThunks.hs +++ b/Cabal-tests/tests/NoThunks.hs @@ -21,7 +21,8 @@ import Distribution.Compat.NonEmptySet (NonEmptySet) import Distribution.Compiler (CompilerFlavor, PerCompilerFlavor) import Distribution.Fields (runParseResult) import Distribution.ModuleName (ModuleName) -import Distribution.PackageDescription.Parsec (parseGenericPackageDescription) +import Distribution.PackageDescription.Parsec (parseGenericPackageDescription, withSource) +import Distribution.Parsec.Source import Distribution.SPDX (License, LicenseExceptionId, LicenseExpression, LicenseId, LicenseRef, SimpleLicenseExpression) import Distribution.System (Arch, OS) import Distribution.Utils.Path (SymbolicPathX) @@ -45,7 +46,7 @@ main = defaultMain $ testGroup "nothunks" noThunksParse :: IO () noThunksParse = do bs <- BS.readFile "Cabal/Cabal.cabal" <|> BS.readFile "../Cabal/Cabal.cabal" - let res = parseGenericPackageDescription bs + let res = withSource (PCabalFile ("Cabal.cabal", bs)) $ parseGenericPackageDescription bs gpd <- either (assertFailure . show) return $ snd $ runParseResult res diff --git a/Cabal-tests/tests/ParserTests.hs b/Cabal-tests/tests/ParserTests.hs index 3990f19fc79..8368ed19451 100644 --- a/Cabal-tests/tests/ParserTests.hs +++ b/Cabal-tests/tests/ParserTests.hs @@ -13,16 +13,18 @@ import Test.Tasty.HUnit import Control.Monad (unless, void) import Data.Algorithm.Diff (PolyDiff (..), getGroupedDiff) import Data.Maybe (isNothing) -import Distribution.Fields (runParseResult) +import Distribution.Fields (pwarning) import Distribution.PackageDescription (GenericPackageDescription) import Distribution.PackageDescription.Parsec (parseGenericPackageDescription) import Distribution.PackageDescription.PrettyPrint (showGenericPackageDescription) -import Distribution.Parsec (PWarnType (..), PWarning (..), showPError, showPWarning) +import Distribution.Parsec (PWarnType (..), PWarning (..), showPErrorWithSource, showPWarningWithSource) import Distribution.Pretty (prettyShow) +import Distribution.Fields.ParseResult import Distribution.Utils.Generic (fromUTF8BS, toUTF8BS) import System.Directory (setCurrentDirectory) import System.Environment (getArgs, withArgs) import System.FilePath (replaceExtension, ()) +import Distribution.Parsec.Source import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BS8 @@ -79,12 +81,12 @@ warningTest :: PWarnType -> FilePath -> TestTree warningTest wt fp = testCase (show wt) $ do contents <- BS.readFile $ "tests" "ParserTests" "warnings" fp - let res = parseGenericPackageDescription contents + let res = withSource (PCabalFile (fp, contents)) $ parseGenericPackageDescription contents let (warns, x) = runParseResult res assertBool ("should parse successfully: " ++ show x) $ isRight x - case warns of + case map pwarning warns of [PWarning wt' _ _] -> assertEqual "warning type" wt wt' [] -> assertFailure "got no warnings" _ -> assertFailure $ "got multiple warnings: " ++ show warns @@ -135,7 +137,7 @@ errorTests = testGroup "errors" errorTest :: FilePath -> TestTree errorTest fp = cabalGoldenTest fp correct $ do contents <- BS.readFile input - let res = parseGenericPackageDescription contents + let res = withSource (PCabalFile (fp, contents)) $ parseGenericPackageDescription contents let (_, x) = runParseResult res return $ toUTF8BS $ case x of @@ -143,7 +145,7 @@ errorTest fp = cabalGoldenTest fp correct $ do "UNEXPECTED SUCCESS\n" ++ showGenericPackageDescription gpd Left (v, errs) -> - unlines $ ("VERSION: " ++ show v) : map (showPError fp) (NE.toList errs) + unlines $ ("VERSION: " ++ show v) : map (showPErrorWithSource . fmap renderCabalFileSource) (NE.toList errs) where input = "tests" "ParserTests" "errors" fp correct = replaceExtension input "errors" @@ -212,18 +214,18 @@ regressionTest fp = testGroup fp formatGoldenTest :: FilePath -> TestTree formatGoldenTest fp = cabalGoldenTest "format" correct $ do contents <- BS.readFile input - let res = parseGenericPackageDescription contents + let res = withSource (PCabalFile (fp, contents)) $ parseGenericPackageDescription contents let (warns, x) = runParseResult res return $ toUTF8BS $ case x of Right gpd -> - unlines (map (showPWarning fp) warns) + unlines (map (showPWarningWithSource . fmap renderCabalFileSource) warns) ++ showGenericPackageDescription gpd Left (csv, errs) -> unlines $ "ERROR" : maybe "unknown-version" prettyShow csv : - map (showPError fp) (NE.toList errs) + map (showPErrorWithSource . fmap renderCabalFileSource) (NE.toList errs) where input = "tests" "ParserTests" "regressions" fp correct = replaceExtension input "format" @@ -232,11 +234,11 @@ formatGoldenTest fp = cabalGoldenTest "format" correct $ do treeDiffGoldenTest :: FilePath -> TestTree treeDiffGoldenTest fp = ediffGolden goldenTest "expr" exprFile $ do contents <- BS.readFile input - let res = parseGenericPackageDescription contents + let res = withSource (PCabalFile (fp, contents)) $ parseGenericPackageDescription contents let (_, x) = runParseResult res case x of Right gpd -> pure (toExpr gpd) - Left (_, errs) -> fail $ unlines $ "ERROR" : map (showPError fp) (NE.toList errs) + Left (_, errs) -> fail $ unlines $ "ERROR" : map (showPErrorWithSource . fmap renderCabalFileSource) (NE.toList errs) where input = "tests" "ParserTests" "regressions" fp exprFile = replaceExtension input "expr" @@ -269,11 +271,11 @@ formatRoundTripTest fp = testCase "roundtrip" $ do where parse :: BS.ByteString -> IO GenericPackageDescription parse c = do - let (_, x') = runParseResult $ parseGenericPackageDescription c + let (_, x') = runParseResult $ withSource (PCabalFile (fp, c)) $ parseGenericPackageDescription c case x' of Right gpd -> pure gpd Left (_, errs) -> do - void $ assertFailure $ unlines (map (showPError fp) $ NE.toList errs) + void $ assertFailure $ unlines (map (showPErrorWithSource . fmap renderCabalFileSource) $ NE.toList errs) fail "failure" input = "tests" "ParserTests" "regressions" fp {- FOURMOLU_ENABLE -} diff --git a/Cabal/src/Distribution/Simple/Compiler.hs b/Cabal/src/Distribution/Simple/Compiler.hs index 6c81b50f891..543ff1e0083 100644 --- a/Cabal/src/Distribution/Simple/Compiler.hs +++ b/Cabal/src/Distribution/Simple/Compiler.hs @@ -50,6 +50,7 @@ module Distribution.Simple.Compiler , interpretPackageDBStack , coercePackageDB , coercePackageDBStack + , readPackageDb -- * Support for optimisation levels , OptimisationLevel (..) @@ -93,7 +94,9 @@ module Distribution.Simple.Compiler , showProfDetailLevel ) where +import Distribution.Compat.CharParsing import Distribution.Compat.Prelude +import Distribution.Parsec import Distribution.Pretty import Prelude () @@ -104,6 +107,7 @@ import Distribution.Version import Language.Haskell.Extension +import Data.Bool (bool) import qualified Data.Map as Map (lookup) import System.Directory (canonicalizePath) @@ -203,6 +207,15 @@ data PackageDBX fp instance Binary fp => Binary (PackageDBX fp) instance Structured fp => Structured (PackageDBX fp) +-- | Parse a PackageDB stack entry +-- +-- @since 3.7.0.0 +readPackageDb :: String -> Maybe PackageDB +readPackageDb "clear" = Nothing +readPackageDb "global" = Just GlobalPackageDB +readPackageDb "user" = Just UserPackageDB +readPackageDb other = Just (SpecificPackageDB (makeSymbolicPath other)) + -- | We typically get packages from several databases, and stack them -- together. This type lets us be explicit about that stacking. For example -- typical stacks include: @@ -294,20 +307,36 @@ data OptimisationLevel instance Binary OptimisationLevel instance Structured OptimisationLevel +instance Parsec OptimisationLevel where + parsec = parsecOptimisationLevel + +parsecOptimisationLevel :: CabalParsing m => m OptimisationLevel +parsecOptimisationLevel = boolParser <|> intParser + where + boolParser = bool NoOptimisation NormalOptimisation <$> parsec + intParser = intToOptimisationLevel <$> integral + flagToOptimisationLevel :: Maybe String -> OptimisationLevel flagToOptimisationLevel Nothing = NormalOptimisation flagToOptimisationLevel (Just s) = case reads s of - [(i, "")] - | i >= fromEnum (minBound :: OptimisationLevel) - && i <= fromEnum (maxBound :: OptimisationLevel) -> - toEnum i - | otherwise -> - error $ - "Bad optimisation level: " - ++ show i - ++ ". Valid values are 0..2" + [(i, "")] -> intToOptimisationLevel i _ -> error $ "Can't parse optimisation level " ++ s +intToOptimisationLevel :: Int -> OptimisationLevel +intToOptimisationLevel i + | i >= minLevel && i <= maxLevel = toEnum i + | otherwise = + error $ + "Bad optimisation level: " + ++ show i + ++ ". Valid values are " + ++ show minLevel + ++ ".." + ++ show maxLevel + where + minLevel = fromEnum (minBound :: OptimisationLevel) + maxLevel = fromEnum (maxBound :: OptimisationLevel) + -- ------------------------------------------------------------ -- * Debug info levels @@ -327,6 +356,12 @@ data DebugInfoLevel instance Binary DebugInfoLevel instance Structured DebugInfoLevel +instance Parsec DebugInfoLevel where + parsec = parsecDebugInfoLevel + +parsecDebugInfoLevel :: CabalParsing m => m DebugInfoLevel +parsecDebugInfoLevel = flagToDebugInfoLevel . pure <$> parsecToken + flagToDebugInfoLevel :: Maybe String -> DebugInfoLevel flagToDebugInfoLevel Nothing = NormalDebugInfo flagToDebugInfoLevel (Just s) = case reads s of @@ -574,6 +609,12 @@ data ProfDetailLevel instance Binary ProfDetailLevel instance Structured ProfDetailLevel +instance Parsec ProfDetailLevel where + parsec = parsecProfDetailLevel + +parsecProfDetailLevel :: CabalParsing m => m ProfDetailLevel +parsecProfDetailLevel = flagToProfDetailLevel <$> parsecToken + flagToProfDetailLevel :: String -> ProfDetailLevel flagToProfDetailLevel "" = ProfDetailDefault flagToProfDetailLevel s = diff --git a/Cabal/src/Distribution/Simple/InstallDirs.hs b/Cabal/src/Distribution/Simple/InstallDirs.hs index 41143ea36ee..818b9273bfd 100644 --- a/Cabal/src/Distribution/Simple/InstallDirs.hs +++ b/Cabal/src/Distribution/Simple/InstallDirs.hs @@ -2,6 +2,7 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} ----------------------------------------------------------------------------- @@ -44,6 +45,7 @@ module Distribution.Simple.InstallDirs , compilerTemplateEnv , packageTemplateEnv , abiTemplateEnv + , installDirsGrammar , installDirsTemplateEnv ) where @@ -51,9 +53,13 @@ import Distribution.Compat.Prelude import Prelude () import Distribution.Compat.Environment (lookupEnv) +import Distribution.Compat.Lens (Lens') import Distribution.Compiler +import Distribution.FieldGrammar import Distribution.Package +import Distribution.Parsec import Distribution.Pretty +import Distribution.Simple.Flag import Distribution.Simple.InstallDirs.Internal import Distribution.System @@ -506,6 +512,12 @@ instance Read PathTemplate where , (template, "") <- reads path ] +instance Parsec PathTemplate where + parsec = parsecPathTemplate + +parsecPathTemplate :: CabalParsing m => m PathTemplate +parsecPathTemplate = toPathTemplate <$> parsecFilePath + -- --------------------------------------------------------------------------- -- Internal utilities @@ -552,3 +564,81 @@ foreign import CALLCONV unsafe "shlobj.h SHGetFolderPathW" -> Prelude.IO CInt #endif {- FOURMOLU_ENABLE -} + +-- --------------------------------------------------------------------------- +-- FieldGrammar + +installDirsGrammar :: ParsecFieldGrammar' (InstallDirs (Flag PathTemplate)) +installDirsGrammar = + InstallDirs + <$> optionalFieldDef "prefix" installDirsPrefixLens mempty + <*> optionalFieldDef "bindir" installDirsBindirLens mempty + <*> optionalFieldDef "libdir" installDirsLibdirLens mempty + <*> optionalFieldDef "libsubdir" installDirsLibsubdirLens mempty + <*> optionalFieldDef "dynlibdir" installDirsDynlibdirLens mempty + <*> pure NoFlag -- flibdir + <*> optionalFieldDef "libexecdir" installDirsLibexecdirLens mempty + <*> optionalFieldDef "libexecsubdir" installDirsLibexecsubdirLens mempty + <*> pure NoFlag -- includedir + <*> optionalFieldDef "datadir" installDirsDatadirLens mempty + <*> optionalFieldDef "datasubdir" installDirsDatasubdirLens mempty + <*> optionalFieldDef "docdir" installDirsDocdirLens mempty + <*> pure NoFlag -- mandir + <*> optionalFieldDef "htmldir" installDirsHtmldirLens mempty + <*> optionalFieldDef "haddockdir" installDirsHaddockdirLens mempty + <*> optionalFieldDef "sysconfdir" installDirsSysconfdirLens mempty + +-- --------------------------------------------------------------------------- +-- Lenses + +installDirsPrefixLens :: Lens' (InstallDirs a) a +installDirsPrefixLens f c = fmap (\x -> c{prefix = x}) (f (prefix c)) +{-# INLINEABLE installDirsPrefixLens #-} + +installDirsBindirLens :: Lens' (InstallDirs a) a +installDirsBindirLens f c = fmap (\x -> c{bindir = x}) (f (bindir c)) +{-# INLINEABLE installDirsBindirLens #-} + +installDirsLibdirLens :: Lens' (InstallDirs a) a +installDirsLibdirLens f c = fmap (\x -> c{libdir = x}) (f (libdir c)) +{-# INLINEABLE installDirsLibdirLens #-} + +installDirsLibsubdirLens :: Lens' (InstallDirs a) a +installDirsLibsubdirLens f c = fmap (\x -> c{libsubdir = x}) (f (libsubdir c)) +{-# INLINEABLE installDirsLibsubdirLens #-} + +installDirsDynlibdirLens :: Lens' (InstallDirs a) a +installDirsDynlibdirLens f c = fmap (\x -> c{dynlibdir = x}) (f (dynlibdir c)) +{-# INLINEABLE installDirsDynlibdirLens #-} + +installDirsLibexecdirLens :: Lens' (InstallDirs a) a +installDirsLibexecdirLens f c = fmap (\x -> c{libexecdir = x}) (f (libexecdir c)) +{-# INLINEABLE installDirsLibexecdirLens #-} + +installDirsLibexecsubdirLens :: Lens' (InstallDirs a) a +installDirsLibexecsubdirLens f c = fmap (\x -> c{libexecsubdir = x}) (f (libexecsubdir c)) +{-# INLINEABLE installDirsLibexecsubdirLens #-} + +installDirsDatadirLens :: Lens' (InstallDirs a) a +installDirsDatadirLens f c = fmap (\x -> c{datadir = x}) (f (datadir c)) +{-# INLINEABLE installDirsDatadirLens #-} + +installDirsDatasubdirLens :: Lens' (InstallDirs a) a +installDirsDatasubdirLens f c = fmap (\x -> c{datasubdir = x}) (f (datasubdir c)) +{-# INLINEABLE installDirsDatasubdirLens #-} + +installDirsDocdirLens :: Lens' (InstallDirs a) a +installDirsDocdirLens f c = fmap (\x -> c{docdir = x}) (f (docdir c)) +{-# INLINEABLE installDirsDocdirLens #-} + +installDirsHtmldirLens :: Lens' (InstallDirs a) a +installDirsHtmldirLens f c = fmap (\x -> c{htmldir = x}) (f (htmldir c)) +{-# INLINEABLE installDirsHtmldirLens #-} + +installDirsHaddockdirLens :: Lens' (InstallDirs a) a +installDirsHaddockdirLens f c = fmap (\x -> c{haddockdir = x}) (f (haddockdir c)) +{-# INLINEABLE installDirsHaddockdirLens #-} + +installDirsSysconfdirLens :: Lens' (InstallDirs a) a +installDirsSysconfdirLens f c = fmap (\x -> c{sysconfdir = x}) (f (sysconfdir c)) +{-# INLINEABLE installDirsSysconfdirLens #-} diff --git a/Cabal/src/Distribution/Simple/PackageDescription.hs b/Cabal/src/Distribution/Simple/PackageDescription.hs index d0ee9d9f86b..e0f1806d6a1 100644 --- a/Cabal/src/Distribution/Simple/PackageDescription.hs +++ b/Cabal/src/Distribution/Simple/PackageDescription.hs @@ -18,6 +18,8 @@ module Distribution.Simple.PackageDescription -- * Utility Parsing function , parseString + , readAndParseFile + , flattenDups ) where import Distribution.Compat.Prelude @@ -31,11 +33,13 @@ import Distribution.PackageDescription.Parsec ( parseGenericPackageDescription , parseHookedBuildInfo ) -import Distribution.Parsec.Error (showPError) +import Distribution.Parsec.Error (showPErrorWithSource) +import Distribution.Parsec.Source import Distribution.Parsec.Warning ( PWarnType (PWTExperimental) , PWarning (..) - , showPWarning + , PWarningWithSource (..) + , showPWarningWithSource ) import Distribution.Simple.Errors import Distribution.Simple.Utils (dieWithException, equating, warn) @@ -70,7 +74,7 @@ readHookedBuildInfo = -- -- Argument order is chosen to encourage partial application. readAndParseFile - :: (BS.ByteString -> ParseResult a) + :: (BS.ByteString -> ParseResult CabalFileSource a) -- ^ File contents to final value parser -> Verbosity -- ^ Verbosity level @@ -90,7 +94,7 @@ readAndParseFile parser verbosity mbWorkDir fpath = do parseString parser verbosity upath bs parseString - :: (BS.ByteString -> ParseResult a) + :: (BS.ByteString -> ParseResult CabalFileSource a) -- ^ File contents to final value parser -> Verbosity -- ^ Verbosity level @@ -99,38 +103,41 @@ parseString -> BS.ByteString -> IO a parseString parser verbosity name bs = do - let (warnings, result) = runParseResult (parser bs) - traverse_ (warn verbosity . showPWarning name) (flattenDups verbosity warnings) + let (warnings, result) = runParseResult $ withSource (PCabalFile (name, bs)) (parser bs) + traverse_ (warn verbosity . showPWarningWithSource . fmap renderCabalFileSource) (flattenDups verbosity warnings) case result of Right x -> return x Left (_, errors) -> do - traverse_ (warn verbosity . showPError name) errors + traverse_ (warn verbosity . showPErrorWithSource . fmap renderCabalFileSource) errors dieWithException verbosity $ FailedParsing name -- | Collapse duplicate experimental feature warnings into single warning, with -- a count of further sites -flattenDups :: Verbosity -> [PWarning] -> [PWarning] +flattenDups :: Verbosity -> [PWarningWithSource src] -> [PWarningWithSource src] flattenDups verbosity ws | verbosity <= normal = rest ++ experimentals | otherwise = ws -- show all instances where - (exps, rest) = partition (\(PWarning w _ _) -> w == PWTExperimental) ws + (exps, rest) = partition (\(PWarningWithSource _ (PWarning w _ _)) -> w == PWTExperimental) ws experimentals = concatMap flatCount - . groupBy (equating warningStr) - . sortBy (comparing warningStr) + . groupBy (equating (warningStr . pwarning)) + . sortBy (comparing (warningStr . pwarning)) $ exps warningStr (PWarning _ _ w) = w -- flatten if we have 3 or more examples - flatCount :: [PWarning] -> [PWarning] + flatCount :: [PWarningWithSource src] -> [PWarningWithSource src] flatCount w@[] = w flatCount w@[_] = w flatCount w@[_, _] = w - flatCount (PWarning t pos w : xs) = - [ PWarning - t - pos - (w <> printf " (and %d more occurrences)" (length xs)) + flatCount (PWarningWithSource source (PWarning t pos w) : xs) = + [ PWarningWithSource + source + ( PWarning + t + pos + (w <> printf " (and %d more occurrences)" (length xs)) + ) ] diff --git a/Cabal/src/Distribution/Simple/Setup/Config.hs b/Cabal/src/Distribution/Simple/Setup/Config.hs index 729b466b4da..67945bd4d43 100644 --- a/Cabal/src/Distribution/Simple/Setup/Config.hs +++ b/Cabal/src/Distribution/Simple/Setup/Config.hs @@ -890,15 +890,6 @@ configureOptions showOrParseArgs = readPackageDbList :: String -> [Maybe PackageDB] readPackageDbList str = [readPackageDb str] --- | Parse a PackageDB stack entry --- --- @since 3.7.0.0 -readPackageDb :: String -> Maybe PackageDB -readPackageDb "clear" = Nothing -readPackageDb "global" = Just GlobalPackageDB -readPackageDb "user" = Just UserPackageDB -readPackageDb other = Just (SpecificPackageDB (makeSymbolicPath other)) - showPackageDbList :: [Maybe PackageDB] -> [String] showPackageDbList = map showPackageDb diff --git a/Cabal/src/Distribution/Types/DumpBuildInfo.hs b/Cabal/src/Distribution/Types/DumpBuildInfo.hs index 99020febc35..0e9cb8ce8d5 100644 --- a/Cabal/src/Distribution/Types/DumpBuildInfo.hs +++ b/Cabal/src/Distribution/Types/DumpBuildInfo.hs @@ -5,6 +5,7 @@ module Distribution.Types.DumpBuildInfo ) where import Distribution.Compat.Prelude +import Distribution.Parsec data DumpBuildInfo = NoDumpBuildInfo @@ -13,3 +14,12 @@ data DumpBuildInfo instance Binary DumpBuildInfo instance Structured DumpBuildInfo + +instance Parsec DumpBuildInfo where + parsec = parsecDumpBuildInfo + +parsecDumpBuildInfo :: CabalParsing m => m DumpBuildInfo +parsecDumpBuildInfo = boolToDumpBuildInfo <$> parsec + +boolToDumpBuildInfo :: Bool -> DumpBuildInfo +boolToDumpBuildInfo bool = if bool then DumpBuildInfo else NoDumpBuildInfo diff --git a/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs b/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs index be6ded8cff2..257ba808aaa 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs @@ -9,9 +9,11 @@ module Distribution.Solver.Types.ProjectConfigPath , nullProjectConfigPath , consProjectConfigPath , unconsProjectConfigPath + , currentProjectConfigPath -- * Messages , docProjectConfigPath + , docProjectImportedBy , docProjectConfigFiles , cyclicalImportMsg , untrimmedUriImportMsg @@ -131,6 +133,13 @@ docProjectConfigPath (ProjectConfigPath (p :| [])) = quoteUntrimmed p docProjectConfigPath (ProjectConfigPath (p :| ps)) = vcat $ quoteUntrimmed p : [ text " " <+> text "imported by:" <+> quoteUntrimmed l | l <- ps ] +-- | Render the paths which imports this config. +docProjectImportedBy :: ProjectConfigPath -> Doc +docProjectImportedBy (ProjectConfigPath (_ :| [])) = text "" +docProjectImportedBy (ProjectConfigPath (_ :| ps)) = vcat $ + [ text " " <+> text "imported by:" <+> quoteUntrimmed l | l <- ps ] + + -- | If the path has leading or trailing spaces then show it quoted. quoteUntrimmed :: FilePath -> Doc quoteUntrimmed s = if trim s /= s then quotes (text s) else text s @@ -237,6 +246,9 @@ consProjectConfigPath p ps = ProjectConfigPath (p <| coerce ps) unconsProjectConfigPath :: ProjectConfigPath -> (FilePath, Maybe ProjectConfigPath) unconsProjectConfigPath ps = fmap ProjectConfigPath <$> NE.uncons (coerce ps) +currentProjectConfigPath :: ProjectConfigPath -> FilePath +currentProjectConfigPath (ProjectConfigPath (p :| _)) = p + -- | Make paths relative to the directory of the root of the project, not -- relative to the file they were imported from. makeRelativeConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath diff --git a/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs b/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs index 4b7fe65b769..306c0c12185 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs @@ -105,3 +105,26 @@ instance Parsec OnlyConstrained where , P.string "none" >> return OnlyConstrainedNone ] +instance Parsec ReorderGoals where + parsec = ReorderGoals <$> parsec + +instance Parsec CountConflicts where + parsec = CountConflicts <$> parsec + +instance Parsec FineGrainedConflicts where + parsec = FineGrainedConflicts <$> parsec + +instance Parsec MinimizeConflictSet where + parsec = MinimizeConflictSet <$> parsec + +instance Parsec StrongFlags where + parsec = StrongFlags <$> parsec + +instance Parsec AllowBootLibInstalls where + parsec = AllowBootLibInstalls <$> parsec + +instance Parsec PreferOldest where + parsec = PreferOldest <$> parsec + +instance Parsec IndependentGoals where + parsec = IndependentGoals <$> parsec diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal index 8ace48c0a55..a8c97dab7fc 100644 --- a/cabal-install/cabal-install.cabal +++ b/cabal-install/cabal-install.cabal @@ -44,6 +44,11 @@ flag git-rev default: False manual: True +flag legacy-comparison + description: Enable comparison between the new and legacy cabal.project parser + default: False + manual: True + common warnings ghc-options: -Wall @@ -131,6 +136,7 @@ library Distribution.Client.Dependency.Types Distribution.Client.DistDirLayout Distribution.Client.Errors + Distribution.Client.Errors.Parser Distribution.Client.Fetch Distribution.Client.FetchUtils Distribution.Client.FileMonitor @@ -177,7 +183,10 @@ library Distribution.Client.ProjectBuilding.PackageFileMonitor Distribution.Client.ProjectBuilding.Types Distribution.Client.ProjectConfig + Distribution.Client.ProjectConfig.FieldGrammar Distribution.Client.ProjectConfig.Legacy + Distribution.Client.ProjectConfig.Lens + Distribution.Client.ProjectConfig.Parsec Distribution.Client.ProjectConfig.Types Distribution.Client.ProjectFlags Distribution.Client.ProjectOrchestration @@ -225,6 +234,7 @@ library Distribution.Client.Upload Distribution.Client.Utils Distribution.Client.Utils.Json + Distribution.Client.Utils.Newtypes Distribution.Client.Utils.Parsec Distribution.Client.VCS Distribution.Client.Version @@ -290,6 +300,9 @@ library build-depends: githash ^>= 0.1.7.0 cpp-options: -DGIT_REV + if flag(legacy-comparison) + cpp-options: -DLEGACY_COMPARISON + executable cabal import: warnings, base-dep main-is: Main.hs @@ -378,6 +391,26 @@ test-suite unit-tests , tree-diff , QuickCheck >= 2.14.3 && <2.17 +-- Tests for the project file parser +test-suite parser-tests + import: warnings, base-dep, cabal-dep, cabal-syntax-dep, cabal-install-solver-dep + default-language: Haskell2010 + ghc-options: -rtsopts -threaded + + type: exitcode-stdio-1.0 + main-is: Tests.hs + hs-source-dirs: parser-tests + build-depends: + , cabal-install + , containers + , directory + , filepath + , network-uri >= 2.6.2.0 && <2.7 + , tasty >= 1.2.3 && <1.6 + , tasty-hunit >= 0.10 + other-modules: + Tests.ParserTests + -- Tests to run with a limited stack and heap size -- The test suite name must be keep short cause a longer one -- could make the build generating paths which exceeds the windows diff --git a/cabal-install/parser-tests/Tests.hs b/cabal-install/parser-tests/Tests.hs new file mode 100644 index 00000000000..f5655ad5389 --- /dev/null +++ b/cabal-install/parser-tests/Tests.hs @@ -0,0 +1,7 @@ +module Main where + +import Test.Tasty (defaultMain) +import Tests.ParserTests (parserTests) + +main :: IO () +main = defaultMain parserTests diff --git a/cabal-install/parser-tests/Tests/ParserTests.hs b/cabal-install/parser-tests/Tests/ParserTests.hs new file mode 100644 index 00000000000..d9ab2f5247f --- /dev/null +++ b/cabal-install/parser-tests/Tests/ParserTests.hs @@ -0,0 +1,624 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} + +-- | Tests for the project file parser +module Tests.ParserTests (parserTests) where + +import Control.Monad.IO.Class + ( MonadIO (liftIO) + ) +import Data.Either (fromRight) +import qualified Data.Map as Map +import Data.Maybe (fromJust) +import qualified Data.Set as Set +import Distribution.Client.BuildReports.Types (ReportLevel (..)) +import Distribution.Client.CmdInstall.ClientInstallFlags (ClientInstallFlags (..)) +import Distribution.Client.Dependency.Types (PreSolver (..)) +import Distribution.Client.DistDirLayout +import Distribution.Client.HttpUtils +import Distribution.Client.IndexUtils.ActiveRepos (ActiveRepoEntry (..), ActiveRepos (..), CombineStrategy (..)) +import Distribution.Client.IndexUtils.IndexState (RepoIndexState (..), headTotalIndexState, insertIndexState) +import Distribution.Client.ProjectConfig +import Distribution.Client.RebuildMonad (runRebuild) +import Distribution.Client.Targets (readUserConstraint) +import Distribution.Client.Types.AllowNewer (AllowNewer (..), AllowOlder (..), RelaxDepMod (..), RelaxDepScope (..), RelaxDepSubject (..), RelaxDeps (..), RelaxedDep (..)) +import Distribution.Client.Types.InstallMethod (InstallMethod (..)) +import Distribution.Client.Types.OverwritePolicy (OverwritePolicy (..)) +import Distribution.Client.Types.Repo (LocalRepo (..), RemoteRepo (..), asPosixPath) +import Distribution.Client.Types.RepoName (RepoName (..)) +import Distribution.Client.Types.SourceRepo +import Distribution.Client.Types.WriteGhcEnvironmentFilesPolicy (WriteGhcEnvironmentFilesPolicy (..)) +import Distribution.Compat.Prelude +import Distribution.Compiler (CompilerFlavor (..)) +import Distribution.Parsec (simpleParsec) +import Distribution.Simple.Compiler (DebugInfoLevel (..), OptimisationLevel (..), PackageDBX (..), ProfDetailLevel (..)) +import Distribution.Simple.Flag +import Distribution.Simple.InstallDirs (InstallDirs (..), toPathTemplate) +import Distribution.Simple.Setup (DumpBuildInfo (..), HaddockTarget (..), TestShowDetails (..)) +import Distribution.Solver.Types.ConstraintSource (ConstraintSource (..)) +import Distribution.Solver.Types.ProjectConfigPath (ProjectConfigPath (..)) +import Distribution.Solver.Types.Settings + ( AllowBootLibInstalls (..) + , CountConflicts (..) + , FineGrainedConflicts (..) + , IndependentGoals (..) + , MinimizeConflictSet (..) + , OnlyConstrained (..) + , PreferOldest (..) + , ReorderGoals (..) + , StrongFlags (..) + ) +import Distribution.System (OS (..), buildOS) +import Distribution.Types.CondTree (CondTree (..)) +import Distribution.Types.Flag (mkFlagAssignment) +import Distribution.Types.PackageId (PackageIdentifier (..)) +import Distribution.Types.PackageName +import Distribution.Types.PackageVersionConstraint (PackageVersionConstraint (..)) +import Distribution.Types.SourceRepo (KnownRepoType (..), RepoType (..)) +import Distribution.Types.Version (mkVersion) +import Distribution.Types.VersionRange.Internal (VersionRange (..)) +import Distribution.Utils.NubList +import Distribution.Verbosity +import Network.URI (parseURI) +import System.Directory (canonicalizePath, doesFileExist) +import System.FilePath (()) +import Prelude () + +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (Assertion, assertBool, assertEqual, testCase) + +parserTests :: TestTree +parserTests = + testGroup + "project files parsec tests" + [ testCase "read packages" testPackages + , testCase "read optional-packages" testOptionalPackages + , testCase "read extra-packages" testExtraPackages + , testCase "read source-repository-package" testSourceRepoList + , testCase "read project-config-build-only" testProjectConfigBuildOnly + , testCase "read project-config-shared" testProjectConfigShared + , testCase "read install-dirs" testInstallDirs + , testCase "read remote-repos" testRemoteRepos + , testCase "read local-no-index-repos" testLocalNoIndexRepos + , testCase "set explicit provenance" testProjectConfigProvenance + , testCase "read project-config-local-packages" testProjectConfigLocalPackages + , testCase "read project-config-all-packages" testProjectConfigAllPackages + , testCase "read project-config-specific-packages" testProjectConfigSpecificPackages + , testCase "test projectConfigAllPackages concatenation" testAllPackagesConcat + , testCase "test projectConfigSpecificPackages concatenation" testSpecificPackagesConcat + , testCase "test program-locations concatenation" testProgramLocationsConcat + , testCase "test program-options concatenation" testProgramOptionsConcat + , testCase "test allow-newer and allow-older concatenation" testRelaxDepsConcat + , testCase "test library-coverage overwrites coverage" testLibraryCoverage + , testCase "test haddock-all flag" testHaddockAll + , testCase "test override haddock-all: True" testHaddockAllOverwriteTrue + , testCase "test override haddock-all: False" testHaddockAllOverwriteFalse + ] + +testPackages :: Assertion +testPackages = do + let expected = [".", "packages/packages.cabal"] + (config, legacy) <- readConfigDefault "packages" + assertConfigEquals expected config legacy (projectPackages . condTreeData) + +testOptionalPackages :: Assertion +testOptionalPackages = do + let expected = [".", "packages/packages.cabal"] + (config, legacy) <- readConfigDefault "optional-packages" + assertConfigEquals expected config legacy (projectPackagesOptional . condTreeData) + +testSourceRepoList :: Assertion +testSourceRepoList = do + (config, legacy) <- readConfigDefault "source-repository-packages" + assertConfigEquals expected config legacy (projectPackagesRepo . condTreeData) + where + expected = + [ SourceRepositoryPackage + { srpType = KnownRepoType Git + , srpLocation = "https://example.com/Project.git" + , srpTag = Just "1234" + , srpBranch = Nothing + , srpSubdir = [] + , srpCommand = [] + } + , SourceRepositoryPackage + { srpType = KnownRepoType Git + , srpLocation = "https://example.com/example-dir/" + , srpTag = Just "12345" + , srpBranch = Nothing + , srpSubdir = ["subproject"] + , srpCommand = [] + } + ] + +testExtraPackages :: Assertion +testExtraPackages = do + (config, legacy) <- readConfigDefault "extra-packages" + assertConfigEquals expected config legacy (projectPackagesNamed . condTreeData) + where + expected = + [ PackageVersionConstraint (mkPackageName "a") (OrLaterVersion (mkVersion [0])) + , PackageVersionConstraint (mkPackageName "b") (IntersectVersionRanges (OrLaterVersion (mkVersion [0, 7, 3])) (EarlierVersion (mkVersion [0, 9]))) + ] + +testProjectConfigBuildOnly :: Assertion +testProjectConfigBuildOnly = do + (config, legacy) <- readConfigDefault "project-config-build-only" + assertConfigEquals expected config legacy (projectConfigBuildOnly . condTreeData) + where + expected = ProjectConfigBuildOnly{..} + projectConfigVerbosity = toFlag (toEnum 2) + projectConfigDryRun = mempty -- cli only + projectConfigOnlyDeps = mempty -- cli only + projectConfigOnlyDownload = mempty -- cli only + projectConfigSummaryFile = toNubList [toPathTemplate "summaryFile", toPathTemplate "summaryFile2"] + projectConfigLogFile = toFlag $ toPathTemplate "myLog.log" + projectConfigBuildReports = toFlag DetailedReports + projectConfigReportPlanningFailure = toFlag True + projectConfigSymlinkBinDir = toFlag "some-bindir" + projectConfigNumJobs = toFlag $ Just 4 + projectConfigUseSemaphore = toFlag True + projectConfigKeepGoing = toFlag True + projectConfigOfflineMode = toFlag True + projectConfigKeepTempFiles = toFlag True + projectConfigHttpTransport = toFlag "wget" + projectConfigIgnoreExpiry = toFlag True + projectConfigCacheDir = toFlag "some-cache-dir" + projectConfigLogsDir = toFlag "logs-directory" + projectConfigClientInstallFlags = + ClientInstallFlags + { cinstInstallLibs = Flag True + , cinstEnvironmentPath = Flag "path/to/env" + , cinstOverwritePolicy = Flag AlwaysOverwrite + , cinstInstallMethod = Flag InstallMethodSymlink + , cinstInstalldir = Flag "path/to/installdir" + } + +testProjectConfigShared :: Assertion +testProjectConfigShared = do + (config, legacy) <- readConfigDefault "project-config-shared" + assertConfigEquals expected config legacy (projectConfigShared . condTreeData) + where + expected = ProjectConfigShared{..} + projectConfigDistDir = toFlag "something" + projectConfigConfigFile = mempty -- cli only + projectConfigProjectFileParser = mempty -- cli only + projectConfigProjectDir = toFlag "my-project-dir" + projectConfigProjectFile = toFlag "my-project" + projectConfigIgnoreProject = toFlag False + projectConfigHcFlavor = toFlag GHCJS + projectConfigHcPath = toFlag "/some/path/to/compiler" + projectConfigHcPkg = toFlag "/some/path/to/ghc-pkg" + projectConfigHaddockIndex = toFlag $ toPathTemplate "/path/to/haddock-index" + projectConfigInstallDirs = mempty -- tested below in testInstallDirs + projectConfigPackageDBs = [Nothing, Just (SpecificPackageDB "foo"), Nothing, Just (SpecificPackageDB "bar"), Just (SpecificPackageDB "baz")] + projectConfigRemoteRepos = mempty -- tested below in testRemoteRepos + projectConfigLocalNoIndexRepos = mempty -- tested below in testLocalNoIndexRepos + projectConfigActiveRepos = Flag (ActiveRepos [ActiveRepo (RepoName "hackage.haskell.org") CombineStrategyMerge, ActiveRepo (RepoName "my-repository") CombineStrategyOverride]) + projectConfigIndexState = + let + hackageState = IndexStateTime $ fromJust $ simpleParsec "2020-05-06T22:33:27Z" + indexState' = insertIndexState (RepoName "hackage.haskell.org") hackageState headTotalIndexState + headHackageState = IndexStateTime $ fromJust $ simpleParsec "2020-04-29T04:11:05Z" + indexState'' = insertIndexState (RepoName "head.hackage") headHackageState indexState' + in + toFlag indexState'' + projectConfigStoreDir = toFlag "a/store/dir/path" -- cli only + projectConfigConstraints = + let + bar = fromRight (error "error parsing bar") $ readUserConstraint "bar == 2.1" + barFlags = fromRight (error "error parsing bar flags") $ readUserConstraint "bar +foo -baz" + source = ConstraintSourceProjectConfig $ ProjectConfigPath $ "cabal.project" :| [] + in + [(bar, source), (barFlags, source)] + projectConfigPreferences = [PackageVersionConstraint (mkPackageName "foo") (ThisVersion (mkVersion [0, 9])), PackageVersionConstraint (mkPackageName "baz") (LaterVersion (mkVersion [2, 0]))] + projectConfigCabalVersion = Flag (mkVersion [1, 24, 0, 1]) + projectConfigSolver = Flag AlwaysModular + projectConfigAllowOlder = Just (AllowOlder $ RelaxDepsSome [RelaxedDep RelaxDepScopeAll RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "dep")), RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "pkga") (mkVersion [1, 1, 2]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "dep-pkg"))]) + projectConfigAllowNewer = Just (AllowNewer $ RelaxDepsSome [RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "pkgb") (mkVersion [1, 2, 3]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "dep-pkgb")), RelaxedDep RelaxDepScopeAll RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "importantlib"))]) + projectConfigWriteGhcEnvironmentFilesPolicy = Flag AlwaysWriteGhcEnvironmentFiles + projectConfigMaxBackjumps = toFlag 42 + projectConfigReorderGoals = Flag (ReorderGoals True) + projectConfigCountConflicts = Flag (CountConflicts False) + projectConfigFineGrainedConflicts = Flag (FineGrainedConflicts False) + projectConfigMinimizeConflictSet = Flag (MinimizeConflictSet True) + projectConfigStrongFlags = Flag (StrongFlags True) + projectConfigAllowBootLibInstalls = Flag (AllowBootLibInstalls True) + projectConfigOnlyConstrained = Flag OnlyConstrainedAll + projectConfigPerComponent = Flag True + projectConfigIndependentGoals = Flag (IndependentGoals True) + projectConfigPreferOldest = Flag (PreferOldest True) + projectConfigProgPathExtra = toNubList ["/foo/bar", "/baz/quux"] + projectConfigMultiRepl = toFlag True + +testInstallDirs :: Assertion +testInstallDirs = do + (config, legacy) <- readConfigDefault "install-dirs" + assertConfigEquals expected config legacy (projectConfigInstallDirs . projectConfigShared . condTreeData) + where + expected = + InstallDirs + { prefix = Flag $ toPathTemplate "my/prefix-path" + , bindir = Flag $ toPathTemplate "bin/dir/" + , libdir = Flag $ toPathTemplate "lib/dir/path" + , libsubdir = Flag $ toPathTemplate "/lib/sub/dir" + , dynlibdir = Flag $ toPathTemplate "dyn/lib/dir/path" + , flibdir = mempty + , libexecdir = Flag $ toPathTemplate "lib/exec/dir/" + , libexecsubdir = Flag $ toPathTemplate "libexec/subdir" + , includedir = mempty + , datadir = Flag $ toPathTemplate "path/to/datadir/" + , datasubdir = Flag $ toPathTemplate "a/datadir/subdir" + , docdir = Flag $ toPathTemplate "path/to/docs" + , mandir = mempty + , htmldir = Flag $ toPathTemplate "dir/html/" + , haddockdir = Flag $ toPathTemplate "haddock/dir" + , sysconfdir = Flag $ toPathTemplate "sys/conf/dir" + } + +testRemoteRepos :: Assertion +testRemoteRepos = do + (config, legacy) <- readConfigDefault "remote-repos" + let actualRemoteRepos = (fromNubList . projectConfigRemoteRepos . projectConfigShared . condTreeData) config + assertBool "Expected RemoteRepos do not match parsed values" $ compareLists expected actualRemoteRepos compareRemoteRepos + assertConfigEquals mempty config legacy (projectConfigLocalNoIndexRepos . projectConfigShared . condTreeData) + where + expected = [packagesRepository, morePackagesRepository, secureLocalRepository] + packagesRepository = + RemoteRepo + { remoteRepoName = RepoName "packages.example.org" + , remoteRepoURI = fromJust $ parseURI "http://packages.example.org/" + , remoteRepoSecure = pure True + , remoteRepoRootKeys = ["21", "42"] + , remoteRepoKeyThreshold = 2 + , remoteRepoShouldTryHttps = False + } + morePackagesRepository = + RemoteRepo + { remoteRepoName = RepoName "more-packages.example.org" + , remoteRepoURI = fromJust $ parseURI "https://more-packages.example.org/" + , remoteRepoSecure = pure True + , remoteRepoRootKeys = ["foo", "bar"] + , remoteRepoKeyThreshold = 1 + , remoteRepoShouldTryHttps = False + } + secureLocalRepository = + RemoteRepo + { remoteRepoName = RepoName "my-secure-local-repository" + , remoteRepoURI = fromJust $ parseURI "file:/path/to/secure/repo" + , remoteRepoSecure = pure True + , remoteRepoRootKeys = ["123"] + , remoteRepoKeyThreshold = 1 + , remoteRepoShouldTryHttps = False + } + +testLocalNoIndexRepos :: Assertion +testLocalNoIndexRepos = do + (config, legacy) <- readConfigDefault "local-no-index-repos" + let actualLocalRepos = (fromNubList . projectConfigLocalNoIndexRepos . projectConfigShared . condTreeData) config + assertBool "Expected LocalNoIndexRepos do not match parsed values" $ compareLists expected actualLocalRepos compareLocalRepos + assertConfigEquals mempty config legacy (projectConfigRemoteRepos . projectConfigShared . condTreeData) + where + expected = [myRepository, mySecureRepository] + myRepository = + LocalRepo + { localRepoName = RepoName "my-repository" + , localRepoPath = normalisePath "/absolute/path/to/directory" + , localRepoSharedCache = False + } + mySecureRepository = + LocalRepo + { localRepoName = RepoName "my-other-repository" + , localRepoPath = normalisePath "/another/path/to/repository" + , localRepoSharedCache = False + } + normalisePath path = case buildOS of + Windows -> asPosixPath path + _ -> path + +testProjectConfigProvenance :: Assertion +testProjectConfigProvenance = do + let expected = Set.singleton (Explicit (ProjectConfigPath $ "cabal.project" :| [])) + (config, legacy) <- readConfigDefault "empty" + assertConfigEquals expected config legacy (projectConfigProvenance . condTreeData) + +testProjectConfigLocalPackages :: Assertion +testProjectConfigLocalPackages = do + (config, legacy) <- readConfigDefault "project-config-local-packages" + assertConfigEquals expected config legacy (projectConfigLocalPackages . condTreeData) + where + expected = PackageConfig{..} + packageConfigProgramPaths = MapLast $ Map.fromList [("ghc", "/tmp/bin/ghc"), ("gcc", "/tmp/bin/gcc")] + packageConfigProgramArgs = MapMappend $ Map.fromList [("ghc", ["-fno-state-hack", "-foo"]), ("gcc", ["-baz", "-quux"])] + packageConfigProgramPathExtra = toNubList ["/tmp/bin/extra", "/usr/local/bin"] + packageConfigFlagAssignment = mkFlagAssignment [("foo", True), ("bar", False)] + packageConfigVanillaLib = Flag False + packageConfigSharedLib = Flag True + packageConfigStaticLib = Flag True + packageConfigDynExe = Flag True + packageConfigFullyStaticExe = Flag True + packageConfigProf = Flag True + packageConfigProfLib = Flag True + packageConfigProfShared = Flag False + packageConfigProfExe = Flag True + packageConfigProfDetail = Flag ProfDetailAllFunctions + packageConfigProfLibDetail = Flag ProfDetailExportedFunctions + packageConfigConfigureArgs = ["-some-arg", "/some/path"] + packageConfigOptimization = Flag MaximumOptimisation + packageConfigProgPrefix = Flag $ toPathTemplate "another/path" + packageConfigProgSuffix = Flag $ toPathTemplate "and/another/path" + packageConfigExtraLibDirs = ["so", "many", "lib/dirs"] + packageConfigExtraLibDirsStatic = ["a/few", "static/lib/dirs"] + packageConfigExtraFrameworkDirs = ["osx/framework", "dirs"] + packageConfigExtraIncludeDirs = ["incredible/amount", "of", "include", "directories"] + packageConfigGHCiLib = Flag False + packageConfigSplitSections = Flag True + packageConfigSplitObjs = Flag True + packageConfigStripExes = Flag False + packageConfigStripLibs = Flag False + packageConfigTests = Flag True + packageConfigBenchmarks = Flag True + packageConfigCoverage = Flag True + packageConfigRelocatable = Flag True + packageConfigDebugInfo = Flag MaximalDebugInfo + packageConfigDumpBuildInfo = Flag DumpBuildInfo + packageConfigRunTests = Flag True + packageConfigDocumentation = Flag True + -- Haddock options + packageConfigHaddockHoogle = Flag True + packageConfigHaddockHtml = Flag False + packageConfigHaddockHtmlLocation = Flag "http://hackage.haskell.org/packages/archive/$pkg/latest/doc/html" + packageConfigHaddockForeignLibs = Flag True + packageConfigHaddockExecutables = Flag True + packageConfigHaddockTestSuites = Flag True + packageConfigHaddockBenchmarks = Flag True + packageConfigHaddockInternal = Flag True + packageConfigHaddockCss = Flag "some/path/to/file.css" + packageConfigHaddockLinkedSource = Flag True + packageConfigHaddockQuickJump = Flag True + packageConfigHaddockHscolourCss = Flag "another/path/to/hscolour.css" + packageConfigHaddockContents = Flag $ toPathTemplate "https://example.com/$pkg/contents" + packageConfigHaddockIndex = Flag $ toPathTemplate "separately-generated/HTML/index" + packageConfigHaddockBaseUrl = Flag "https://example.com/haddock-base-url" + packageConfigHaddockResourcesDir = Flag "/haddock/static" + packageConfigHaddockOutputDir = Flag "/haddock/output" + packageConfigHaddockUseUnicode = Flag False + packageConfigHaddockForHackage = Flag ForHackage + packageConfigTestHumanLog = Flag $ toPathTemplate "human-log.log" + packageConfigTestMachineLog = Flag $ toPathTemplate "machine.log" + packageConfigTestShowDetails = Flag Streaming + packageConfigTestKeepTix = Flag True + packageConfigTestWrapper = Flag "/test-wrapper-path/" + packageConfigTestFailWhenNoTestSuites = Flag True + packageConfigTestTestOptions = [toPathTemplate "--some-option", toPathTemplate "42"] + packageConfigBenchmarkOptions = [toPathTemplate "--some-benchmark-option", toPathTemplate "--another-option"] + +testProjectConfigAllPackages :: Assertion +testProjectConfigAllPackages = do + (config, legacy) <- readConfigDefault "project-config-all-packages" + assertConfigEquals expected config legacy (projectConfigAllPackages . condTreeData) + where + expected :: PackageConfig + expected = + mempty + { packageConfigProfDetail = Flag ProfDetailAllFunctions + , packageConfigProfLibDetail = Flag ProfDetailExportedFunctions + } + +testProjectConfigSpecificPackages :: Assertion +testProjectConfigSpecificPackages = do + (config, legacy) <- readConfigDefault "project-config-specific-packages" + assertConfigEquals expected config legacy (projectConfigSpecificPackage . condTreeData) + where + expected = MapMappend $ Map.fromList [("foo", expectedFoo), ("bar", expectedBar), ("baz", expectedBaz)] + expectedFoo :: PackageConfig + expectedFoo = + mempty + { packageConfigProfDetail = Flag ProfDetailAllFunctions + , packageConfigProfLibDetail = Flag ProfDetailExportedFunctions + , packageConfigVanillaLib = Flag True + } + expectedBar :: PackageConfig + expectedBar = + mempty + { packageConfigProfDetail = Flag ProfDetailTopLate + , packageConfigProfLibDetail = Flag ProfDetailNone + , packageConfigProgPrefix = Flag $ toPathTemplate "prefix/path" + } + expectedBaz :: PackageConfig + expectedBaz = + mempty + { packageConfigSharedLib = Flag True + } + +testAllPackagesConcat :: Assertion +testAllPackagesConcat = do + (config, legacy) <- readConfigDefault "all-packages-concat" + assertConfigEquals expected config legacy (projectConfigAllPackages . condTreeData) + where + expected :: PackageConfig + expected = + mempty + { packageConfigSharedLib = Flag True + , packageConfigStaticLib = Flag True + , packageConfigProgramArgs = + MapMappend $ + Map.fromList + [ ("ghc", ["-fwarn-tabs", "-optc-fno-builtin-malloc", "-Wall", "-optc-fno-builtin-realloc", "-fwrite-ide-info"]) + ] + } + +testSpecificPackagesConcat :: Assertion +testSpecificPackagesConcat = do + (config, legacy) <- readConfigDefault "specific-packages-concat" + assertConfigEquals expected config legacy (projectConfigSpecificPackage . condTreeData) + where + expected = MapMappend $ Map.fromList [("foo", expectedFoo)] + expectedFoo :: PackageConfig + expectedFoo = + mempty + { packageConfigSharedLib = Flag True + , packageConfigStaticLib = Flag True + , packageConfigProgramArgs = MapMappend $ Map.fromList [("ghc", ["-fno-state-hack", "-threaded"])] + } + +testProgramLocationsConcat :: Assertion +testProgramLocationsConcat = do + (config, legacy) <- readConfigDefault "program-locations-concat" + assertConfigEquals expected config legacy (projectConfigLocalPackages . condTreeData) + where + expected :: PackageConfig + expected = + mempty + { packageConfigProgramPaths = MapLast $ Map.fromList [("gcc", "/tmp/bin/gcc"), ("ghc", "/tmp/bin/ghc")] + } + +testProgramOptionsConcat :: Assertion +testProgramOptionsConcat = do + (config, legacy) <- readConfigDefault "program-options-concat" + assertConfigEquals expected config legacy (projectConfigLocalPackages . condTreeData) + where + expected :: PackageConfig + expected = + mempty + { packageConfigProgramArgs = + MapMappend $ + Map.fromList + [ ("ghc", ["-threaded", "-Wall", "-fno-state-hack"]) + , ("gcc", ["-baz", "-foo", "-bar"]) + , ("haddock", ["--optghc=-optP -P"]) + , ("ld", ["-Wl,--gc-sections"]) + ] + } + +testRelaxDepsConcat :: Assertion +testRelaxDepsConcat = do + (config, legacy) <- readConfigDefault "relax-deps-concat" + assertConfigEquals expectedAllowNewer config legacy (projectConfigAllowNewer . projectConfigShared . condTreeData) + assertConfigEquals expectedAllowOlder config legacy (projectConfigAllowOlder . projectConfigShared . condTreeData) + where + expectedAllowNewer :: Maybe AllowNewer + expectedAllowNewer = + pure $ + AllowNewer $ + RelaxDepsSome + [ RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "cassava") (mkVersion [0, 5, 2, 0]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "base")) + , RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "vector-th-unbox") (mkVersion [0, 2, 1, 7]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "base")) + , RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "vector-th-unbox") (mkVersion [0, 2, 1, 7]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "template-haskell")) + ] + expectedAllowOlder :: Maybe AllowOlder + expectedAllowOlder = + pure $ + AllowOlder $ + RelaxDepsSome + [ RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "mtl") (mkVersion [2, 3, 1]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "base")) + , RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "aeson") (mkVersion [2, 2, 3, 0]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "bytestring")) + , RelaxedDep (RelaxDepScopePackageId (PackageIdentifier (mkPackageName "containers") (mkVersion [0, 7]))) RelaxDepModNone (RelaxDepSubjectPkg (mkPackageName "array")) + ] + +-- | Tests that if both library-coverage and coverage flags are specified, library-coverage is used. +testLibraryCoverage :: Assertion +testLibraryCoverage = do + (config, legacy) <- readConfigDefault "library-coverage" + assertConfigEquals (Flag False) config legacy (packageConfigCoverage . projectConfigLocalPackages . condTreeData) + +testHaddockAll :: Assertion +testHaddockAll = do + (config, legacy) <- readConfigDefault "haddock-all" + assertConfigEquals (Flag True) config legacy (packageConfigHaddockExecutables . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag True) config legacy (packageConfigHaddockTestSuites . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag True) config legacy (packageConfigHaddockBenchmarks . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag True) config legacy (packageConfigHaddockForeignLibs . projectConfigLocalPackages . condTreeData) + +-- | Tests that an explicitly set field can override a value inherited from haddock-all. +testHaddockAllOverwriteTrue :: Assertion +testHaddockAllOverwriteTrue = do + (config, legacy) <- readConfigDefault "haddock-all-overwrite-true" + assertConfigEquals (Flag True) config legacy (packageConfigHaddockExecutables . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag True) config legacy (packageConfigHaddockTestSuites . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag True) config legacy (packageConfigHaddockBenchmarks . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag False) config legacy (packageConfigHaddockForeignLibs . projectConfigLocalPackages . condTreeData) + +testHaddockAllOverwriteFalse :: Assertion +testHaddockAllOverwriteFalse = do + (config, legacy) <- readConfigDefault "haddock-all-overwrite-false" + assertConfigEquals (Flag True) config legacy (packageConfigHaddockExecutables . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag False) config legacy (packageConfigHaddockTestSuites . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag False) config legacy (packageConfigHaddockBenchmarks . projectConfigLocalPackages . condTreeData) + assertConfigEquals (Flag False) config legacy (packageConfigHaddockForeignLibs . projectConfigLocalPackages . condTreeData) + +------------------------------------------------------------------------------- +-- Test Utilities +------------------------------------------------------------------------------- +baseDir :: FilePath +baseDir = "parser-tests" "Tests" "files" + +verbosity :: Verbosity +verbosity = normal + +readConfigDefault :: FilePath -> IO (ProjectConfigSkeleton, ProjectConfigSkeleton) +readConfigDefault testSubDir = readConfig testSubDir "cabal.project" + +readConfig :: FilePath -> FilePath -> IO (ProjectConfigSkeleton, ProjectConfigSkeleton) +readConfig testSubDir projectFileName = do + (TestDir testRootFp projectConfigFp distDirLayout) <- testDirInfo testSubDir projectFileName + exists <- liftIO $ doesFileExist projectConfigFp + assertBool ("projectConfig does not exist: " <> projectConfigFp) exists + httpTransport <- liftIO $ configureTransport verbosity [] Nothing + let extensionName = "" + extensionDescription = "" + parsec <- + liftIO $ + runRebuild testRootFp $ + readProjectFileSkeletonParsec verbosity httpTransport distDirLayout extensionName extensionDescription + legacy <- + liftIO $ + runRebuild testRootFp $ + readProjectFileSkeletonLegacy verbosity httpTransport distDirLayout extensionName extensionDescription + return (parsec, legacy) + +assertConfigEquals :: (Eq a, Show a) => a -> ProjectConfigSkeleton -> ProjectConfigSkeleton -> (ProjectConfigSkeleton -> a) -> Assertion +assertConfigEquals expected config configLegacy access = do + assertEqual "Expectation does not match result of Legacy parser" expected actualLegacy + assertEqual "Parsed Config does not match expected" expected actual + where + actual = access config + actualLegacy = access configLegacy + +-- | Represents the directory structure and associated file paths for a test +data TestDir = TestDir + { _testDirTestRootFp :: FilePath + -- ^ Every test has a root directory in ./files/ + , _testDirProjectConfigFp :: FilePath + -- ^ Every test has a project config in testDirTestRootFp/cabal.project + , _testDirDistDirLayout :: DistDirLayout + } + +testDirInfo :: FilePath -> FilePath -> IO TestDir +testDirInfo testSubDir projectFileName = do + projectRootDir <- canonicalizePath (baseDir testSubDir) + let + projectRoot = ProjectRootExplicit projectRootDir projectFileName + distDirLayout = defaultDistDirLayout projectRoot Nothing Nothing + extensionName = "" + projectConfigFp = distProjectFile distDirLayout extensionName + return $ TestDir projectRootDir projectConfigFp distDirLayout + +-- | Compares two lists element-wise using a comparison function. +compareLists :: [a] -> [a] -> (a -> a -> Bool) -> Bool +compareLists xs ys compare' = length xs == length ys && all (uncurry compare') (zip xs ys) + +-- | Compares LocalRepos ignoring field 'localRepoSharedCache' because we do not parse it. +compareLocalRepos :: LocalRepo -> LocalRepo -> Bool +compareLocalRepos repo1 repo2 = + localRepoName repo1 == localRepoName repo2 + && localRepoPath repo1 == localRepoPath repo2 + +-- | Compares RemoteRepos ignoring field 'remoteRepoShouldTryHttps' because we do not parse it. +compareRemoteRepos :: RemoteRepo -> RemoteRepo -> Bool +compareRemoteRepos repo1 repo2 = + remoteRepoName repo1 == remoteRepoName repo2 + && remoteRepoURI repo1 == remoteRepoURI repo2 + && remoteRepoSecure repo1 == remoteRepoSecure repo2 + && remoteRepoRootKeys repo1 == remoteRepoRootKeys repo2 + && remoteRepoKeyThreshold repo1 == remoteRepoKeyThreshold repo2 diff --git a/cabal-install/parser-tests/Tests/files/all-packages-concat/cabal.project b/cabal-install/parser-tests/Tests/files/all-packages-concat/cabal.project new file mode 100644 index 00000000000..a85e19b9868 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/all-packages-concat/cabal.project @@ -0,0 +1,8 @@ +package * + static: True + ghc-options: -fwrite-ide-info + ghc-options: -Wall -optc-fno-builtin-realloc + +package * + shared: True + ghc-options: -fwarn-tabs -optc-fno-builtin-malloc diff --git a/cabal-install/parser-tests/Tests/files/empty/cabal.project b/cabal-install/parser-tests/Tests/files/empty/cabal.project new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-install/parser-tests/Tests/files/extra-packages/cabal.project b/cabal-install/parser-tests/Tests/files/extra-packages/cabal.project new file mode 100644 index 00000000000..13d55f2ef33 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/extra-packages/cabal.project @@ -0,0 +1,3 @@ +extra-packages: + a + , b >= 0.7.3 && < 0.9, diff --git a/cabal-install/parser-tests/Tests/files/haddock-all-overwrite-false/cabal.project b/cabal-install/parser-tests/Tests/files/haddock-all-overwrite-false/cabal.project new file mode 100644 index 00000000000..c0c044081b6 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/haddock-all-overwrite-false/cabal.project @@ -0,0 +1,2 @@ +haddock-all: False +haddock-executables: True diff --git a/cabal-install/parser-tests/Tests/files/haddock-all-overwrite-true/cabal.project b/cabal-install/parser-tests/Tests/files/haddock-all-overwrite-true/cabal.project new file mode 100644 index 00000000000..c591d86a961 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/haddock-all-overwrite-true/cabal.project @@ -0,0 +1,2 @@ +haddock-all: True +haddock-foreign-libraries: False diff --git a/cabal-install/parser-tests/Tests/files/haddock-all/cabal.project b/cabal-install/parser-tests/Tests/files/haddock-all/cabal.project new file mode 100644 index 00000000000..275c4539524 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/haddock-all/cabal.project @@ -0,0 +1 @@ +haddock-all: True diff --git a/cabal-install/parser-tests/Tests/files/install-dirs/cabal.project b/cabal-install/parser-tests/Tests/files/install-dirs/cabal.project new file mode 100644 index 00000000000..e9397c1bbef --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/install-dirs/cabal.project @@ -0,0 +1,13 @@ +prefix: my/prefix-path +bindir: bin/dir/ +libdir: lib/dir/path +libsubdir: /lib/sub/dir +dynlibdir: dyn/lib/dir/path +libexecdir: lib/exec/dir/ +libexecsubdir: libexec/subdir +datadir: path/to/datadir/ +datasubdir: a/datadir/subdir +docdir: path/to/docs +htmldir: dir/html/ +haddockdir: haddock/dir +sysconfdir: sys/conf/dir diff --git a/cabal-install/parser-tests/Tests/files/library-coverage/cabal.project b/cabal-install/parser-tests/Tests/files/library-coverage/cabal.project new file mode 100644 index 00000000000..074a06e7731 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/library-coverage/cabal.project @@ -0,0 +1,2 @@ +library-coverage: False +coverage: True diff --git a/cabal-install/parser-tests/Tests/files/local-no-index-repos/cabal.project b/cabal-install/parser-tests/Tests/files/local-no-index-repos/cabal.project new file mode 100644 index 00000000000..f6b42df28b6 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/local-no-index-repos/cabal.project @@ -0,0 +1,5 @@ +repository my-repository + url: file+noindex:///absolute/path/to/directory + +repository my-other-repository + url: file+noindex:/another/path/to/repository diff --git a/cabal-install/parser-tests/Tests/files/optional-packages/cabal.project b/cabal-install/parser-tests/Tests/files/optional-packages/cabal.project new file mode 100644 index 00000000000..37e21016d6a --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/optional-packages/cabal.project @@ -0,0 +1 @@ +optional-packages: . packages/packages.cabal diff --git a/cabal-install/parser-tests/Tests/files/packages/cabal.project b/cabal-install/parser-tests/Tests/files/packages/cabal.project new file mode 100644 index 00000000000..6d9d4728a55 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/packages/cabal.project @@ -0,0 +1 @@ +packages: . packages/packages.cabal diff --git a/cabal-install/parser-tests/Tests/files/program-locations-concat/cabal.project b/cabal-install/parser-tests/Tests/files/program-locations-concat/cabal.project new file mode 100644 index 00000000000..95657d95a17 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/program-locations-concat/cabal.project @@ -0,0 +1,5 @@ +program-locations + gcc-location: /tmp/bin/gcc + +program-locations + ghc-location: /tmp/bin/ghc diff --git a/cabal-install/parser-tests/Tests/files/program-options-concat/cabal.project b/cabal-install/parser-tests/Tests/files/program-options-concat/cabal.project new file mode 100644 index 00000000000..a418091c0d5 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/program-options-concat/cabal.project @@ -0,0 +1,9 @@ +program-options + ghc-options: -fno-state-hack + gcc-options: -foo -bar + haddock-options: --optghc="-optP -P" + +program-options + ghc-options: -threaded -Wall + gcc-options: -baz + ld-options: -Wl,--gc-sections diff --git a/cabal-install/parser-tests/Tests/files/project-config-all-packages/cabal.project b/cabal-install/parser-tests/Tests/files/project-config-all-packages/cabal.project new file mode 100644 index 00000000000..2b336cf830c --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/project-config-all-packages/cabal.project @@ -0,0 +1,3 @@ +package * + profiling-detail: all-functions + library-profiling-detail: exported-functions diff --git a/cabal-install/parser-tests/Tests/files/project-config-build-only/cabal.project b/cabal-install/parser-tests/Tests/files/project-config-build-only/cabal.project new file mode 100644 index 00000000000..eac06d8aadd --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/project-config-build-only/cabal.project @@ -0,0 +1,22 @@ +verbose: 2 +build-summary: summaryFile, summaryFile2 +build-log: myLog.log +remote-build-reporting: detailed +report-planning-failure: True +symlink-bindir: some-bindir +jobs: 4 +semaphore: True +keep-going: True +offline: True +haddock-keep-temp-files: True +http-transport: wget +ignore-expiry: True +remote-repo-cache: some-cache-dir +logs-dir: logs-directory + +-- clientInstallFlags +lib: True +package-env: path/to/env +overwrite-policy: always +install-method: symlink +installdir: path/to/installdir diff --git a/cabal-install/parser-tests/Tests/files/project-config-local-packages/cabal.project b/cabal-install/parser-tests/Tests/files/project-config-local-packages/cabal.project new file mode 100644 index 00000000000..77ffb93ff00 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/project-config-local-packages/cabal.project @@ -0,0 +1,70 @@ +program-options + ghc-options: -fno-state-hack -foo + gcc-options: -baz -quux + +program-locations + ghc-location: /tmp/bin/ghc + gcc-location: /tmp/bin/gcc + +extra-prog-path: /tmp/bin/extra, /usr/local/bin +flags: +foo -bar +library-vanilla: False +shared: True +static: True +executable-dynamic: True +executable-static: True +profiling: True +library-profiling: True +profiling-shared: False +executable-profiling: True +profiling-detail: all-functions +library-profiling-detail: exported-functions +configure-options: -some-arg /some/path +optimization: 2 +program-prefix: another/path +program-suffix: and/another/path +extra-lib-dirs: so, many, lib/dirs +extra-lib-dirs-static: a/few, static/lib/dirs +extra-framework-dirs: osx/framework, dirs +extra-include-dirs: incredible/amount, of, include, directories +library-for-ghci: False +split-sections: True +split-objs: True +executable-stripping: False +library-stripping: False +tests: True +benchmarks: True +coverage: True +relocatable: True +debug-info: 3 +build-info: True +run-tests: True +documentation: True +haddock-hoogle: True +haddock-html: False +haddock-html-location: http://hackage.haskell.org/packages/archive/$pkg/latest/doc/html +haddock-foreign-libraries: True +haddock-executables: True +haddock-tests: True +haddock-benchmarks: True +haddock-internal: True +haddock-css: some/path/to/file.css +haddock-hyperlink-source: True +haddock-quickjump: True +haddock-hscolour-css: another/path/to/hscolour.css +haddock-contents-location: https://example.com/$pkg/contents +haddock-index-location: separately-generated/HTML/index +haddock-base-url: https://example.com/haddock-base-url +haddock-resources-dir: /haddock/static +haddock-output-dir: /haddock/output +haddock-use-unicode: False +haddock-for-hackage: for-hackage + +test-log: human-log.log +test-machine-log: machine.log +test-keep-tix-files: True +test-wrapper: /test-wrapper-path/ +test-fail-when-no-test-suites: True +test-show-details: streaming +test-options: --some-option 42 +benchmark-options: --some-benchmark-option --another-option diff --git a/cabal-install/parser-tests/Tests/files/project-config-shared/cabal.project b/cabal-install/parser-tests/Tests/files/project-config-shared/cabal.project new file mode 100644 index 00000000000..90f00f96278 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/project-config-shared/cabal.project @@ -0,0 +1,39 @@ +builddir: something +project-dir: my-project-dir +project-file: my-project +store-dir: a/store/dir/path +per-component: True +independent-goals: True +ignore-project: False +compiler: ghcjs +with-compiler: /some/path/to/compiler +with-hc-pkg: /some/path/to/ghc-pkg +doc-index-file: /path/to/haddock-index +package-dbs: clear, foo, clear, bar, baz +active-repositories: + , hackage.haskell.org + , my-repository:override +index-state: + , hackage.haskell.org 2020-05-06T22:33:27Z + , head.hackage 2020-04-29T04:11:05Z +constraints: bar == 2.1, + bar +foo -baz +preferences: foo == 0.9, + baz > 2.0 +cabal-lib-version: 1.24.0.1 +solver: modular +allow-older: dep, pkga-1.1.2:dep-pkg +allow-newer: pkgb-1.2.3:dep-pkgb, importantlib +write-ghc-environment-files: always +max-backjumps: 42 +reorder-goals: True +count-conflicts: False +fine-grained-conflicts: False +minimize-conflict-set: True +strong-flags: True +allow-boot-library-installs: True +reject-unconstrained-dependencies: all +prefer-oldest: True +extra-prog-path: /foo/bar, /baz/quux +extra-prog-path-shared-only: /foo/bar, /baz/quux +multi-repl: True diff --git a/cabal-install/parser-tests/Tests/files/project-config-specific-packages/cabal.project b/cabal-install/parser-tests/Tests/files/project-config-specific-packages/cabal.project new file mode 100644 index 00000000000..166b5db3d68 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/project-config-specific-packages/cabal.project @@ -0,0 +1,12 @@ +package foo + profiling-detail: all-functions + library-profiling-detail: exported-functions + library-vanilla: True + +package bar + profiling-detail: late-toplevel + library-profiling-detail: none + program-prefix: prefix/path + +package baz + shared: True diff --git a/cabal-install/parser-tests/Tests/files/relax-deps-concat/cabal.project b/cabal-install/parser-tests/Tests/files/relax-deps-concat/cabal.project new file mode 100644 index 00000000000..8e328432b64 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/relax-deps-concat/cabal.project @@ -0,0 +1,7 @@ +-- allow-newer: parallel-3.2.2.0:base +allow-newer: cassava-0.5.2.0:base +allow-newer: vector-th-unbox-0.2.1.7:base +allow-newer: vector-th-unbox-0.2.1.7:template-haskell + +allow-older: mtl-2.3.1:base, aeson-2.2.3.0:bytestring +allow-older: containers-0.7:array diff --git a/cabal-install/parser-tests/Tests/files/remote-repos/cabal.project b/cabal-install/parser-tests/Tests/files/remote-repos/cabal.project new file mode 100644 index 00000000000..819af437b03 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/remote-repos/cabal.project @@ -0,0 +1,18 @@ +repository packages.example.org + url: http://packages.example.org/ + secure: True + root-keys: 21, 42 + key-threshold: 2 + +repository more-packages.example.org + url: https://more-packages.example.org/ + secure: True + root-keys: foo + , bar + key-threshold: 1 + +repository my-secure-local-repository + url: file:/path/to/secure/repo + secure: True + root-keys: 123 + key-threshold: 1 diff --git a/cabal-install/parser-tests/Tests/files/source-repository-packages/cabal.project b/cabal-install/parser-tests/Tests/files/source-repository-packages/cabal.project new file mode 100644 index 00000000000..1ab7d417b54 --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/source-repository-packages/cabal.project @@ -0,0 +1,10 @@ +source-repository-package + type: git + location: https://example.com/Project.git + tag: 1234 + +source-repository-package + type: git + location: https://example.com/example-dir/ + tag: 12345 + subdir: subproject diff --git a/cabal-install/parser-tests/Tests/files/specific-packages-concat/cabal.project b/cabal-install/parser-tests/Tests/files/specific-packages-concat/cabal.project new file mode 100644 index 00000000000..94766d451fa --- /dev/null +++ b/cabal-install/parser-tests/Tests/files/specific-packages-concat/cabal.project @@ -0,0 +1,7 @@ +package foo + static: True + ghc-options: -fno-state-hack + +package foo + shared: True + ghc-options: -threaded diff --git a/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs b/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs index ce1d1665327..7bc6bb8872b 100644 --- a/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs +++ b/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs @@ -140,10 +140,10 @@ fieldDescrs = parseBuildReport :: BS.ByteString -> Either String BuildReport parseBuildReport s = case snd $ runParseResult $ parseFields s of - Left (_, perrors) -> Left $ unlines [err | PError _ err <- toList perrors] + Left (_, perrors) -> Left $ unlines [err | PErrorWithSource _ (PError _ err) <- toList perrors] Right report -> Right report -parseFields :: BS.ByteString -> ParseResult BuildReport +parseFields :: BS.ByteString -> ParseResult src BuildReport parseFields input = do fields <- either (parseFatalFailure zeroPos . show) pure $ readFields input case partitionFields fields of diff --git a/cabal-install/src/Distribution/Client/Check.hs b/cabal-install/src/Distribution/Client/Check.hs index eb053d7e46e..6ca20139855 100644 --- a/cabal-install/src/Distribution/Client/Check.hs +++ b/cabal-install/src/Distribution/Client/Check.hs @@ -16,19 +16,16 @@ import Distribution.Client.Compat.Prelude import Prelude () import Distribution.Client.Errors -import Distribution.Client.Utils.Parsec (renderParseError) + +import Distribution.Client.Errors.Parser +import Distribution.Fields.ParseResult import Distribution.PackageDescription (GenericPackageDescription) import Distribution.PackageDescription.Check import Distribution.PackageDescription.Parsec - ( parseGenericPackageDescription - , runParseResult - ) -import Distribution.Parsec (PWarning (..), showPError) +import Distribution.Parsec import Distribution.Simple.Utils (defaultPackageDescCwd, dieWithException, notice, warn, warnError) import Distribution.Utils.Path (getSymbolicPath) -import System.IO (hPutStr, stderr) - import qualified Control.Monad as CM import qualified Data.ByteString as BS import qualified Data.Function as F @@ -36,19 +33,17 @@ import qualified Data.List as L import qualified Data.List.NonEmpty as NE import qualified System.Directory as Dir -readGenericPackageDescriptionCheck :: Verbosity -> FilePath -> IO ([PWarning], GenericPackageDescription) +readGenericPackageDescriptionCheck :: Verbosity -> FilePath -> IO ([PWarningWithSource CabalFileSource], GenericPackageDescription) readGenericPackageDescriptionCheck verbosity fpath = do exists <- Dir.doesFileExist fpath unless exists $ dieWithException verbosity $ FileDoesntExist fpath bs <- BS.readFile fpath - let (warnings, result) = runParseResult (parseGenericPackageDescription bs) + let (warnings, result) = runParseResult $ withSource (PCabalFile (fpath, bs)) (parseGenericPackageDescription bs) case result of - Left (_, errors) -> do - traverse_ (warn verbosity . showPError fpath) errors - hPutStr stderr $ renderParseError fpath bs errors warnings - dieWithException verbosity ParseError + Left (mspecVersion, errors) -> do + dieWithException verbosity (CabalCheckParseError (CabalFileParseError fpath bs errors mspecVersion warnings)) Right x -> return (warnings, x) -- | Checks a package for common errors. Returns @True@ if the package @@ -65,7 +60,7 @@ check verbosity ignores = do pdfile <- getSymbolicPath <$> defaultPackageDescCwd verbosity (ws, ppd) <- readGenericPackageDescriptionCheck verbosity pdfile -- convert parse warnings into PackageChecks - let ws' = map (wrapParseWarning pdfile) ws + let ws' = map (wrapParseWarning pdfile . pwarning) ws ioChecks <- checkPackageFilesGPD verbosity ppd "." let packageChecksPrim = ioChecks ++ checkPackage ppd ++ ws' (packageChecks, unrecs) = filterPackageChecksByIdString packageChecksPrim ignores diff --git a/cabal-install/src/Distribution/Client/CmdConfigure.hs b/cabal-install/src/Distribution/Client/CmdConfigure.hs index 8ce26f986ad..dbaec030520 100644 --- a/cabal-install/src/Distribution/Client/CmdConfigure.hs +++ b/cabal-install/src/Distribution/Client/CmdConfigure.hs @@ -159,7 +159,11 @@ configureAction' flags@NixStyleFlags{..} _extraArgs globalFlags = do (flagToMaybe . projectConfigHttpTransport $ projectConfigBuildOnly cliConfig) (CondNode conf imps bs) <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $ - readProjectLocalExtraConfig v httpTransport (distDirLayout baseCtx) + readProjectLocalExtraConfig + v + (fromFlagOrDefault defaultProjectFileParser $ projectConfigProjectFileParser $ projectConfigShared cliConfig) + httpTransport + (distDirLayout baseCtx) when (not (null imps && null bs)) $ dieWithException v UnableToPerformInplaceUpdate return (baseCtx, conf <> cliConfig) else return (baseCtx, cliConfig) diff --git a/cabal-install/src/Distribution/Client/CmdInstall/ClientInstallFlags.hs b/cabal-install/src/Distribution/Client/CmdInstall/ClientInstallFlags.hs index 1be09c514a9..32077fe7b99 100644 --- a/cabal-install/src/Distribution/Client/CmdInstall/ClientInstallFlags.hs +++ b/cabal-install/src/Distribution/Client/CmdInstall/ClientInstallFlags.hs @@ -1,4 +1,6 @@ +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} module Distribution.Client.CmdInstall.ClientInstallFlags @@ -6,9 +8,12 @@ module Distribution.Client.CmdInstall.ClientInstallFlags , ClientInstallFlags (..) , defaultClientInstallFlags , clientInstallOptions + , clientInstallFlagsGrammar ) where import Distribution.Client.Compat.Prelude +import Distribution.Compat.Lens (Lens') +import Distribution.FieldGrammar import Prelude () import Distribution.ReadE @@ -35,6 +40,7 @@ import Distribution.Client.Types.InstallMethod import Distribution.Client.Types.OverwritePolicy ( OverwritePolicy (..) ) +import Distribution.Client.Utils.Parsec import qualified Distribution.Compat.CharParsing as P @@ -114,6 +120,26 @@ clientInstallOptions _ = $ reqArg "DIR" (succeedReadE Flag) flagToList ] +clientInstallFlagsGrammar + :: ( FieldGrammar c g + , Applicative (g ClientInstallFlags) + , c (Identity (Flag Bool)) + , c (Flag' FilePathNT FilePath) + , c (Identity (Flag OverwritePolicy)) + , c (Identity (Flag InstallMethod)) + ) + => g ClientInstallFlags ClientInstallFlags +clientInstallFlagsGrammar = + ClientInstallFlags + <$> optionalFieldDef "lib" cinstInstallLibsLens mempty + <*> ( optionalFieldDefAla "package-env" (alaFlag FilePathNT) cinstEnvironmentPathLens mempty + <* optionalFieldDefAla "env" (alaFlag FilePathNT) cinstEnvironmentPathLens mempty + ) + <*> optionalFieldDef "overwrite-policy" cinstOverwritePolicyLens mempty + <*> optionalFieldDef "install-method" cinstInstallMethodLens mempty + <*> optionalFieldDefAla "installdir" (alaFlag FilePathNT) cinstInstalldirLens mempty +{-# SPECIALIZE clientInstallFlagsGrammar :: ParsecFieldGrammar' ClientInstallFlags #-} + parsecInstallMethod :: CabalParsing m => m InstallMethod parsecInstallMethod = do name <- P.munch1 isAlpha @@ -121,3 +147,23 @@ parsecInstallMethod = do "copy" -> pure InstallMethodCopy "symlink" -> pure InstallMethodSymlink _ -> P.unexpected $ "InstallMethod: " ++ name + +cinstInstallLibsLens :: Lens' ClientInstallFlags (Flag Bool) +cinstInstallLibsLens f c = fmap (\x -> c{cinstInstallLibs = x}) (f (cinstInstallLibs c)) +{-# INLINEABLE cinstInstallLibsLens #-} + +cinstEnvironmentPathLens :: Lens' ClientInstallFlags (Flag FilePath) +cinstEnvironmentPathLens f c = fmap (\x -> c{cinstEnvironmentPath = x}) (f (cinstEnvironmentPath c)) +{-# INLINEABLE cinstEnvironmentPathLens #-} + +cinstOverwritePolicyLens :: Lens' ClientInstallFlags (Flag OverwritePolicy) +cinstOverwritePolicyLens f c = fmap (\x -> c{cinstOverwritePolicy = x}) (f (cinstOverwritePolicy c)) +{-# INLINEABLE cinstOverwritePolicyLens #-} + +cinstInstallMethodLens :: Lens' ClientInstallFlags (Flag InstallMethod) +cinstInstallMethodLens f c = fmap (\x -> c{cinstInstallMethod = x}) (f (cinstInstallMethod c)) +{-# INLINEABLE cinstInstallMethodLens #-} + +cinstInstalldirLens :: Lens' ClientInstallFlags (Flag FilePath) +cinstInstalldirLens f c = fmap (\x -> c{cinstInstalldir = x}) (f (cinstInstalldir c)) +{-# INLINEABLE cinstInstalldirLens #-} diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index 77348dbc638..d0c3db2c32e 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -675,6 +675,7 @@ instance Semigroup SavedConfig where { flagProjectDir = combine flagProjectDir , flagProjectFile = combine flagProjectFile , flagIgnoreProject = combine flagIgnoreProject + , flagProjectFileParser = combine flagProjectFileParser } where combine = combine' savedProjectFlags diff --git a/cabal-install/src/Distribution/Client/Errors.hs b/cabal-install/src/Distribution/Client/Errors.hs index 06f965fd972..4270435b54f 100644 --- a/cabal-install/src/Distribution/Client/Errors.hs +++ b/cabal-install/src/Distribution/Client/Errors.hs @@ -37,6 +37,8 @@ import Text.PrettyPrint hiding (render, (<>)) import qualified Text.PrettyPrint as PP import Text.Regex.Posix.ByteString (WrapError) +import Distribution.Client.Errors.Parser + data CabalInstallException = UnpackGet | NotTarballDir FilePath @@ -61,7 +63,7 @@ data CabalInstallException | UnableToPerformInplaceUpdate | EmptyValuePagerEnvVariable | FileDoesntExist FilePath - | ParseError + | CabalCheckParseError CabalFileParseError | CabalFileNotFound FilePath | FindOpenProgramLocationErr String | PkgConfParseFailed String @@ -187,6 +189,9 @@ data CabalInstallException | CmdPathAcceptsNoTargets | CmdPathCommandDoesn'tSupportDryRun | GenBoundsDoesNotSupportScript FilePath + | LegacyAndParsecParseResultsDiffer FilePath String String + | CabalFileParseFailure CabalFileParseError + | ProjectConfigParseFailure ProjectConfigParseError deriving (Show) exceptionCodeCabalInstall :: CabalInstallException -> Int @@ -214,7 +219,7 @@ exceptionCodeCabalInstall e = case e of UnableToPerformInplaceUpdate{} -> 7032 EmptyValuePagerEnvVariable{} -> 7033 FileDoesntExist{} -> 7034 - ParseError{} -> 7035 + CabalCheckParseError{} -> 7035 CabalFileNotFound{} -> 7036 FindOpenProgramLocationErr{} -> 7037 PkgConfParseFailed{} -> 7038 @@ -340,6 +345,9 @@ exceptionCodeCabalInstall e = case e of CmdPathAcceptsNoTargets{} -> 7161 CmdPathCommandDoesn'tSupportDryRun -> 7163 GenBoundsDoesNotSupportScript{} -> 7164 + LegacyAndParsecParseResultsDiffer{} -> 7165 + CabalFileParseFailure{} -> 7166 + ProjectConfigParseFailure{} -> 7167 exceptionMessageCabalInstall :: CabalInstallException -> String exceptionMessageCabalInstall e = case e of @@ -378,7 +386,7 @@ exceptionMessageCabalInstall e = case e of UnableToPerformInplaceUpdate -> "local project file has conditional and/or import logic, unable to perform and automatic in-place update" EmptyValuePagerEnvVariable -> "man: empty value of the PAGER environment variable" FileDoesntExist fpath -> "Error Parsing: file \"" ++ fpath ++ "\" doesn't exist. Cannot continue." - ParseError -> "parse error" + CabalCheckParseError err -> renderCabalFileParseError err CabalFileNotFound cabalFile -> "Package .cabal file not found in the tarball: " ++ cabalFile FindOpenProgramLocationErr err -> err PkgConfParseFailed perror -> @@ -864,6 +872,19 @@ exceptionMessageCabalInstall e = case e of "The 'path' command doesn't support the flag '--dry-run'." GenBoundsDoesNotSupportScript{} -> "The 'gen-bounds' command does not support script targets." + LegacyAndParsecParseResultsDiffer _fp legacyParsec parsec -> + unlines + [ "The legacy and parsec parsers produced different results for the project file. This is unexpected, please report this as a bug." + , "The legacy parser will be removed in the next major version." + , "Legacy parse result:" + , legacyParsec + , "Parsec parse result:" + , parsec + ] + CabalFileParseFailure cbfError -> + renderCabalFileParseError cbfError + ProjectConfigParseFailure pcfError -> + renderProjectConfigParseError pcfError instance Exception (VerboseException CabalInstallException) where displayException :: VerboseException CabalInstallException -> [Char] diff --git a/cabal-install/src/Distribution/Client/Errors/Parser.hs b/cabal-install/src/Distribution/Client/Errors/Parser.hs new file mode 100644 index 00000000000..b2e19397925 --- /dev/null +++ b/cabal-install/src/Distribution/Client/Errors/Parser.hs @@ -0,0 +1,277 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Distribution.Client.Errors.Parser where + +import Distribution.Client.Compat.Prelude +import System.FilePath (normalise) +import Prelude () + +import qualified Data.ByteString.Char8 as BS8 +import qualified Data.List.NonEmpty as NE +import qualified Data.Map as Map +import qualified Data.Map.Merge.Strict as Map +import Distribution.Parsec +import Distribution.Parsec.Source +import Distribution.Simple.Utils (fromUTF8BS) +import Distribution.Solver.Types.ProjectConfigPath +import Distribution.Version (Version) +import Text.PrettyPrint (render) + +-- Error when parsing a .cabal file + +-- | Errors reported upon failing to parse a @.cabal@ file. +data CabalFileParseError + = CabalFileParseError + FilePath + -- ^ @.cabal@ file path + BS8.ByteString + -- ^ @.cabal@ file contents + (NonEmpty (PErrorWithSource CabalFileSource)) + -- ^ errors + (Maybe Version) + -- ^ We might discover the spec version the package needs + [PWarningWithSource CabalFileSource] + -- ^ warnings + +-- | Manual instance which skips file contents +instance Show CabalFileParseError where + showsPrec d (CabalFileParseError fp _ es mv ws) = + showParen (d > 10) $ + showString "CabalFileParseError" + . showChar ' ' + . showsPrec 11 fp + . showChar ' ' + . showsPrec 11 ("" :: String) + . showChar ' ' + . showsPrec 11 es + . showChar ' ' + . showsPrec 11 mv + . showChar ' ' + . showsPrec 11 ws + +instance Exception CabalFileParseError where + displayException = renderCabalFileParseError + +renderCabalFileParseError :: CabalFileParseError -> String +renderCabalFileParseError (CabalFileParseError _filePath _contents errors _ warnings) = + renderParseErrorCabalFile errors warnings + +-- Error when parsing a project file + +-- | Errors reported upon failing to parse a @cabal.project@ file. +data ProjectConfigParseError + = ProjectConfigParseError + (NonEmpty (PErrorWithSource ProjectFileSource)) + -- ^ errors + [PWarningWithSource ProjectFileSource] + -- ^ warnings + +-- | Manual instance which skips file contents +instance Show ProjectConfigParseError where + showsPrec d (ProjectConfigParseError es ws) = + showParen (d > 10) $ + showString "ProjectConfigParseError" + . showChar ' ' + . showsPrec 11 es + . showChar ' ' + . showsPrec 11 ws + +instance Exception ProjectConfigParseError where + displayException = renderProjectConfigParseError + +renderProjectConfigParseError :: ProjectConfigParseError -> String +renderProjectConfigParseError (ProjectConfigParseError errors warnings) = + renderParseError displayProjectFileSource errors warnings + where + displayProjectFileSource (ProjectFileSource (path, contents)) = + renderParseErrorFile "project" (currentProjectConfigPath path) (if isTopLevelConfigPath path then Nothing else Just $ render (docProjectImportedBy path)) contents + +data ProjectFileSource = ProjectFileSource (ProjectConfigPath, BS8.ByteString) deriving (Show, Generic) + +instance Eq ProjectFileSource where + (ProjectFileSource (path1, _)) == (ProjectFileSource (path2, _)) = path1 == path2 + +instance Ord ProjectFileSource where + (ProjectFileSource (path1, _)) `compare` (ProjectFileSource (path2, _)) = path1 `compare` path2 + +renderProjectFileSource :: ProjectFileSource -> String +renderProjectFileSource (ProjectFileSource (path, _contents)) = + currentProjectConfigPath path + +renderParseErrorCabalFile :: NonEmpty (PErrorWithSource CabalFileSource) -> [PWarningWithSource CabalFileSource] -> String +renderParseErrorCabalFile errors warnings = + renderParseError renderCabalFileSourceMsgs errors warnings + +-- | Render parse error highlighting the part of the input file. +renderParseError + :: forall src + . (Ord src, Eq src) + => (src -> ([PError], [PWarning]) -> String) + -> NonEmpty (PErrorWithSource src) + -> [PWarningWithSource src] + -> String +renderParseError display errors warnings = + unlines [renderParseErrorsWarnings (fmap display source) ws | (source, ws) <- joinedErrorsWarningsList] + where + mkErrorGroup :: NonEmpty (PErrorWithSource src) -> (PSource src, [PError]) + mkErrorGroup (x :| xs) = (perrorSource x, perror x : map perror xs) + + mkWarningGroup :: NonEmpty (PWarningWithSource src) -> (PSource src, [PWarning]) + mkWarningGroup (x :| xs) = (pwarningSource x, pwarning x : map pwarning xs) + + groupedErrors = + Map.fromListWith (++) $ + map mkErrorGroup $ + NE.groupBy + (\a b -> perrorSource a == perrorSource b) + errors + groupedWarnings = + Map.fromListWith (++) $ + map mkWarningGroup $ + NE.groupBy + (\a b -> pwarningSource a == pwarningSource b) + warnings + + joinedErrorsWarnings :: Map.Map (PSource src) ([PError], [PWarning]) + joinedErrorsWarnings = Map.merge (Map.mapMissing (\_ es -> (es, []))) (Map.mapMissing (\_ ps -> ([], ps))) (Map.zipWithMatched (\_ es ps -> (es, ps))) groupedErrors groupedWarnings + + joinedErrorsWarningsList = Map.toList joinedErrorsWarnings + +renderParseErrorsWarnings :: PSource (([PError], [PWarning]) -> String) -> ([PError], [PWarning]) -> String +renderParseErrorsWarnings source (errors, warnings) = + case source of + PKnownSource src -> src (errors, warnings) + PUnknownSource -> renderParseErrorNoFile "" errors warnings + +renderCabalFileSourceMsgs :: CabalFileSource -> ([PError], [PWarning]) -> String +renderCabalFileSourceMsgs (PCabalFile (fpath, contents)) (errors, warnings) = + renderParseErrorFile "cabal" fpath Nothing contents (errors, warnings) + +renderInstalledPackageInfoSourceMsgs :: InstalledPackageInfoSource -> ([PError], [PWarning]) -> String +renderInstalledPackageInfoSourceMsgs PInstalledPackageInfo (errors, warnings) = + renderParseErrorNoFile "installed package info" errors warnings + +renderParseErrorNoFile :: String -> [PError] -> [PWarning] -> String +renderParseErrorNoFile herald errors warnings = + renderParseErrorGeneral herald "" Nothing (const []) errors warnings + +-- | Render a parse error which resulted from a file on disk +renderParseErrorFile + :: String + -- ^ Human name for the kind of file (i.e. cabal, project "file") + -> FilePath + -- ^ Path to the file + -> Maybe String + -- ^ Provenance, any additional contextual info to print + -> BS8.ByteString + -- ^ Contents of the file + -> ([PError], [PWarning]) + -> String +renderParseErrorFile herald filepath provenance contents (errors, warnings) = + renderParseErrorGeneral (herald <> " file " <> filepath) (filepath' <> ":") provenance formatInput errors warnings + where + filepath' = normalise filepath + + -- lines of the input file. 'lines' is taken, so they are called rows + -- contents, line number, whether it's empty line + rows :: [(String, Int, Bool)] + rows = zipWith f (BS8.lines contents) [1 ..] + where + f bs i = let s = fromUTF8BS bs in (s, i, isEmptyOrComment s) + + rowsZipper = listToZipper rows + + isEmptyOrComment :: String -> Bool + isEmptyOrComment s = case dropWhile (== ' ') s of + "" -> True -- empty + ('-' : '-' : _) -> True -- comment + _ -> False + + -- format line: prepend the given line number + formatInput :: Position -> [String] + formatInput (Position row col) = case advance (row - 1) rowsZipper of + Zipper xs ys -> before ++ after + where + before = case span (\(_, _, b) -> b) xs of + (_, []) -> [] + (zs, z : _) -> map formatInputLine $ z : reverse zs + + after = case ys of + [] -> [] + (z : _zs) -> + [ formatInputLine z -- error line + , " | " ++ replicate (col - 1) ' ' ++ "^" -- pointer: ^ + ] + -- do we need rows after? + -- ++ map formatInputLine (take 1 zs) -- one row after + + formatInputLine :: (String, Int, Bool) -> String + formatInputLine (str, row, _) = leftPadShow row ++ " | " ++ str + + -- hopefully we don't need to work with over 99999 lines .cabal files + -- at that point small glitches in error messages are hopefully fine. + leftPadShow :: Int -> String + leftPadShow n = let s = show n in replicate (5 - length s) ' ' ++ s + +-- | A generic rendering function which can render from many sources. +renderParseErrorGeneral + :: String + -> String + -> Maybe String + -> (Position -> [String]) + -- ^ Extra information to render based on the position + -> [PError] + -> [PWarning] + -> String +renderParseErrorGeneral header err_header provenance extra_info errors warnings = + unlines $ + [ warningsOrErrors <> " encountered when parsing" <> header' <> ":" + ] + ++ [p | Just p <- [provenance]] + ++ renderedErrors + ++ renderedWarnings + where + warningsOrErrors = case errors of + [] -> case warnings of + [_] -> "Warning" + _ -> "Warnings" + [_] -> "Error" + _ -> "Errors" + + header' = if null header then "" else (" " <> header) + + renderedErrors = concatMap renderError (sortBy (comparing perrorPosition) errors) + renderedWarnings = concatMap renderWarning (sortBy (comparing pwarningPosition) warnings) + + renderError :: PError -> [String] + renderError (PError pos msg) + -- if position is 0:0, then it doesn't make sense to show input + -- looks like, Parsec errors have line-feed in them + | pos == zeroPos = msgs + | otherwise = msgs ++ extra_info pos + where + msgs = ["", err_header ++ showPos pos ++ ": error:", trimLF msg, ""] + + renderWarning :: PWarning -> [String] + renderWarning (PWarning _ pos msg) + | pos == zeroPos = msgs + | otherwise = msgs ++ extra_info pos + where + msgs = ["", err_header ++ showPos pos ++ ": warning:", trimLF msg, ""] + + -- sometimes there are (especially trailing) newlines. + trimLF :: String -> String + trimLF = dropWhile (== '\n') . reverse . dropWhile (== '\n') . reverse + +data Zipper a = Zipper [a] [a] + +listToZipper :: [a] -> Zipper a +listToZipper = Zipper [] + +advance :: Int -> Zipper a -> Zipper a +advance n z@(Zipper xs ys) + | n <= 0 = z + | otherwise = case ys of + [] -> z + (y : ys') -> advance (n - 1) $ Zipper (y : xs) ys' diff --git a/cabal-install/src/Distribution/Client/ParseUtils.hs b/cabal-install/src/Distribution/Client/ParseUtils.hs index 5d4bd8331a1..54d4c622976 100644 --- a/cabal-install/src/Distribution/Client/ParseUtils.hs +++ b/cabal-install/src/Distribution/Client/ParseUtils.hs @@ -89,9 +89,9 @@ import Distribution.FieldGrammar (parseFieldGrammar, partitionFields) import qualified Distribution.FieldGrammar as FG import qualified Distribution.Fields as F import Distribution.Fields.ParseResult (runParseResult) -import Distribution.Parsec.Error (showPError) +import Distribution.Parsec.Error (showPErrorWithSource) import Distribution.Parsec.Position (Position (..)) -import Distribution.Parsec.Warning (showPWarning) +import Distribution.Parsec.Warning (showPWarningWithSource) import Distribution.Simple.Utils (fromUTF8BS, toUTF8BS) ------------------------- @@ -321,12 +321,12 @@ parseFieldsAndSections fieldDescrs sectionDescrs fgSectionDescrs = ++ show line' case runParseResult $ parseFieldGrammar cabalSpecLatest fields2 grammar of (warnings, Right b) -> do - for_ warnings $ \w -> warning $ showPWarning "???" w + for_ warnings $ \w -> warning $ showPWarningWithSource $ w setter line param b a (warnings, Left (_, errs)) -> do - for_ warnings $ \w -> warning $ showPWarning "???" w + for_ warnings $ \w -> warning $ showPWarningWithSource $ w case errs of - err :| _errs -> fail $ showPError "???" err + err :| _errs -> fail $ showPErrorWithSource $ err Nothing -> do warning $ "Unrecognized section '" diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index 5d2c3981c0e..32d8048b2b5 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -1,8 +1,10 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-unused-matches #-} -- | Handling project configuration. module Distribution.Client.ProjectConfig @@ -11,6 +13,7 @@ module Distribution.Client.ProjectConfig , ProjectConfigToParse (..) , ProjectConfigBuildOnly (..) , ProjectConfigShared (..) + , ProjectConfigSkeleton , ProjectConfigProvenance (..) , PackageConfig (..) , MapLast (..) @@ -40,6 +43,12 @@ module Distribution.Client.ProjectConfig , readSourcePackageCabalFile , readSourcePackageCabalFile' , CabalFileParseError (..) + , readProjectFileSkeleton + , ProjectFileParser (..) + , readProjectFileSkeletonLegacy + , readProjectFileSkeletonParsec + , readProjectFileSkeletonFallback + , readProjectFileSkeletonCompare -- * Packages within projects , ProjectPackageLocation (..) @@ -68,8 +77,10 @@ module Distribution.Client.ProjectConfig ) where import Distribution.Client.Compat.Prelude hiding (empty) +import Distribution.Parsec.Source import Distribution.Simple.Utils ( createDirectoryIfMissingVerbose + , debug , dieWithException , maybeExit , notice @@ -86,6 +97,7 @@ import Distribution.Client.Glob ) import Distribution.Client.JobControl import Distribution.Client.ProjectConfig.Legacy +import qualified Distribution.Client.ProjectConfig.Parsec as Parsec import Distribution.Client.ProjectConfig.Types import Distribution.Client.RebuildMonad import Distribution.Client.VCS @@ -96,6 +108,7 @@ import Distribution.Client.VCS , syncSourceRepos , validateSourceRepos ) +import Distribution.Fields.ParseResult import Distribution.Client.BuildReports.Types ( ReportLevel (..) @@ -110,6 +123,7 @@ import Distribution.Client.DistDirLayout , ProjectRoot (..) , defaultProjectFile ) +import Distribution.Client.Errors.Parser import Distribution.Client.GlobalFlags ( RepoContext (..) , withRepoContext' @@ -122,7 +136,6 @@ import Distribution.Client.HttpUtils , transportCheckHttps ) import Distribution.Client.Types -import Distribution.Client.Utils.Parsec (renderParseError) import Distribution.Solver.Types.ConstraintSource import Distribution.Solver.Types.PackageConstraint @@ -154,11 +167,6 @@ import qualified Distribution.Deprecated.ProjectParseUtils as OldParser ( ProjectParseResult (..) ) import Distribution.Fields - ( PError - , PWarning - , runParseResult - , showPWarning - ) import Distribution.Package import Distribution.PackageDescription.Parsec ( parseGenericPackageDescription @@ -751,35 +759,37 @@ withProjectOrGlobalConfig' with without = do -- file if any, plus other global config. readProjectConfig :: Verbosity + -> ProjectFileParser -> HttpTransport -> Flag Bool -- ^ @--ignore-project@ -> Flag FilePath -> DistDirLayout -> Rebuild ProjectConfigSkeleton -readProjectConfig verbosity _ (Flag True) configFileFlag _ = do +readProjectConfig verbosity parserOption _ (Flag True) configFileFlag _ = do global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag return (global <> singletonProjectConfigSkeleton defaultImplicitProjectConfig) -readProjectConfig verbosity httpTransport _ configFileFlag distDirLayout = do +readProjectConfig verbosity parserOption httpTransport _ configFileFlag distDirLayout = do global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag - local <- readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout - freeze <- readProjectLocalFreezeConfig verbosity httpTransport distDirLayout - extra <- readProjectLocalExtraConfig verbosity httpTransport distDirLayout + local <- readProjectLocalConfigOrDefault verbosity parserOption httpTransport distDirLayout + freeze <- readProjectLocalFreezeConfig verbosity parserOption httpTransport distDirLayout + extra <- readProjectLocalExtraConfig verbosity parserOption httpTransport distDirLayout return (global <> local <> freeze <> extra) -- | Reads an explicit @cabal.project@ file in the given project root dir, -- or returns the default project config for an implicitly defined project. readProjectLocalConfigOrDefault :: Verbosity + -> ProjectFileParser -> HttpTransport -> DistDirLayout -> Rebuild ProjectConfigSkeleton -readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout = do +readProjectLocalConfigOrDefault verbosity parserOption httpTransport distDirLayout = do let projectFile = distProjectFile distDirLayout "" usesExplicitProjectRoot <- liftIO $ doesFileExist projectFile if usesExplicitProjectRoot then do - readProjectFileSkeleton verbosity httpTransport distDirLayout "" "project file" + readProjectFileSkeleton parserOption verbosity httpTransport distDirLayout "" "project file" else do monitorFiles [monitorNonExistentFile projectFile] return (singletonProjectConfigSkeleton defaultImplicitProjectConfig) @@ -797,11 +807,13 @@ defaultImplicitProjectConfig = -- principle can be edited manually or by other tools. readProjectLocalExtraConfig :: Verbosity + -> ProjectFileParser -> HttpTransport -> DistDirLayout -> Rebuild ProjectConfigSkeleton -readProjectLocalExtraConfig verbosity httpTransport distDirLayout = +readProjectLocalExtraConfig verbosity parserOption httpTransport distDirLayout = readProjectFileSkeleton + parserOption verbosity httpTransport distDirLayout @@ -813,11 +825,13 @@ readProjectLocalExtraConfig verbosity httpTransport distDirLayout = -- principle can be edited manually or by other tools. readProjectLocalFreezeConfig :: Verbosity + -> ProjectFileParser -> HttpTransport -> DistDirLayout -> Rebuild ProjectConfigSkeleton -readProjectLocalFreezeConfig verbosity httpTransport distDirLayout = +readProjectLocalFreezeConfig verbosity parserOption httpTransport distDirLayout = readProjectFileSkeleton + parserOption verbosity httpTransport distDirLayout @@ -825,30 +839,148 @@ readProjectLocalFreezeConfig verbosity httpTransport distDirLayout = "project freeze file" -- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty. -readProjectFileSkeleton :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton -readProjectFileSkeleton +-- This function is generic and can be used with the legacy or parsec parser, or a combination of both. +readProjectFileSkeletonGen :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> (FilePath -> IO ProjectConfigSkeleton) -> Rebuild ProjectConfigSkeleton +readProjectFileSkeletonGen verbosity httpTransport - DistDirLayout{distProjectFile, distDownloadSrcDirectory} + dir extensionName - extensionDescription = do - exists <- liftIO $ doesFileExist extensionFile - if exists - then do - monitorFiles [monitorFileHashed extensionFile] - pcs <- liftIO readExtensionFile - monitorFiles $ map monitorFileHashed (projectConfigPathRoot <$> projectSkeletonImports pcs) - pure pcs - else do - monitorFiles [monitorNonExistentFile extensionFile] - return mempty + extensionDescription + parseConfig = + do + exists <- liftIO $ doesFileExist extensionFile + if exists + then do + monitorFiles [monitorFileHashed extensionFile] + pcs <- liftIO $ parseConfig extensionFile + monitorFiles $ map monitorFileHashed (projectConfigPathRoot <$> projectSkeletonImports pcs) + return pcs + else do + monitorFiles [monitorNonExistentFile extensionFile] + return mempty where - extensionFile = distProjectFile extensionName + extensionFile = (distProjectFile dir) extensionName + +-- There are 3 different variants of the project parsing function. +-- 1. readProjectFileSkeletonLegacy: always uses the legacy parser +-- 2. readProjectFileSkeletonParsec: always uses the parsec parser +-- 3. readProjectFileSkeletonFallback: uses the parsec parser, but if that fails, it falls back to the legacy parser. +-- 4. readProjectFileSkeletonCompare: Run both parsers, and compare the results to check they are the same. +-- +-- +-- correspondingly there are two "pure" functions to attempt to parse a project +-- file using the "legacy" or "parsec" parser. +-- +-- 1. parseProjectFileSkeletonLegacy: parses a project file using the legacy parser +-- 2. parseProjectFileSkeletonParsec: parses a project file using the parsec parser +-- +-- Errors are handled in each case by +-- +-- 1. reportParseResult: reports legacy parse errors to the user +-- 2. reportParseResultParsec: reports parsec parse errors to the user + +readProjectFileSkeleton :: ProjectFileParser -> Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton +readProjectFileSkeleton option = + case option of + LegacyParser -> readProjectFileSkeletonLegacy + ParsecParser -> readProjectFileSkeletonParsec + FallbackParser -> readProjectFileSkeletonFallback + CompareParser -> readProjectFileSkeletonCompare + +-- | Read a project file using the legacy parser. +readProjectFileSkeletonLegacy :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton +readProjectFileSkeletonLegacy verbosity httpTransport distDirLayout extensionName extensionDescription = do + readProjectFileSkeletonGen verbosity httpTransport distDirLayout extensionName extensionDescription $ \fp -> do + debug verbosity $ "Reading project file using the legacy parser" + parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout extensionName extensionDescription fp + >>= liftIO . reportParseResult verbosity extensionDescription fp + +-- | Read a project file using the parsec parser, but if that fails, it falls back to the legacy parser. +readProjectFileSkeletonFallback :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton +readProjectFileSkeletonFallback verbosity httpTransport distDirLayout extensionName extensionDescription = do + readProjectFileSkeletonGen verbosity httpTransport distDirLayout extensionName extensionDescription $ \fp -> do + debug verbosity $ "Reading project file using the fallback parser" + (res, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout extensionName extensionDescription fp + let (_, pres) = runParseResult res + case pres of + -- 1. Successful parse with parsec parser, handle the result as normal. + Right{} -> liftIO $ reportParseResultParsec verbosity fp bs res + -- 2. The parse failed with the parsec parser, fallback to the legacy parser. + Left{} -> do + lres <- parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout extensionName extensionDescription fp + case lres of + -- 3a. The legacy parser worked, but the parsec parser failed! + -- Report a warning to the user that this happened. + OldParser.ProjectParseOk{} -> do + warn verbosity $ "The new parsec parser failed, but the legacy parser worked. This is unexpected, please report this as a bug.\nThe legacy parser will be removed in the next major version." + liftIO $ reportParseResult verbosity extensionDescription fp lres + -- 3b. The legacy parser failed as well, report the original error. + OldParser.ProjectParseFailed{} -> do + liftIO $ reportParseResultParsec verbosity fp bs res + +-- | Read a project file using the parsec parser. +readProjectFileSkeletonParsec :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton +readProjectFileSkeletonParsec verbosity httpTransport distDirLayout extensionName extensionDescription = do + readProjectFileSkeletonGen verbosity httpTransport distDirLayout extensionName extensionDescription $ \fp -> do + debug verbosity $ "Reading project file using the parsec parser" + (res, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout extensionName extensionDescription fp + liftIO $ reportParseResultParsec verbosity fp bs res + +readProjectFileSkeletonCompare :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton +readProjectFileSkeletonCompare verbosity httpTransport distDirLayout extensionName extensionDescription = do + readProjectFileSkeletonGen verbosity httpTransport distDirLayout extensionName extensionDescription $ \fp -> do + debug verbosity $ "Reading project file using the comparative parser" + (pres, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout extensionName extensionDescription fp + lres <- parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout extensionName extensionDescription fp + let (_, ppres) = runParseResult pres + case (lres, ppres) of + -- 1. Both succeed, compare the results + (OldParser.ProjectParseOk lwarns lpcs, Right ppcs) -> do + unless (lpcs == ppcs) (dieWithException verbosity $ LegacyAndParsecParseResultsDiffer fp (show lpcs) (show ppcs)) + liftIO $ reportParseResultParsec verbosity fp bs pres + -- 2. The legacy parser failed, but the parsec parser succeeded. + -- Report a warning to the user that this happened. + (OldParser.ProjectParseFailed{}, Right{}) -> do + warn verbosity $ "The legacy parser failed, but the new parsec parser worked. This is unexpected, please report this as a bug.\nThe legacy parser will be removed in the next major version." + liftIO $ reportParseResult verbosity extensionDescription fp lres + -- 3. The legacy parser succeeded, but the parsec parser failed. + -- Report a warning to the user that this happened. + (OldParser.ProjectParseOk{}, Left{}) -> do + warn verbosity $ "The new parsec parser failed, but the legacy parser worked. This is unexpected, please report this as a bug.\nThe legacy parser will be removed in the next major version." + liftIO $ reportParseResult verbosity extensionDescription fp lres + (OldParser.ProjectParseFailed{}, Left{}) -> do + -- 4. Both failed, report the original error. We don't check that the same errors are reported. + liftIO $ reportParseResultParsec verbosity fp bs pres + +reportParseResultParsec + :: Verbosity + -> FilePath + -> BS.ByteString + -> Parsec.ParseResult ProjectFileSource a + -> IO a +reportParseResultParsec verbosity fpath contents pr = do + let (warnings, result) = runParseResult pr + case result of + Right x -> do + let sortKey p = (pwarningSource p, pwarningPosition (pwarning p)) + sortedWarnings = sortBy (comparing sortKey) warnings + reportProjectParseWarnings verbosity fpath (map (showPWarningWithSource . fmap renderProjectFileSource) sortedWarnings) + return x + Left (_, errors) -> do + dieWithException verbosity $ ProjectConfigParseFailure $ ProjectConfigParseError errors warnings - readExtensionFile = - reportParseResult verbosity extensionDescription extensionFile - =<< parseProject extensionFile distDownloadSrcDirectory httpTransport verbosity . ProjectConfigToParse - =<< BS.readFile extensionFile +-- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty. +parseProjectFileSkeletonLegacy :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> FilePath -> IO (OldParser.ProjectParseResult ProjectConfigSkeleton) +parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout extensionName extensionDescription extensionFile = + parseProject extensionFile (distDownloadSrcDirectory distDirLayout) httpTransport verbosity . ProjectConfigToParse + =<< BS.readFile extensionFile + +parseProjectFileSkeletonParsec :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> FilePath -> IO (Parsec.ParseResult ProjectFileSource ProjectConfigSkeleton, BS.ByteString) +parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout extensionName extensionDescription extensionFile = do + bs <- BS.readFile extensionFile + res <- Parsec.parseProject extensionFile (distDownloadSrcDirectory distDirLayout) httpTransport verbosity $ ProjectConfigToParse bs + return (res, bs) -- | Render the 'ProjectConfig' format. -- @@ -881,23 +1013,27 @@ readGlobalConfig verbosity configFileFlag = do monitorFiles [monitorFileHashed configFile] return (convertLegacyGlobalConfig config) -reportProjectParseWarnings :: Verbosity -> FilePath -> [ProjectParseWarning] -> IO () -reportProjectParseWarnings verbosity projectFile warnings = - unless (null warnings) $ - let msgs = - [ OldParser.showPWarning pFilename w - | (p, w) <- warnings - , let pFilename = fst $ unconsProjectConfigPath p - ] - in noticeDoc verbosity $ - vcat - [ (text "Warnings found while parsing the project file" <> comma) <+> (text (takeFileName projectFile) <> colon) - , cat [nest 1 $ text "-" <+> text m | m <- ordNub msgs] - ] +reportProjectParseWarningsLegacy :: Verbosity -> FilePath -> [ProjectParseWarning] -> IO () +reportProjectParseWarningsLegacy verbosity projectFile warnings = + let msgs = + [ OldParser.showPWarning pFilename w + | (p, w) <- warnings + , let pFilename = fst $ unconsProjectConfigPath p + ] + in reportProjectParseWarnings verbosity projectFile msgs + +reportProjectParseWarnings :: Verbosity -> FilePath -> [String] -> IO () +reportProjectParseWarnings verbosity projectFile msgs = + unless (null msgs) $ + noticeDoc verbosity $ + vcat + [ (text "Warnings found while parsing the project file" <> comma) <+> (text (takeFileName projectFile) <> colon) + , cat [nest 1 $ text "-" <+> text m | m <- ordNub msgs] + ] reportParseResult :: Verbosity -> String -> FilePath -> OldParser.ProjectParseResult ProjectConfigSkeleton -> IO ProjectConfigSkeleton reportParseResult verbosity _filetype projectFile (OldParser.ProjectParseOk warnings x) = do - reportProjectParseWarnings verbosity projectFile warnings + reportProjectParseWarningsLegacy verbosity projectFile warnings return x reportParseResult verbosity filetype projectFile (OldParser.ProjectParseFailed (ProjectParseError snippet rootOrImportee err)) = do let (line, msg) = OldParser.locatedErrorMsg err @@ -906,7 +1042,7 @@ reportParseResult verbosity filetype projectFile (OldParser.ProjectParseFailed ( maybe (projectFile, empty) ( \p -> - ( fst $ unconsProjectConfigPath p + ( currentProjectConfigPath p , if isTopLevelConfigPath p then empty else docProjectConfigPath p ) ) @@ -1602,43 +1738,6 @@ mkSpecificSourcePackage location pkg = , srcpkgDescrOverride = Nothing } --- | Errors reported upon failing to parse a @.cabal@ file. -data CabalFileParseError - = CabalFileParseError - FilePath - -- ^ @.cabal@ file path - BS.ByteString - -- ^ @.cabal@ file contents - (NonEmpty PError) - -- ^ errors - (Maybe Version) - -- ^ We might discover the spec version the package needs - [PWarning] - -- ^ warnings - --- | Manual instance which skips file contents -instance Show CabalFileParseError where - showsPrec d (CabalFileParseError fp _ es mv ws) = - showParen (d > 10) $ - showString "CabalFileParseError" - . showChar ' ' - . showsPrec 11 fp - . showChar ' ' - . showsPrec 11 ("" :: String) - . showChar ' ' - . showsPrec 11 es - . showChar ' ' - . showsPrec 11 mv - . showChar ' ' - . showsPrec 11 ws - -instance Exception CabalFileParseError where - displayException = renderCabalFileParseError - -renderCabalFileParseError :: CabalFileParseError -> String -renderCabalFileParseError (CabalFileParseError filePath contents errors _ warnings) = - renderParseError filePath contents errors warnings - -- | Wrapper for the @.cabal@ file parser. It reports warnings on higher -- verbosity levels and throws 'CabalFileParseError' on failure. readSourcePackageCabalFile @@ -1659,10 +1758,10 @@ readSourcePackageCabalFile' -> BS.ByteString -> IO GenericPackageDescription readSourcePackageCabalFile' logWarnings pkgfilename content = - case runParseResult (parseGenericPackageDescription content) of + case runParseResult (withSource (PCabalFile (pkgfilename, content)) $ parseGenericPackageDescription content) of (warnings, Right pkg) -> do unless (null warnings) $ - logWarnings (formatWarnings warnings) + logWarnings (formatWarnings . map (fmap renderCabalFileSource) $ warnings) return pkg (warnings, Left (mspecVersion, errors)) -> throwIO $ CabalFileParseError pkgfilename content errors mspecVersion warnings @@ -1671,7 +1770,7 @@ readSourcePackageCabalFile' logWarnings pkgfilename content = "The package description file " ++ pkgfilename ++ " has warnings: " - ++ unlines (map (showPWarning pkgfilename) warnings) + ++ unlines (map showPWarningWithSource warnings) -- | When looking for a package's @.cabal@ file we can find none, or several, -- both of which are failures. diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs b/cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs new file mode 100644 index 00000000000..591bf0ba03d --- /dev/null +++ b/cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs @@ -0,0 +1,265 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} + +-- | 'ProjectConfig' Field descriptions +module Distribution.Client.ProjectConfig.FieldGrammar + ( projectConfigFieldGrammar + , packageConfigFieldGrammar + ) where + +import qualified Data.ByteString.Char8 as BS +import qualified Data.Set as Set +import Distribution.CabalSpecVersion (CabalSpecVersion (..)) +import Distribution.Client.CmdInstall.ClientInstallFlags (clientInstallFlagsGrammar) +import qualified Distribution.Client.ProjectConfig.Lens as L +import Distribution.Client.ProjectConfig.Types (PackageConfig (..), ProjectConfig (..), ProjectConfigBuildOnly (..), ProjectConfigProvenance (..), ProjectConfigShared (..)) +import Distribution.Client.Utils.Parsec +import Distribution.Compat.Prelude +import Distribution.FieldGrammar +import Distribution.Simple.Flag +import Distribution.Simple.InstallDirs +import Distribution.Solver.Types.ConstraintSource (ConstraintSource (..)) +import Distribution.Solver.Types.ProjectConfigPath +import Distribution.Types.PackageVersionConstraint (PackageVersionConstraint (..)) + +projectConfigFieldGrammar :: ProjectConfigPath -> [String] -> ParsecFieldGrammar' ProjectConfig +projectConfigFieldGrammar source knownPrograms = + ProjectConfig + <$> monoidalFieldAla "packages" (alaList' FSep Token) L.projectPackages + <*> monoidalFieldAla "optional-packages" (alaList' FSep Token) L.projectPackagesOptional + <*> pure mempty -- source-repository-package stanza + <*> monoidalFieldAla "extra-packages" formatPackageVersionConstraints L.projectPackagesNamed + <*> blurFieldGrammar L.projectConfigBuildOnly projectConfigBuildOnlyFieldGrammar + <*> blurFieldGrammar L.projectConfigShared (projectConfigSharedFieldGrammar source) + <*> pure provenance + <*> pure mempty + -- \^ PackageConfig to be applied to all packages, specified inside 'package *' stanza + <*> blurFieldGrammar L.projectConfigLocalPackages (packageConfigFieldGrammar knownPrograms) + -- \^ PackageConfig to be applied to locally built packages, specified not inside a stanza + <*> pure mempty + where + -- \^ PackageConfig applied to explicitly named packages + provenance = Set.singleton (Explicit source) + +formatPackageVersionConstraints :: [PackageVersionConstraint] -> List CommaVCat (Identity PackageVersionConstraint) PackageVersionConstraint +formatPackageVersionConstraints = alaList CommaVCat + +projectConfigBuildOnlyFieldGrammar :: ParsecFieldGrammar' ProjectConfigBuildOnly +projectConfigBuildOnlyFieldGrammar = + ProjectConfigBuildOnly + <$> optionalFieldDef "verbose" L.projectConfigVerbosity mempty + <*> pure mempty -- cli flag: projectConfigDryRun + <*> pure mempty -- cli flag: projectConfigOnlyDeps + <*> pure mempty -- cli flag: projectConfigOnlyDownload + <*> monoidalFieldAla "build-summary" (alaNubList VCat) L.projectConfigSummaryFile + <*> optionalFieldDef "build-log" L.projectConfigLogFile mempty + <*> optionalFieldDef "remote-build-reporting" L.projectConfigBuildReports mempty + <*> optionalFieldDef "report-planning-failure" L.projectConfigReportPlanningFailure mempty + <*> optionalFieldDefAla "symlink-bindir" (alaFlag FilePathNT) L.projectConfigSymlinkBinDir mempty + <*> optionalFieldDefAla "jobs" (alaFlag NumJobs) L.projectConfigNumJobs mempty + <*> optionalFieldDef "semaphore" L.projectConfigUseSemaphore mempty + <*> optionalFieldDef "keep-going" L.projectConfigKeepGoing mempty + <*> optionalFieldDef "offline" L.projectConfigOfflineMode mempty + <*> optionalFieldDef "haddock-keep-temp-files" L.projectConfigKeepTempFiles mempty + <*> optionalFieldDefAla "http-transport" (alaFlag Token) L.projectConfigHttpTransport mempty + <*> optionalFieldDef "ignore-expiry" L.projectConfigIgnoreExpiry mempty + <*> optionalFieldDefAla "remote-repo-cache" (alaFlag FilePathNT) L.projectConfigCacheDir mempty + <*> optionalFieldDefAla "logs-dir" (alaFlag FilePathNT) L.projectConfigLogsDir mempty + <*> blurFieldGrammar L.projectConfigClientInstallFlags clientInstallFlagsGrammar + +projectConfigSharedFieldGrammar :: ProjectConfigPath -> ParsecFieldGrammar' ProjectConfigShared +projectConfigSharedFieldGrammar source = + ProjectConfigShared + <$> optionalFieldDefAla "builddir" (alaFlag FilePathNT) L.projectConfigDistDir mempty + <*> pure mempty -- cli flag: projectConfigConfigFile + <*> optionalFieldDefAla "project-dir" (alaFlag FilePathNT) L.projectConfigProjectDir mempty + <*> optionalFieldDefAla "project-file" (alaFlag FilePathNT) L.projectConfigProjectFile mempty + <*> pure mempty -- You can't set the parser type in the project file. + <*> optionalFieldDef "ignore-project" L.projectConfigIgnoreProject mempty + <*> optionalFieldDef "compiler" L.projectConfigHcFlavor mempty + <*> optionalFieldDefAla "with-compiler" (alaFlag FilePathNT) L.projectConfigHcPath mempty + <*> optionalFieldDefAla "with-hc-pkg" (alaFlag FilePathNT) L.projectConfigHcPkg mempty + <*> optionalFieldDef "doc-index-file" L.projectConfigHaddockIndex mempty + <*> blurFieldGrammar L.projectConfigInstallDirs installDirsGrammar + <*> monoidalFieldAla "package-dbs" (alaList' CommaFSep PackageDBNT) L.projectConfigPackageDBs + <*> pure mempty -- repository stanza for projectConfigRemoteRepos + <*> pure mempty -- repository stanza for projectConfigLocalNoIndexRepos + <*> monoidalField "active-repositories" L.projectConfigActiveRepos + <*> monoidalField "index-state" L.projectConfigIndexState + <*> optionalFieldDefAla "store-dir" (alaFlag FilePathNT) L.projectConfigStoreDir mempty + <*> monoidalFieldAla "constraints" (alaList' FSep ProjectConstraints) L.projectConfigConstraints + ^^^ (fmap . fmap) (\(userConstraint, _) -> (userConstraint, ConstraintSourceProjectConfig source)) + <*> monoidalFieldAla "preferences" formatPackageVersionConstraints L.projectConfigPreferences + <*> optionalFieldDef "cabal-lib-version" L.projectConfigCabalVersion mempty + <*> optionalFieldDef "solver" L.projectConfigSolver mempty + <*> monoidalFieldAla "allow-older" AllowOlderNT L.projectConfigAllowOlder + <*> monoidalFieldAla "allow-newer" AllowNewerNT L.projectConfigAllowNewer + <*> optionalFieldDef "write-ghc-environment-files" L.projectConfigWriteGhcEnvironmentFilesPolicy mempty + <*> optionalFieldDefAla "max-backjumps" (alaFlag MaxBackjumps) L.projectConfigMaxBackjumps mempty + <*> optionalFieldDef "reorder-goals" L.projectConfigReorderGoals mempty + <*> optionalFieldDef "count-conflicts" L.projectConfigCountConflicts mempty + <*> optionalFieldDef "fine-grained-conflicts" L.projectConfigFineGrainedConflicts mempty + <*> optionalFieldDef "minimize-conflict-set" L.projectConfigMinimizeConflictSet mempty + <*> optionalFieldDef "strong-flags" L.projectConfigStrongFlags mempty + <*> optionalFieldDef "allow-boot-library-installs" L.projectConfigAllowBootLibInstalls mempty + <*> optionalFieldDef "reject-unconstrained-dependencies" L.projectConfigOnlyConstrained mempty + <*> optionalFieldDef "per-component" L.projectConfigPerComponent mempty + <*> optionalFieldDef "independent-goals" L.projectConfigIndependentGoals mempty + <*> optionalFieldDef "prefer-oldest" L.projectConfigPreferOldest mempty + <*> monoidalFieldAla "extra-prog-path-shared-only" (alaNubList' FSep FilePathNT) L.projectConfigProgPathExtra + <*> optionalFieldDef "multi-repl" L.projectConfigMultiRepl mempty + +packageConfigFieldGrammar :: [String] -> ParsecFieldGrammar' PackageConfig +packageConfigFieldGrammar knownPrograms = + mkPackageConfig + <$> optionalFieldDef "haddock-all" noopLens mempty + ^^^ hiddenField + <*> pure mempty -- program-options stanza + <*> pure mempty -- program-locations stanza + <*> monoidalFieldAla "extra-prog-path" (alaNubList' FSep FilePathNT) L.packageConfigProgramPathExtra + <*> monoidalField "flags" L.packageConfigFlagAssignment + <*> optionalFieldDef "library-vanilla" L.packageConfigVanillaLib mempty + <*> optionalFieldDef "shared" L.packageConfigSharedLib mempty + <*> optionalFieldDef "static" L.packageConfigStaticLib mempty + <*> optionalFieldDef "executable-dynamic" L.packageConfigDynExe mempty + <*> optionalFieldDef "executable-static" L.packageConfigFullyStaticExe mempty + <*> optionalFieldDef "profiling" L.packageConfigProf mempty + <*> optionalFieldDef "library-profiling" L.packageConfigProfLib mempty + <*> optionalFieldDef "profiling-shared" L.packageConfigProfShared mempty + <*> optionalFieldDef "executable-profiling" L.packageConfigProfExe mempty + <*> optionalFieldDef "profiling-detail" L.packageConfigProfDetail mempty + <*> optionalFieldDef "library-profiling-detail" L.packageConfigProfLibDetail mempty + <*> monoidalFieldAla "configure-options" (alaList' NoCommaFSep Token) L.packageConfigConfigureArgs + <*> optionalFieldDef "optimization" L.packageConfigOptimization mempty + <*> optionalFieldDef "program-prefix" L.packageConfigProgPrefix mempty + <*> optionalFieldDef "program-suffix" L.packageConfigProgSuffix mempty + <*> monoidalFieldAla "extra-lib-dirs" (alaList' FSep FilePathNT) L.packageConfigExtraLibDirs + <*> monoidalFieldAla "extra-lib-dirs-static" (alaList' FSep FilePathNT) L.packageConfigExtraLibDirsStatic + <*> monoidalFieldAla "extra-framework-dirs" (alaList' FSep FilePathNT) L.packageConfigExtraFrameworkDirs + <*> monoidalFieldAla "extra-include-dirs" (alaList' FSep FilePathNT) L.packageConfigExtraIncludeDirs + <*> optionalFieldDef "library-for-ghci" L.packageConfigGHCiLib mempty + <*> optionalFieldDef "split-sections" L.packageConfigSplitSections mempty + <*> optionalFieldDef "split-objs" L.packageConfigSplitObjs mempty + <*> optionalFieldDef "executable-stripping" L.packageConfigStripExes mempty + <*> optionalFieldDef "library-stripping" L.packageConfigStripLibs mempty + <*> optionalFieldDef "tests" L.packageConfigTests mempty + <*> optionalFieldDef "benchmarks" L.packageConfigBenchmarks mempty + <*> packageConfigCoverageGrammar + <*> optionalFieldDef "relocatable" L.packageConfigRelocatable mempty + <*> optionalFieldDef "debug-info" L.packageConfigDebugInfo mempty + <*> optionalFieldDef "build-info" L.packageConfigDumpBuildInfo mempty + <*> optionalFieldDef "run-tests" L.packageConfigRunTests mempty + <*> optionalFieldDef "documentation" L.packageConfigDocumentation mempty + <*> optionalFieldDef "haddock-hoogle" L.packageConfigHaddockHoogle mempty + <*> optionalFieldDef "haddock-html" L.packageConfigHaddockHtml mempty + <*> optionalFieldDefAla "haddock-html-location" (alaFlag Token) L.packageConfigHaddockHtmlLocation mempty + <*> optionalFieldDef "haddock-foreign-libraries" L.packageConfigHaddockForeignLibs mempty + <*> optionalFieldDef "haddock-executables" L.packageConfigHaddockExecutables mempty + <*> optionalFieldDef "haddock-tests" L.packageConfigHaddockTestSuites mempty + <*> optionalFieldDef "haddock-benchmarks" L.packageConfigHaddockBenchmarks mempty + <*> optionalFieldDef "haddock-internal" L.packageConfigHaddockInternal mempty + <*> optionalFieldDefAla "haddock-css" (alaFlag FilePathNT) L.packageConfigHaddockCss mempty + <*> optionalFieldDef "haddock-hyperlink-source" L.packageConfigHaddockLinkedSource mempty + <*> optionalFieldDef "haddock-quickjump" L.packageConfigHaddockQuickJump mempty + <*> optionalFieldDefAla "haddock-hscolour-css" (alaFlag FilePathNT) L.packageConfigHaddockHscolourCss mempty + <*> optionalFieldDef "haddock-contents-location" L.packageConfigHaddockContents mempty + <*> optionalFieldDef "haddock-index-location" L.packageConfigHaddockIndex mempty + <*> optionalFieldDefAla "haddock-base-url" (alaFlag Token) L.packageConfigHaddockBaseUrl mempty + <*> optionalFieldDefAla "haddock-resources-dir" (alaFlag Token) L.packageConfigHaddockResourcesDir mempty + <*> optionalFieldDefAla "haddock-output-dir" (alaFlag FilePathNT) L.packageConfigHaddockOutputDir mempty + <*> optionalFieldDef "haddock-use-unicode" L.packageConfigHaddockUseUnicode mempty + <*> optionalFieldDef "haddock-for-hackage" L.packageConfigHaddockForHackage mempty + <*> optionalFieldDef "test-log" L.packageConfigTestHumanLog mempty + <*> optionalFieldDef "test-machine-log" L.packageConfigTestMachineLog mempty + <*> optionalFieldDef "test-show-details" L.packageConfigTestShowDetails mempty + <*> optionalFieldDef "test-keep-tix-files" L.packageConfigTestKeepTix mempty + <*> optionalFieldDefAla "test-wrapper" (alaFlag FilePathNT) L.packageConfigTestWrapper mempty + <*> optionalFieldDef "test-fail-when-no-test-suites" L.packageConfigTestFailWhenNoTestSuites mempty + <*> monoidalFieldAla "test-options" (alaList NoCommaFSep) L.packageConfigTestTestOptions + <*> monoidalFieldAla "benchmark-options" (alaList NoCommaFSep) L.packageConfigBenchmarkOptions + -- A PackageConfig may contain -options and -location fields inside a package * (projectConfigAllPackages) or package stanza (packageConfigSpecificPackage). + -- When declared at top level (packageConfigLocalPackages), the PackageConfig must contain a program-options stanza/program-locations for these fields. + <* traverse_ (knownField . BS.pack . (<> "-options")) knownPrograms + <* traverse_ (knownField . BS.pack . (<> "-location")) knownPrograms + where + noopLens f s = s <$ f mempty + mkPackageConfig + haddockAll + packageConfigProgramPaths + packageConfigProgramArgs + packageConfigProgramPathExtra + packageConfigFlagAssignment + packageConfigVanillaLib + packageConfigSharedLib + packageConfigStaticLib + packageConfigDynExe + packageConfigFullyStaticExe + packageConfigProf + packageConfigProfLib + packageConfigProfShared + packageConfigProfExe + packageConfigProfDetail + packageConfigProfLibDetail + packageConfigConfigureArgs + packageConfigOptimization + packageConfigProgPrefix + packageConfigProgSuffix + packageConfigExtraLibDirs + packageConfigExtraLibDirsStatic + packageConfigExtraFrameworkDirs + packageConfigExtraIncludeDirs + packageConfigGHCiLib + packageConfigSplitSections + packageConfigSplitObjs + packageConfigStripExes + packageConfigStripLibs + packageConfigTests + packageConfigBenchmarks + packageConfigCoverage + packageConfigRelocatable + packageConfigDebugInfo + packageConfigDumpBuildInfo + packageConfigRunTests + packageConfigDocumentation + packageConfigHaddockHoogle + packageConfigHaddockHtml + packageConfigHaddockHtmlLocation + packageConfigHaddockForeignLibs' + packageConfigHaddockExecutables' + packageConfigHaddockTestSuites' + packageConfigHaddockBenchmarks' + packageConfigHaddockInternal + packageConfigHaddockCss + packageConfigHaddockLinkedSource + packageConfigHaddockQuickJump + packageConfigHaddockHscolourCss + packageConfigHaddockContents + packageConfigHaddockIndex + packageConfigHaddockBaseUrl + packageConfigHaddockResourcesDir + packageConfigHaddockOutputDir + packageConfigHaddockUseUnicode + packageConfigHaddockForHackage + packageConfigTestHumanLog + packageConfigTestMachineLog + packageConfigTestShowDetails + packageConfigTestKeepTix + packageConfigTestWrapper + packageConfigTestFailWhenNoTestSuites + packageConfigTestTestOptions + packageConfigBenchmarkOptions = + PackageConfig + { -- The haddock-al` field provides a default value, but explicit declarations can override it + packageConfigHaddockForeignLibs = haddockAll <> packageConfigHaddockForeignLibs' + , packageConfigHaddockExecutables = haddockAll <> packageConfigHaddockExecutables' + , packageConfigHaddockTestSuites = haddockAll <> packageConfigHaddockTestSuites' + , packageConfigHaddockBenchmarks = haddockAll <> packageConfigHaddockBenchmarks' + , .. + } + +packageConfigCoverageGrammar :: ParsecFieldGrammar PackageConfig (Distribution.Simple.Flag.Flag Bool) +packageConfigCoverageGrammar = + (<>) + <$> optionalFieldDef "coverage" L.packageConfigCoverage mempty + <*> optionalFieldDef "library-coverage" L.packageConfigCoverage mempty + ^^^ deprecatedSince CabalSpecV1_22 "Please use 'coverage' field instead." diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 2e24a493d0e..32b3670b479 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -771,6 +771,7 @@ convertLegacyAllPackageFlags globalFlags configFlags configExFlags installFlags { flagProjectDir = projectConfigProjectDir , flagProjectFile = projectConfigProjectFile , flagIgnoreProject = projectConfigIgnoreProject + , flagProjectFileParser = projectConfigProjectFileParser } = projectFlags -- | Helper used by other conversion functions that returns the @@ -1070,6 +1071,7 @@ convertToLegacySharedConfig { flagProjectDir = projectConfigProjectDir , flagProjectFile = projectConfigProjectFile , flagIgnoreProject = projectConfigIgnoreProject + , flagProjectFileParser = projectConfigProjectFileParser } convertToLegacyAllPackageConfig :: ProjectConfig -> LegacyPackageConfig diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs new file mode 100644 index 00000000000..03e05835cd6 --- /dev/null +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs @@ -0,0 +1,557 @@ +module Distribution.Client.ProjectConfig.Lens where + +import Distribution.Client.BuildReports.Types (ReportLevel (..)) +import Distribution.Client.CmdInstall.ClientInstallFlags (ClientInstallFlags (..)) +import Distribution.Client.Dependency.Types (PreSolver (..)) +import Distribution.Client.IndexUtils.ActiveRepos + ( ActiveRepos + ) +import Distribution.Client.IndexUtils.IndexState (TotalIndexState) +import Distribution.Client.ProjectConfig.Types (MapLast, MapMappend, PackageConfig, ProjectConfig (..), ProjectConfigBuildOnly (..), ProjectConfigProvenance, ProjectConfigShared) +import qualified Distribution.Client.ProjectConfig.Types as T +import Distribution.Client.Targets (UserConstraint) +import Distribution.Client.Types.AllowNewer (AllowNewer, AllowOlder) +import Distribution.Client.Types.Repo (LocalRepo, RemoteRepo) +import Distribution.Client.Types.SourceRepo (SourceRepoList) +import Distribution.Client.Types.WriteGhcEnvironmentFilesPolicy (WriteGhcEnvironmentFilesPolicy) +import Distribution.Compat.Lens +import Distribution.Compat.Prelude +import Distribution.Compiler (CompilerFlavor (..)) +import Distribution.Package + ( PackageName + ) +import Distribution.PackageDescription + ( FlagAssignment + ) +import Distribution.Simple.Compiler + ( DebugInfoLevel (..) + , OptimisationLevel (..) + , PackageDBCWD + , ProfDetailLevel + ) +import Distribution.Simple.InstallDirs + ( InstallDirs + , PathTemplate + ) +import Distribution.Simple.Setup + ( DumpBuildInfo (..) + , Flag + , HaddockTarget (..) + , TestShowDetails (..) + ) +import Distribution.Solver.Types.ConstraintSource (ConstraintSource) +import Distribution.Solver.Types.Settings + ( AllowBootLibInstalls (..) + , CountConflicts (..) + , FineGrainedConflicts (..) + , IndependentGoals (..) + , MinimizeConflictSet (..) + , OnlyConstrained (..) + , PreferOldest (..) + , ReorderGoals (..) + , StrongFlags (..) + ) +import Distribution.Types.PackageVersionConstraint + ( PackageVersionConstraint + ) +import Distribution.Types.Version (Version) +import Distribution.Utils.NubList + ( NubList + ) +import Distribution.Verbosity + +projectPackages :: Lens' ProjectConfig [String] +projectPackages f s = fmap (\x -> s{T.projectPackages = x}) (f (T.projectPackages s)) +{-# INLINEABLE projectPackages #-} + +projectPackagesOptional :: Lens' ProjectConfig [String] +projectPackagesOptional f s = fmap (\x -> s{T.projectPackagesOptional = x}) (f (T.projectPackagesOptional s)) +{-# INLINEABLE projectPackagesOptional #-} + +projectPackagesRepo :: Lens' ProjectConfig [SourceRepoList] +projectPackagesRepo f s = fmap (\x -> s{T.projectPackagesRepo = x}) (f (T.projectPackagesRepo s)) +{-# INLINEABLE projectPackagesRepo #-} + +projectPackagesNamed :: Lens' ProjectConfig [PackageVersionConstraint] +projectPackagesNamed f s = fmap (\x -> s{T.projectPackagesNamed = x}) (f (T.projectPackagesNamed s)) +{-# INLINEABLE projectPackagesNamed #-} + +projectConfigBuildOnly :: Lens' ProjectConfig ProjectConfigBuildOnly +projectConfigBuildOnly f s = fmap (\x -> s{T.projectConfigBuildOnly = x}) (f (T.projectConfigBuildOnly s)) +{-# INLINEABLE projectConfigBuildOnly #-} + +projectConfigShared :: Lens' ProjectConfig ProjectConfigShared +projectConfigShared f s = fmap (\x -> s{T.projectConfigShared = x}) (f (T.projectConfigShared s)) +{-# INLINEABLE projectConfigShared #-} + +projectConfigProvenance :: Lens' ProjectConfig (Set ProjectConfigProvenance) +projectConfigProvenance f s = fmap (\x -> s{T.projectConfigProvenance = x}) (f (T.projectConfigProvenance s)) +{-# INLINEABLE projectConfigProvenance #-} + +projectConfigAllPackages :: Lens' ProjectConfig PackageConfig +projectConfigAllPackages f s = fmap (\x -> s{T.projectConfigAllPackages = x}) (f (T.projectConfigAllPackages s)) +{-# INLINEABLE projectConfigAllPackages #-} + +projectConfigLocalPackages :: Lens' ProjectConfig PackageConfig +projectConfigLocalPackages f s = fmap (\x -> s{T.projectConfigLocalPackages = x}) (f (T.projectConfigLocalPackages s)) +{-# INLINEABLE projectConfigLocalPackages #-} + +projectConfigSpecificPackage :: Lens' ProjectConfig (MapMappend PackageName PackageConfig) +projectConfigSpecificPackage f s = fmap (\x -> s{T.projectConfigSpecificPackage = x}) (f (T.projectConfigSpecificPackage s)) +{-# INLINEABLE projectConfigSpecificPackage #-} + +projectConfigVerbosity :: Lens' ProjectConfigBuildOnly (Flag Verbosity) +projectConfigVerbosity f s = fmap (\x -> s{T.projectConfigVerbosity = x}) (f (T.projectConfigVerbosity s)) +{-# INLINEABLE projectConfigVerbosity #-} + +projectConfigSummaryFile :: Lens' ProjectConfigBuildOnly (NubList PathTemplate) +projectConfigSummaryFile f s = fmap (\x -> s{T.projectConfigSummaryFile = x}) (f (T.projectConfigSummaryFile s)) +{-# INLINEABLE projectConfigSummaryFile #-} + +projectConfigLogFile :: Lens' ProjectConfigBuildOnly (Flag PathTemplate) +projectConfigLogFile f s = fmap (\x -> s{T.projectConfigLogFile = x}) (f (T.projectConfigLogFile s)) +{-# INLINEABLE projectConfigLogFile #-} + +projectConfigBuildReports :: Lens' ProjectConfigBuildOnly (Flag ReportLevel) +projectConfigBuildReports f s = fmap (\x -> s{T.projectConfigBuildReports = x}) (f (T.projectConfigBuildReports s)) +{-# INLINEABLE projectConfigBuildReports #-} + +projectConfigReportPlanningFailure :: Lens' ProjectConfigBuildOnly (Flag Bool) +projectConfigReportPlanningFailure f s = fmap (\x -> s{T.projectConfigReportPlanningFailure = x}) (f (T.projectConfigReportPlanningFailure s)) +{-# INLINEABLE projectConfigReportPlanningFailure #-} + +projectConfigSymlinkBinDir :: Lens' ProjectConfigBuildOnly (Flag FilePath) +projectConfigSymlinkBinDir f s = fmap (\x -> s{T.projectConfigSymlinkBinDir = x}) (f (T.projectConfigSymlinkBinDir s)) +{-# INLINEABLE projectConfigSymlinkBinDir #-} + +projectConfigNumJobs :: Lens' ProjectConfigBuildOnly (Flag (Maybe Int)) +projectConfigNumJobs f s = fmap (\x -> s{T.projectConfigNumJobs = x}) (f (T.projectConfigNumJobs s)) +{-# INLINEABLE projectConfigNumJobs #-} + +projectConfigUseSemaphore :: Lens' ProjectConfigBuildOnly (Flag Bool) +projectConfigUseSemaphore f s = fmap (\x -> s{T.projectConfigUseSemaphore = x}) (f (T.projectConfigUseSemaphore s)) +{-# INLINEABLE projectConfigUseSemaphore #-} + +projectConfigKeepGoing :: Lens' ProjectConfigBuildOnly (Flag Bool) +projectConfigKeepGoing f s = fmap (\x -> s{T.projectConfigKeepGoing = x}) (f (T.projectConfigKeepGoing s)) +{-# INLINEABLE projectConfigKeepGoing #-} + +projectConfigOfflineMode :: Lens' ProjectConfigBuildOnly (Flag Bool) +projectConfigOfflineMode f s = fmap (\x -> s{T.projectConfigOfflineMode = x}) (f (T.projectConfigOfflineMode s)) +{-# INLINEABLE projectConfigOfflineMode #-} + +projectConfigKeepTempFiles :: Lens' ProjectConfigBuildOnly (Flag Bool) +projectConfigKeepTempFiles f s = fmap (\x -> s{T.projectConfigKeepTempFiles = x}) (f (T.projectConfigKeepTempFiles s)) +{-# INLINEABLE projectConfigKeepTempFiles #-} + +projectConfigHttpTransport :: Lens' ProjectConfigBuildOnly (Flag String) +projectConfigHttpTransport f s = fmap (\x -> s{T.projectConfigHttpTransport = x}) (f (T.projectConfigHttpTransport s)) +{-# INLINEABLE projectConfigHttpTransport #-} + +projectConfigIgnoreExpiry :: Lens' ProjectConfigBuildOnly (Flag Bool) +projectConfigIgnoreExpiry f s = fmap (\x -> s{T.projectConfigIgnoreExpiry = x}) (f (T.projectConfigIgnoreExpiry s)) +{-# INLINEABLE projectConfigIgnoreExpiry #-} + +projectConfigCacheDir :: Lens' ProjectConfigBuildOnly (Flag FilePath) +projectConfigCacheDir f s = fmap (\x -> s{T.projectConfigCacheDir = x}) (f (T.projectConfigCacheDir s)) +{-# INLINEABLE projectConfigCacheDir #-} + +projectConfigLogsDir :: Lens' ProjectConfigBuildOnly (Flag FilePath) +projectConfigLogsDir f s = fmap (\x -> s{T.projectConfigLogsDir = x}) (f (T.projectConfigLogsDir s)) +{-# INLINEABLE projectConfigLogsDir #-} + +projectConfigClientInstallFlags :: Lens' ProjectConfigBuildOnly (ClientInstallFlags) +projectConfigClientInstallFlags f s = fmap (\x -> s{T.projectConfigClientInstallFlags = x}) (f (T.projectConfigClientInstallFlags s)) +{-# INLINEABLE projectConfigClientInstallFlags #-} + +projectConfigDistDir :: Lens' ProjectConfigShared (Flag FilePath) +projectConfigDistDir f s = fmap (\x -> s{T.projectConfigDistDir = x}) (f (T.projectConfigDistDir s)) +{-# INLINEABLE projectConfigDistDir #-} + +projectConfigProjectDir :: Lens' ProjectConfigShared (Flag FilePath) +projectConfigProjectDir f s = fmap (\x -> s{T.projectConfigProjectDir = x}) (f (T.projectConfigProjectDir s)) +{-# INLINEABLE projectConfigProjectDir #-} + +projectConfigStoreDir :: Lens' ProjectConfigShared (Flag FilePath) +projectConfigStoreDir f s = fmap (\x -> s{T.projectConfigStoreDir = x}) (f (T.projectConfigStoreDir s)) +{-# INLINEABLE projectConfigStoreDir #-} + +projectConfigPerComponent :: Lens' ProjectConfigShared (Flag Bool) +projectConfigPerComponent f s = fmap (\x -> s{T.projectConfigPerComponent = x}) (f (T.projectConfigPerComponent s)) +{-# INLINEABLE projectConfigPerComponent #-} + +projectConfigIndependentGoals :: Lens' ProjectConfigShared (Flag IndependentGoals) +projectConfigIndependentGoals f s = fmap (\x -> s{T.projectConfigIndependentGoals = x}) (f (T.projectConfigIndependentGoals s)) +{-# INLINEABLE projectConfigIndependentGoals #-} + +projectConfigProjectFile :: Lens' ProjectConfigShared (Flag FilePath) +projectConfigProjectFile f s = fmap (\x -> s{T.projectConfigProjectFile = x}) (f (T.projectConfigProjectFile s)) +{-# INLINEABLE projectConfigProjectFile #-} + +projectConfigIgnoreProject :: Lens' ProjectConfigShared (Flag Bool) +projectConfigIgnoreProject f s = fmap (\x -> s{T.projectConfigIgnoreProject = x}) (f (T.projectConfigIgnoreProject s)) +{-# INLINEABLE projectConfigIgnoreProject #-} + +projectConfigHcFlavor :: Lens' ProjectConfigShared (Flag CompilerFlavor) +projectConfigHcFlavor f s = fmap (\x -> s{T.projectConfigHcFlavor = x}) (f (T.projectConfigHcFlavor s)) +{-# INLINEABLE projectConfigHcFlavor #-} + +projectConfigHcPath :: Lens' ProjectConfigShared (Flag FilePath) +projectConfigHcPath f s = fmap (\x -> s{T.projectConfigHcPath = x}) (f (T.projectConfigHcPath s)) +{-# INLINEABLE projectConfigHcPath #-} + +projectConfigHcPkg :: Lens' ProjectConfigShared (Flag FilePath) +projectConfigHcPkg f s = fmap (\x -> s{T.projectConfigHcPkg = x}) (f (T.projectConfigHcPkg s)) +{-# INLINEABLE projectConfigHcPkg #-} + +projectConfigHaddockIndex :: Lens' ProjectConfigShared (Flag PathTemplate) +projectConfigHaddockIndex f s = fmap (\x -> s{T.projectConfigHaddockIndex = x}) (f (T.projectConfigHaddockIndex s)) +{-# INLINEABLE projectConfigHaddockIndex #-} + +projectConfigInstallDirs :: Lens' ProjectConfigShared (InstallDirs (Flag PathTemplate)) +projectConfigInstallDirs f s = fmap (\x -> s{T.projectConfigInstallDirs = x}) (f (T.projectConfigInstallDirs s)) +{-# INLINEABLE projectConfigInstallDirs #-} + +projectConfigPackageDBs :: Lens' ProjectConfigShared [Maybe PackageDBCWD] +projectConfigPackageDBs f s = fmap (\x -> s{T.projectConfigPackageDBs = x}) (f (T.projectConfigPackageDBs s)) +{-# INLINEABLE projectConfigPackageDBs #-} + +projectConfigLocalNoIndexRepos :: Lens' ProjectConfigShared (NubList LocalRepo) +projectConfigLocalNoIndexRepos f s = fmap (\x -> s{T.projectConfigLocalNoIndexRepos = x}) (f (T.projectConfigLocalNoIndexRepos s)) +{-# INLINEABLE projectConfigLocalNoIndexRepos #-} + +projectConfigRemoteRepos :: Lens' ProjectConfigShared (NubList RemoteRepo) +projectConfigRemoteRepos f s = fmap (\x -> s{T.projectConfigRemoteRepos = x}) (f (T.projectConfigRemoteRepos s)) +{-# INLINEABLE projectConfigRemoteRepos #-} + +projectConfigActiveRepos :: Lens' ProjectConfigShared (Flag ActiveRepos) +projectConfigActiveRepos f s = fmap (\x -> s{T.projectConfigActiveRepos = x}) (f (T.projectConfigActiveRepos s)) +{-# INLINEABLE projectConfigActiveRepos #-} + +projectConfigIndexState :: Lens' ProjectConfigShared (Flag TotalIndexState) +projectConfigIndexState f s = fmap (\x -> s{T.projectConfigIndexState = x}) (f (T.projectConfigIndexState s)) +{-# INLINEABLE projectConfigIndexState #-} + +projectConfigConstraints :: Lens' ProjectConfigShared [(UserConstraint, ConstraintSource)] +projectConfigConstraints f s = fmap (\x -> s{T.projectConfigConstraints = x}) (f (T.projectConfigConstraints s)) +{-# INLINEABLE projectConfigConstraints #-} + +projectConfigPreferences :: Lens' ProjectConfigShared [PackageVersionConstraint] +projectConfigPreferences f s = fmap (\x -> s{T.projectConfigPreferences = x}) (f (T.projectConfigPreferences s)) +{-# INLINEABLE projectConfigPreferences #-} + +projectConfigCabalVersion :: Lens' ProjectConfigShared (Flag Version) +projectConfigCabalVersion f s = fmap (\x -> s{T.projectConfigCabalVersion = x}) (f (T.projectConfigCabalVersion s)) +{-# INLINEABLE projectConfigCabalVersion #-} + +projectConfigSolver :: Lens' ProjectConfigShared (Flag PreSolver) +projectConfigSolver f s = fmap (\x -> s{T.projectConfigSolver = x}) (f (T.projectConfigSolver s)) +{-# INLINEABLE projectConfigSolver #-} + +projectConfigAllowOlder :: Lens' ProjectConfigShared (Maybe AllowOlder) +projectConfigAllowOlder f s = fmap (\x -> s{T.projectConfigAllowOlder = x}) (f (T.projectConfigAllowOlder s)) +{-# INLINEABLE projectConfigAllowOlder #-} + +projectConfigAllowNewer :: Lens' ProjectConfigShared (Maybe AllowNewer) +projectConfigAllowNewer f s = fmap (\x -> s{T.projectConfigAllowNewer = x}) (f (T.projectConfigAllowNewer s)) +{-# INLINEABLE projectConfigAllowNewer #-} + +projectConfigWriteGhcEnvironmentFilesPolicy :: Lens' ProjectConfigShared (Flag WriteGhcEnvironmentFilesPolicy) +projectConfigWriteGhcEnvironmentFilesPolicy f s = fmap (\x -> s{T.projectConfigWriteGhcEnvironmentFilesPolicy = x}) (f (T.projectConfigWriteGhcEnvironmentFilesPolicy s)) +{-# INLINEABLE projectConfigWriteGhcEnvironmentFilesPolicy #-} + +projectConfigMaxBackjumps :: Lens' ProjectConfigShared (Flag Int) +projectConfigMaxBackjumps f s = fmap (\x -> s{T.projectConfigMaxBackjumps = x}) (f (T.projectConfigMaxBackjumps s)) +{-# INLINEABLE projectConfigMaxBackjumps #-} + +projectConfigReorderGoals :: Lens' ProjectConfigShared (Flag ReorderGoals) +projectConfigReorderGoals f s = fmap (\x -> s{T.projectConfigReorderGoals = x}) (f (T.projectConfigReorderGoals s)) +{-# INLINEABLE projectConfigReorderGoals #-} + +projectConfigCountConflicts :: Lens' ProjectConfigShared (Flag CountConflicts) +projectConfigCountConflicts f s = fmap (\x -> s{T.projectConfigCountConflicts = x}) (f (T.projectConfigCountConflicts s)) +{-# INLINEABLE projectConfigCountConflicts #-} + +projectConfigFineGrainedConflicts :: Lens' ProjectConfigShared (Flag FineGrainedConflicts) +projectConfigFineGrainedConflicts f s = fmap (\x -> s{T.projectConfigFineGrainedConflicts = x}) (f (T.projectConfigFineGrainedConflicts s)) +{-# INLINEABLE projectConfigFineGrainedConflicts #-} + +projectConfigMinimizeConflictSet :: Lens' ProjectConfigShared (Flag MinimizeConflictSet) +projectConfigMinimizeConflictSet f s = fmap (\x -> s{T.projectConfigMinimizeConflictSet = x}) (f (T.projectConfigMinimizeConflictSet s)) +{-# INLINEABLE projectConfigMinimizeConflictSet #-} + +projectConfigStrongFlags :: Lens' ProjectConfigShared (Flag StrongFlags) +projectConfigStrongFlags f s = fmap (\x -> s{T.projectConfigStrongFlags = x}) (f (T.projectConfigStrongFlags s)) +{-# INLINEABLE projectConfigStrongFlags #-} + +projectConfigAllowBootLibInstalls :: Lens' ProjectConfigShared (Flag AllowBootLibInstalls) +projectConfigAllowBootLibInstalls f s = fmap (\x -> s{T.projectConfigAllowBootLibInstalls = x}) (f (T.projectConfigAllowBootLibInstalls s)) +{-# INLINEABLE projectConfigAllowBootLibInstalls #-} + +projectConfigOnlyConstrained :: Lens' ProjectConfigShared (Flag OnlyConstrained) +projectConfigOnlyConstrained f s = fmap (\x -> s{T.projectConfigOnlyConstrained = x}) (f (T.projectConfigOnlyConstrained s)) +{-# INLINEABLE projectConfigOnlyConstrained #-} + +projectConfigPreferOldest :: Lens' ProjectConfigShared (Flag PreferOldest) +projectConfigPreferOldest f s = fmap (\x -> s{T.projectConfigPreferOldest = x}) (f (T.projectConfigPreferOldest s)) +{-# INLINEABLE projectConfigPreferOldest #-} + +projectConfigProgPathExtra :: Lens' ProjectConfigShared (NubList FilePath) +projectConfigProgPathExtra f s = fmap (\x -> s{T.projectConfigProgPathExtra = x}) (f (T.projectConfigProgPathExtra s)) +{-# INLINEABLE projectConfigProgPathExtra #-} + +projectConfigMultiRepl :: Lens' ProjectConfigShared (Flag Bool) +projectConfigMultiRepl f s = fmap (\x -> s{T.projectConfigMultiRepl = x}) (f (T.projectConfigMultiRepl s)) +{-# INLINEABLE projectConfigMultiRepl #-} + +packageConfigProgramPaths :: Lens' PackageConfig (MapLast String FilePath) +packageConfigProgramPaths f s = fmap (\x -> s{T.packageConfigProgramPaths = x}) (f (T.packageConfigProgramPaths s)) +{-# INLINEABLE packageConfigProgramPaths #-} + +packageConfigProgramArgs :: Lens' PackageConfig (MapMappend String [String]) +packageConfigProgramArgs f s = fmap (\x -> s{T.packageConfigProgramArgs = x}) (f (T.packageConfigProgramArgs s)) +{-# INLINEABLE packageConfigProgramArgs #-} + +packageConfigProgramPathExtra :: Lens' PackageConfig (NubList FilePath) +packageConfigProgramPathExtra f s = fmap (\x -> s{T.packageConfigProgramPathExtra = x}) (f (T.packageConfigProgramPathExtra s)) +{-# INLINEABLE packageConfigProgramPathExtra #-} + +packageConfigFlagAssignment :: Lens' PackageConfig (FlagAssignment) +packageConfigFlagAssignment f s = fmap (\x -> s{T.packageConfigFlagAssignment = x}) (f (T.packageConfigFlagAssignment s)) +{-# INLINEABLE packageConfigFlagAssignment #-} + +packageConfigVanillaLib :: Lens' PackageConfig (Flag Bool) +packageConfigVanillaLib f s = fmap (\x -> s{T.packageConfigVanillaLib = x}) (f (T.packageConfigVanillaLib s)) +{-# INLINEABLE packageConfigVanillaLib #-} + +packageConfigSharedLib :: Lens' PackageConfig (Flag Bool) +packageConfigSharedLib f s = fmap (\x -> s{T.packageConfigSharedLib = x}) (f (T.packageConfigSharedLib s)) +{-# INLINEABLE packageConfigSharedLib #-} + +packageConfigStaticLib :: Lens' PackageConfig (Flag Bool) +packageConfigStaticLib f s = fmap (\x -> s{T.packageConfigStaticLib = x}) (f (T.packageConfigStaticLib s)) +{-# INLINEABLE packageConfigStaticLib #-} + +packageConfigDynExe :: Lens' PackageConfig (Flag Bool) +packageConfigDynExe f s = fmap (\x -> s{T.packageConfigDynExe = x}) (f (T.packageConfigDynExe s)) +{-# INLINEABLE packageConfigDynExe #-} + +packageConfigFullyStaticExe :: Lens' PackageConfig (Flag Bool) +packageConfigFullyStaticExe f s = fmap (\x -> s{T.packageConfigFullyStaticExe = x}) (f (T.packageConfigFullyStaticExe s)) +{-# INLINEABLE packageConfigFullyStaticExe #-} + +packageConfigProf :: Lens' PackageConfig (Flag Bool) +packageConfigProf f s = fmap (\x -> s{T.packageConfigProf = x}) (f (T.packageConfigProf s)) +{-# INLINEABLE packageConfigProf #-} + +packageConfigProfLib :: Lens' PackageConfig (Flag Bool) +packageConfigProfLib f s = fmap (\x -> s{T.packageConfigProfLib = x}) (f (T.packageConfigProfLib s)) +{-# INLINEABLE packageConfigProfLib #-} + +packageConfigProfShared :: Lens' PackageConfig (Flag Bool) +packageConfigProfShared f s = fmap (\x -> s{T.packageConfigProfShared = x}) (f (T.packageConfigProfShared s)) +{-# INLINEABLE packageConfigProfShared #-} + +packageConfigProfExe :: Lens' PackageConfig (Flag Bool) +packageConfigProfExe f s = fmap (\x -> s{T.packageConfigProfExe = x}) (f (T.packageConfigProfExe s)) +{-# INLINEABLE packageConfigProfExe #-} + +packageConfigProfDetail :: Lens' PackageConfig (Flag ProfDetailLevel) +packageConfigProfDetail f s = fmap (\x -> s{T.packageConfigProfDetail = x}) (f (T.packageConfigProfDetail s)) +{-# INLINEABLE packageConfigProfDetail #-} + +packageConfigProfLibDetail :: Lens' PackageConfig (Flag ProfDetailLevel) +packageConfigProfLibDetail f s = fmap (\x -> s{T.packageConfigProfLibDetail = x}) (f (T.packageConfigProfLibDetail s)) +{-# INLINEABLE packageConfigProfLibDetail #-} + +packageConfigConfigureArgs :: Lens' PackageConfig [String] +packageConfigConfigureArgs f s = fmap (\x -> s{T.packageConfigConfigureArgs = x}) (f (T.packageConfigConfigureArgs s)) +{-# INLINEABLE packageConfigConfigureArgs #-} + +packageConfigOptimization :: Lens' PackageConfig (Flag OptimisationLevel) +packageConfigOptimization f s = fmap (\x -> s{T.packageConfigOptimization = x}) (f (T.packageConfigOptimization s)) +{-# INLINEABLE packageConfigOptimization #-} + +packageConfigProgPrefix :: Lens' PackageConfig (Flag PathTemplate) +packageConfigProgPrefix f s = fmap (\x -> s{T.packageConfigProgPrefix = x}) (f (T.packageConfigProgPrefix s)) +{-# INLINEABLE packageConfigProgPrefix #-} + +packageConfigProgSuffix :: Lens' PackageConfig (Flag PathTemplate) +packageConfigProgSuffix f s = fmap (\x -> s{T.packageConfigProgSuffix = x}) (f (T.packageConfigProgSuffix s)) +{-# INLINEABLE packageConfigProgSuffix #-} + +packageConfigExtraLibDirs :: Lens' PackageConfig [FilePath] +packageConfigExtraLibDirs f s = fmap (\x -> s{T.packageConfigExtraLibDirs = x}) (f (T.packageConfigExtraLibDirs s)) +{-# INLINEABLE packageConfigExtraLibDirs #-} + +packageConfigExtraLibDirsStatic :: Lens' PackageConfig [FilePath] +packageConfigExtraLibDirsStatic f s = fmap (\x -> s{T.packageConfigExtraLibDirsStatic = x}) (f (T.packageConfigExtraLibDirsStatic s)) +{-# INLINEABLE packageConfigExtraLibDirsStatic #-} + +packageConfigExtraFrameworkDirs :: Lens' PackageConfig [FilePath] +packageConfigExtraFrameworkDirs f s = fmap (\x -> s{T.packageConfigExtraFrameworkDirs = x}) (f (T.packageConfigExtraFrameworkDirs s)) +{-# INLINEABLE packageConfigExtraFrameworkDirs #-} + +packageConfigExtraIncludeDirs :: Lens' PackageConfig [FilePath] +packageConfigExtraIncludeDirs f s = fmap (\x -> s{T.packageConfigExtraIncludeDirs = x}) (f (T.packageConfigExtraIncludeDirs s)) +{-# INLINEABLE packageConfigExtraIncludeDirs #-} + +packageConfigGHCiLib :: Lens' PackageConfig (Flag Bool) +packageConfigGHCiLib f s = fmap (\x -> s{T.packageConfigGHCiLib = x}) (f (T.packageConfigGHCiLib s)) +{-# INLINEABLE packageConfigGHCiLib #-} + +packageConfigSplitSections :: Lens' PackageConfig (Flag Bool) +packageConfigSplitSections f s = fmap (\x -> s{T.packageConfigSplitSections = x}) (f (T.packageConfigSplitSections s)) +{-# INLINEABLE packageConfigSplitSections #-} + +packageConfigSplitObjs :: Lens' PackageConfig (Flag Bool) +packageConfigSplitObjs f s = fmap (\x -> s{T.packageConfigSplitObjs = x}) (f (T.packageConfigSplitObjs s)) +{-# INLINEABLE packageConfigSplitObjs #-} + +packageConfigStripExes :: Lens' PackageConfig (Flag Bool) +packageConfigStripExes f s = fmap (\x -> s{T.packageConfigStripExes = x}) (f (T.packageConfigStripExes s)) +{-# INLINEABLE packageConfigStripExes #-} + +packageConfigStripLibs :: Lens' PackageConfig (Flag Bool) +packageConfigStripLibs f s = fmap (\x -> s{T.packageConfigStripLibs = x}) (f (T.packageConfigStripLibs s)) +{-# INLINEABLE packageConfigStripLibs #-} + +packageConfigTests :: Lens' PackageConfig (Flag Bool) +packageConfigTests f s = fmap (\x -> s{T.packageConfigTests = x}) (f (T.packageConfigTests s)) +{-# INLINEABLE packageConfigTests #-} + +packageConfigBenchmarks :: Lens' PackageConfig (Flag Bool) +packageConfigBenchmarks f s = fmap (\x -> s{T.packageConfigBenchmarks = x}) (f (T.packageConfigBenchmarks s)) +{-# INLINEABLE packageConfigBenchmarks #-} + +packageConfigCoverage :: Lens' PackageConfig (Flag Bool) +packageConfigCoverage f s = fmap (\x -> s{T.packageConfigCoverage = x}) (f (T.packageConfigCoverage s)) +{-# INLINEABLE packageConfigCoverage #-} + +packageConfigRelocatable :: Lens' PackageConfig (Flag Bool) +packageConfigRelocatable f s = fmap (\x -> s{T.packageConfigRelocatable = x}) (f (T.packageConfigRelocatable s)) +{-# INLINEABLE packageConfigRelocatable #-} + +packageConfigDebugInfo :: Lens' PackageConfig (Flag DebugInfoLevel) +packageConfigDebugInfo f s = fmap (\x -> s{T.packageConfigDebugInfo = x}) (f (T.packageConfigDebugInfo s)) +{-# INLINEABLE packageConfigDebugInfo #-} + +packageConfigDumpBuildInfo :: Lens' PackageConfig (Flag DumpBuildInfo) +packageConfigDumpBuildInfo f s = fmap (\x -> s{T.packageConfigDumpBuildInfo = x}) (f (T.packageConfigDumpBuildInfo s)) +{-# INLINEABLE packageConfigDumpBuildInfo #-} + +packageConfigRunTests :: Lens' PackageConfig (Flag Bool) +packageConfigRunTests f s = fmap (\x -> s{T.packageConfigRunTests = x}) (f (T.packageConfigRunTests s)) +{-# INLINEABLE packageConfigRunTests #-} + +packageConfigDocumentation :: Lens' PackageConfig (Flag Bool) +packageConfigDocumentation f s = fmap (\x -> s{T.packageConfigDocumentation = x}) (f (T.packageConfigDocumentation s)) +{-# INLINEABLE packageConfigDocumentation #-} + +packageConfigHaddockHoogle :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockHoogle f s = fmap (\x -> s{T.packageConfigHaddockHoogle = x}) (f (T.packageConfigHaddockHoogle s)) +{-# INLINEABLE packageConfigHaddockHoogle #-} + +packageConfigHaddockHtml :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockHtml f s = fmap (\x -> s{T.packageConfigHaddockHtml = x}) (f (T.packageConfigHaddockHtml s)) +{-# INLINEABLE packageConfigHaddockHtml #-} + +packageConfigHaddockHtmlLocation :: Lens' PackageConfig (Flag String) +packageConfigHaddockHtmlLocation f s = fmap (\x -> s{T.packageConfigHaddockHtmlLocation = x}) (f (T.packageConfigHaddockHtmlLocation s)) +{-# INLINEABLE packageConfigHaddockHtmlLocation #-} + +packageConfigHaddockForeignLibs :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockForeignLibs f s = fmap (\x -> s{T.packageConfigHaddockForeignLibs = x}) (f (T.packageConfigHaddockForeignLibs s)) +{-# INLINEABLE packageConfigHaddockForeignLibs #-} + +packageConfigHaddockExecutables :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockExecutables f s = fmap (\x -> s{T.packageConfigHaddockExecutables = x}) (f (T.packageConfigHaddockExecutables s)) +{-# INLINEABLE packageConfigHaddockExecutables #-} + +packageConfigHaddockTestSuites :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockTestSuites f s = fmap (\x -> s{T.packageConfigHaddockTestSuites = x}) (f (T.packageConfigHaddockTestSuites s)) +{-# INLINEABLE packageConfigHaddockTestSuites #-} + +packageConfigHaddockBenchmarks :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockBenchmarks f s = fmap (\x -> s{T.packageConfigHaddockBenchmarks = x}) (f (T.packageConfigHaddockBenchmarks s)) +{-# INLINEABLE packageConfigHaddockBenchmarks #-} + +packageConfigHaddockInternal :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockInternal f s = fmap (\x -> s{T.packageConfigHaddockInternal = x}) (f (T.packageConfigHaddockInternal s)) +{-# INLINEABLE packageConfigHaddockInternal #-} + +packageConfigHaddockCss :: Lens' PackageConfig (Flag FilePath) +packageConfigHaddockCss f s = fmap (\x -> s{T.packageConfigHaddockCss = x}) (f (T.packageConfigHaddockCss s)) +{-# INLINEABLE packageConfigHaddockCss #-} + +packageConfigHaddockLinkedSource :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockLinkedSource f s = fmap (\x -> s{T.packageConfigHaddockLinkedSource = x}) (f (T.packageConfigHaddockLinkedSource s)) +{-# INLINEABLE packageConfigHaddockLinkedSource #-} + +packageConfigHaddockQuickJump :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockQuickJump f s = fmap (\x -> s{T.packageConfigHaddockQuickJump = x}) (f (T.packageConfigHaddockQuickJump s)) +{-# INLINEABLE packageConfigHaddockQuickJump #-} + +packageConfigHaddockHscolourCss :: Lens' PackageConfig (Flag FilePath) +packageConfigHaddockHscolourCss f s = fmap (\x -> s{T.packageConfigHaddockHscolourCss = x}) (f (T.packageConfigHaddockHscolourCss s)) +{-# INLINEABLE packageConfigHaddockHscolourCss #-} + +packageConfigHaddockContents :: Lens' PackageConfig (Flag PathTemplate) +packageConfigHaddockContents f s = fmap (\x -> s{T.packageConfigHaddockContents = x}) (f (T.packageConfigHaddockContents s)) +{-# INLINEABLE packageConfigHaddockContents #-} + +packageConfigHaddockIndex :: Lens' PackageConfig (Flag PathTemplate) +packageConfigHaddockIndex f s = fmap (\x -> s{T.packageConfigHaddockIndex = x}) (f (T.packageConfigHaddockIndex s)) +{-# INLINEABLE packageConfigHaddockIndex #-} + +packageConfigHaddockBaseUrl :: Lens' PackageConfig (Flag String) +packageConfigHaddockBaseUrl f s = fmap (\x -> s{T.packageConfigHaddockBaseUrl = x}) (f (T.packageConfigHaddockBaseUrl s)) +{-# INLINEABLE packageConfigHaddockBaseUrl #-} + +packageConfigHaddockResourcesDir :: Lens' PackageConfig (Flag String) +packageConfigHaddockResourcesDir f s = fmap (\x -> s{T.packageConfigHaddockResourcesDir = x}) (f (T.packageConfigHaddockResourcesDir s)) +{-# INLINEABLE packageConfigHaddockResourcesDir #-} + +packageConfigHaddockOutputDir :: Lens' PackageConfig (Flag FilePath) +packageConfigHaddockOutputDir f s = fmap (\x -> s{T.packageConfigHaddockOutputDir = x}) (f (T.packageConfigHaddockOutputDir s)) +{-# INLINEABLE packageConfigHaddockOutputDir #-} + +packageConfigHaddockUseUnicode :: Lens' PackageConfig (Flag Bool) +packageConfigHaddockUseUnicode f s = fmap (\x -> s{T.packageConfigHaddockUseUnicode = x}) (f (T.packageConfigHaddockUseUnicode s)) +{-# INLINEABLE packageConfigHaddockUseUnicode #-} + +packageConfigHaddockForHackage :: Lens' PackageConfig (Flag HaddockTarget) +packageConfigHaddockForHackage f s = fmap (\x -> s{T.packageConfigHaddockForHackage = x}) (f (T.packageConfigHaddockForHackage s)) +{-# INLINEABLE packageConfigHaddockForHackage #-} + +packageConfigTestHumanLog :: Lens' PackageConfig (Flag PathTemplate) +packageConfigTestHumanLog f s = fmap (\x -> s{T.packageConfigTestHumanLog = x}) (f (T.packageConfigTestHumanLog s)) +{-# INLINEABLE packageConfigTestHumanLog #-} + +packageConfigTestMachineLog :: Lens' PackageConfig (Flag PathTemplate) +packageConfigTestMachineLog f s = fmap (\x -> s{T.packageConfigTestMachineLog = x}) (f (T.packageConfigTestMachineLog s)) +{-# INLINEABLE packageConfigTestMachineLog #-} + +packageConfigTestShowDetails :: Lens' PackageConfig (Flag TestShowDetails) +packageConfigTestShowDetails f s = fmap (\x -> s{T.packageConfigTestShowDetails = x}) (f (T.packageConfigTestShowDetails s)) +{-# INLINEABLE packageConfigTestShowDetails #-} + +packageConfigTestKeepTix :: Lens' PackageConfig (Flag Bool) +packageConfigTestKeepTix f s = fmap (\x -> s{T.packageConfigTestKeepTix = x}) (f (T.packageConfigTestKeepTix s)) +{-# INLINEABLE packageConfigTestKeepTix #-} + +packageConfigTestWrapper :: Lens' PackageConfig (Flag FilePath) +packageConfigTestWrapper f s = fmap (\x -> s{T.packageConfigTestWrapper = x}) (f (T.packageConfigTestWrapper s)) +{-# INLINEABLE packageConfigTestWrapper #-} + +packageConfigTestFailWhenNoTestSuites :: Lens' PackageConfig (Flag Bool) +packageConfigTestFailWhenNoTestSuites f s = fmap (\x -> s{T.packageConfigTestFailWhenNoTestSuites = x}) (f (T.packageConfigTestFailWhenNoTestSuites s)) +{-# INLINEABLE packageConfigTestFailWhenNoTestSuites #-} + +packageConfigTestTestOptions :: Lens' PackageConfig [PathTemplate] +packageConfigTestTestOptions f s = fmap (\x -> s{T.packageConfigTestTestOptions = x}) (f (T.packageConfigTestTestOptions s)) +{-# INLINEABLE packageConfigTestTestOptions #-} + +packageConfigBenchmarkOptions :: Lens' PackageConfig [PathTemplate] +packageConfigBenchmarkOptions f s = fmap (\x -> s{T.packageConfigBenchmarkOptions = x}) (f (T.packageConfigBenchmarkOptions s)) +{-# INLINEABLE packageConfigBenchmarkOptions #-} diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Parsec.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Parsec.hs new file mode 100644 index 00000000000..f4d638c0d6b --- /dev/null +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Parsec.hs @@ -0,0 +1,427 @@ +{-# LANGUAGE OverloadedStrings #-} + +-- | Parsing project configuration. +module Distribution.Client.ProjectConfig.Parsec + ( -- * Package configuration + parseProjectSkeleton + , parseProject + , ProjectConfigSkeleton + , ProjectConfig (..) + + -- ** Parsing + , ParseResult + , runParseResult + ) where + +import Distribution.CabalSpecVersion +import Distribution.Client.HttpUtils +import Distribution.Client.ProjectConfig.FieldGrammar (packageConfigFieldGrammar, projectConfigFieldGrammar) +import Distribution.Client.ProjectConfig.Legacy (ProjectConfigSkeleton) +import qualified Distribution.Client.ProjectConfig.Lens as L +import Distribution.Client.ProjectConfig.Types +import Distribution.Client.Types.Repo hiding (repoName) +import Distribution.Client.Types.RepoName (RepoName (..)) +import Distribution.Client.Types.SourceRepo (sourceRepositoryPackageGrammar) +import Distribution.Client.Utils.Parsec +import Distribution.Compat.Lens +import Distribution.Compat.Prelude +import Distribution.FieldGrammar +import Distribution.FieldGrammar.Parsec (NamelessField (..), namelessFieldAnn) +import Distribution.Fields (Field (..), FieldLine (..), FieldName, Name (..), SectionArg (..), readFields') +import Distribution.Fields.ConfVar (parseConditionConfVar) +import Distribution.Fields.Field (fieldLinesToString, sectionArgAnn) +import Distribution.Fields.LexerMonad (toPWarnings) +import Distribution.Fields.ParseResult +import Distribution.Parsec (ParsecParser, eitherParsec, parsec, parsecFilePath, runParsecParser) +import Distribution.Parsec.FieldLineStream (fieldLineStreamFromBS) +import Distribution.Parsec.Position (Position (..), incPos, zeroPos) +import Distribution.Parsec.Warning (PWarnType (..)) +import Distribution.Simple.Program.Db (ProgramDb, defaultProgramDb, knownPrograms, lookupKnownProgram) +import Distribution.Simple.Program.Types (programName) +import Distribution.Simple.Setup +import Distribution.Simple.Utils (debug, noticeDoc) +import Distribution.Solver.Types.ProjectConfigPath +import Distribution.System (buildOS) +import Distribution.Types.CondTree (CondBranch (..), CondTree (..)) +import Distribution.Types.ConfVar (ConfVar (..)) +import Distribution.Types.PackageName (PackageName) +import Distribution.Utils.Generic (fromUTF8BS, toUTF8BS, validateUTF8) +import Distribution.Utils.NubList (toNubList) +import Distribution.Utils.String (trim) +import Distribution.Verbosity + +import Control.Monad.State.Strict (StateT, execStateT, lift) +import qualified Data.ByteString as BS +import Data.Coerce (coerce) +import qualified Data.Map.Strict as Map +import qualified Data.Set as Set +import Distribution.Client.Errors.Parser (ProjectFileSource (..)) +import qualified Distribution.Compat.CharParsing as P +import Network.URI (parseURI, uriFragment, uriPath, uriScheme) +import System.Directory (createDirectoryIfMissing, makeAbsolute) +import System.FilePath (isAbsolute, isPathSeparator, makeValid, splitFileName, ()) +import qualified Text.Parsec +import Text.PrettyPrint (render) +import qualified Text.PrettyPrint as Disp + +singletonProjectConfigSkeleton :: ProjectConfig -> ProjectConfigSkeleton +singletonProjectConfigSkeleton x = CondNode x mempty mempty + +readPreprocessFields :: BS.ByteString -> ParseResult src [Field Position] +readPreprocessFields bs = do + case readFields' bs' of + Right (fs, lexWarnings) -> do + parseWarnings (toPWarnings lexWarnings) + for_ invalidUtf8 $ \pos -> + parseWarning zeroPos PWTUTF $ "UTF8 encoding problem at byte offset " ++ show pos + return fs + Left perr -> parseFatalFailure pos (show perr) + where + ppos = Text.Parsec.errorPos perr + pos = Position (Text.Parsec.sourceLine ppos) (Text.Parsec.sourceColumn ppos) + where + invalidUtf8 = validateUTF8 bs + bs' = case invalidUtf8 of + Nothing -> bs + Just _ -> toUTF8BS (fromUTF8BS bs) + +-- | Parses a project from its root config file, typically cabal.project. +parseProject + :: FilePath + -- ^ The root of the project configuration, typically cabal.project + -> FilePath + -> HttpTransport + -> Verbosity + -> ProjectConfigToParse + -- ^ The contents of the file to parse + -> IO (ParseResult ProjectFileSource ProjectConfigSkeleton) +parseProject rootPath cacheDir httpTransport verbosity configToParse = do + let (dir, projectFileName) = splitFileName rootPath + projectDir <- makeAbsolute dir + projectPath <- canonicalizeConfigPath projectDir (ProjectConfigPath $ projectFileName :| []) + parseProjectSkeleton cacheDir httpTransport verbosity projectDir projectPath configToParse + +parseProjectSkeleton + :: FilePath + -> HttpTransport + -> Verbosity + -> FilePath + -- ^ The directory of the project configuration, typically the directory of cabal.project + -> ProjectConfigPath + -- ^ The path of the file being parsed, either the root or an import + -> ProjectConfigToParse + -- ^ The contents of the file to parse + -> IO (ParseResult ProjectFileSource ProjectConfigSkeleton) +parseProjectSkeleton cacheDir httpTransport verbosity projectDir source (ProjectConfigToParse bs) = do + normSource <- canonicalizeConfigPath projectDir source + res <- (sanityWalkPCS False =<<) <$> liftParseResult (go []) (readPreprocessFields bs) + pure $ withSource (ProjectFileSource (normSource, bs)) res + where + go :: [Field Position] -> [Field Position] -> IO (ParseResult ProjectFileSource ProjectConfigSkeleton) + go acc (x : xs) = case x of + (Field (Name pos name) importLines) | name == "import" -> do + liftParseResult + ( \importLoc -> do + let importLocPath = importLoc `consProjectConfigPath` source + + -- Once we canonicalize the import path, we can check for cyclical imports + normSource <- canonicalizeConfigPath projectDir source + normLocPath <- canonicalizeConfigPath projectDir importLocPath + debug verbosity $ "\nimport path, normalized\n=======================\n" ++ render (docProjectConfigPath normLocPath) + + if isCyclicConfigPath normLocPath + then pure $ parseFatalFailure pos (render $ cyclicalImportMsg normLocPath) + else do + when + (isUntrimmedUriConfigPath importLocPath) + (noticeDoc verbosity $ untrimmedUriImportMsg (Disp.text "Warning:") importLocPath) + let fs = (\z -> CondNode z [normLocPath] mempty) <$> fieldsToConfig normSource (reverse acc) + importParseResult <- parseProjectSkeleton cacheDir httpTransport verbosity projectDir importLocPath . ProjectConfigToParse =<< fetchImportConfig normLocPath + + rest <- go [] xs + pure . fmap mconcat . sequence $ [fs, importParseResult, rest] + ) + (parseImport pos importLines) + (Section (Name pos "if") args xs') -> do + subpcs <- go [] xs' + let fs = fmap singletonProjectConfigSkeleton $ fieldsToConfig source (reverse acc) + (elseClauses, rest) <- parseElseClauses xs + let condNode = + (\c pcs e -> CondNode mempty mempty [CondBranch c pcs e]) + <$> parseConditionConfVar (startOfSection (incPos 2 pos) args) args + <*> subpcs + <*> elseClauses + pure . fmap mconcat . sequence $ [fs, condNode, rest] + _ -> go (x : acc) xs + go acc [] = do + normSource <- canonicalizeConfigPath projectDir source + pure . fmap singletonProjectConfigSkeleton . fieldsToConfig normSource $ reverse acc + + parseElseClauses :: [Field Position] -> IO (ParseResult ProjectFileSource (Maybe ProjectConfigSkeleton), ParseResult ProjectFileSource ProjectConfigSkeleton) + parseElseClauses x = case x of + (Section (Name _pos "else") _args xs' : xs) -> do + subpcs <- go [] xs' + rest <- go [] xs + pure (Just <$> subpcs, rest) + (Section (Name pos "elif") args xs' : xs) -> do + subpcs <- go [] xs' + (elseClauses, rest) <- parseElseClauses xs + let condNode = + (\c pcs e -> CondNode mempty mempty [CondBranch c pcs e]) + <$> parseConditionConfVar (startOfSection (incPos 4 pos) args) args + <*> subpcs + <*> elseClauses + pure (Just <$> condNode, rest) + _ -> (\r -> (pure Nothing, r)) <$> go [] x + + parseImport :: Position -> [FieldLine Position] -> ParseResult ProjectFileSource FilePath + parseImport pos lines' = runFieldParser pos (P.many P.anyChar) cabalSpec lines' + + -- We want a normalized path for @fieldsToConfig@. This eventually surfaces + -- in solver rejection messages and build messages "this build was affected + -- by the following (project) config files" so we want all paths shown there + -- to be relative to the directory of the project, not relative to the file + -- they were imported from. + fieldsToConfig :: ProjectConfigPath -> [Field Position] -> ParseResult ProjectFileSource ProjectConfig + fieldsToConfig sourceConfigPath xs = do + let (fs, sectionGroups) = partitionFields xs + sections = concat sectionGroups + config <- parseFieldGrammarCheckingStanzas cabalSpec fs (projectConfigFieldGrammar sourceConfigPath (knownProgramNames programDb)) stanzas + config' <- view stateConfig <$> execStateT (goSections programDb sections) (SectionS config) + return config' + + fetchImportConfig :: ProjectConfigPath -> IO BS.ByteString + fetchImportConfig (ProjectConfigPath (pci :| _)) = do + debug verbosity $ "fetching import: " ++ pci + fetch pci + + fetch :: FilePath -> IO BS.ByteString + fetch pci = case parseURI (trim pci) of + Just uri -> do + let fp = cacheDir map (\x -> if isPathSeparator x then '_' else x) (makeValid $ show uri) + createDirectoryIfMissing True cacheDir + _ <- downloadURI httpTransport verbosity uri fp + BS.readFile fp + Nothing -> + BS.readFile $ + if isAbsolute pci then pci else coerce projectDir pci + + modifiesCompiler :: ProjectConfig -> Bool + modifiesCompiler pc = isSet projectConfigHcFlavor || isSet projectConfigHcPath || isSet projectConfigHcPkg + where + isSet f = f (projectConfigShared pc) /= NoFlag + + sanityWalkPCS :: Bool -> ProjectConfigSkeleton -> ParseResult ProjectFileSource ProjectConfigSkeleton + sanityWalkPCS underConditional t@(CondNode d _c comps) + | underConditional && modifiesCompiler d = parseFatalFailure zeroPos "Cannot set compiler in a conditional clause of a cabal project file" + | otherwise = mapM_ sanityWalkBranch comps >> pure t + + sanityWalkBranch :: CondBranch ConfVar [ProjectConfigPath] ProjectConfig -> ParseResult ProjectFileSource () + sanityWalkBranch (CondBranch _c t f) = traverse_ (sanityWalkPCS True) f >> sanityWalkPCS True t >> pure () + + programDb = defaultProgramDb + +startOfSection :: Position -> [SectionArg Position] -> Position +-- The case where we have no args is the start of the section +startOfSection defaultPos [] = defaultPos +-- Otherwise the start of the section is the position of the first argument. +startOfSection _ (cond : _) = sectionArgAnn cond + +knownProgramNames :: ProgramDb -> [String] +knownProgramNames programDb = (programName . fst) <$> knownPrograms programDb + +-- | Monad in which sections are parsed +type SectionParser src = StateT SectionS (ParseResult src) + +-- | State of 'SectionParser' +newtype SectionS = SectionS + { _stateConfig :: ProjectConfig + } + +stateConfig :: Lens' SectionS ProjectConfig +stateConfig f (SectionS cfg) = SectionS <$> f cfg +{-# INLINEABLE stateConfig #-} + +goSections :: ProgramDb -> [Section Position] -> SectionParser src () +goSections programDb = traverse_ (parseSection programDb) + +parseSection :: ProgramDb -> Section Position -> SectionParser src () +parseSection programDb (MkSection (Name pos name) args secFields) + | name == "source-repository-package" = do + verifyNullSubsections + verifyNullSectionArgs + srp <- lift $ parseFieldGrammar cabalSpec fields sourceRepositoryPackageGrammar + stateConfig . L.projectPackagesRepo %= (<> [srp]) + | name == "program-options" = do + verifyNullSubsections + verifyNullSectionArgs + opts' <- lift $ parseProgramArgs programDb fields + stateConfig . L.projectConfigLocalPackages . L.packageConfigProgramArgs %= (opts' <>) + | name == "program-locations" = do + verifyNullSubsections + verifyNullSectionArgs + paths' <- lift $ parseProgramPaths programDb fields + stateConfig . L.projectConfigLocalPackages . L.packageConfigProgramPaths %= (paths' <>) + | name == "repository" = do + verifyNullSubsections + mRepoName <- lift $ parseRepoName pos args + case mRepoName of + Just repoName -> do + remoteRepo <- lift $ parseFieldGrammar cabalSpec fields (remoteRepoGrammar repoName) + remoteOrLocalRepo <- lift $ postProcessRemoteRepo pos remoteRepo + case remoteOrLocalRepo of + Left local -> stateConfig . L.projectConfigShared . L.projectConfigLocalNoIndexRepos %= (<> toNubList [local]) + Right remote -> stateConfig . L.projectConfigShared . L.projectConfigRemoteRepos %= (<> toNubList [remote]) + Nothing -> lift $ parseFailure pos "a 'repository' section requires the repository name as an argument" + | name == "package" = do + verifyNullSubsections + package <- lift $ parsePackageName pos args + case package of + Just AllPackages -> do + packageCfg' <- parsePackageConfig + stateConfig . L.projectConfigAllPackages %= (packageCfg' <>) + Just (SpecificPackage packageName) -> do + packageCfg <- parsePackageConfig + stateConfig . L.projectConfigSpecificPackage %= (<> MapMappend (Map.singleton packageName packageCfg)) + Nothing -> do + lift $ parseWarning pos PWTUnknownSection "target package name or * required" + return () + | otherwise = do + warnInvalidSubsection pos name + where + (fields, sections) = partitionFields secFields + warnInvalidSubsection pos' name' = lift $ parseWarning pos' PWTInvalidSubsection $ "Invalid subsection " ++ show name' + programNames = knownProgramNames programDb + verifyNullSubsections = unless (null sections) (warnInvalidSubsection pos name) + verifyNullSectionArgs = unless (null args) (lift $ parseFailure pos $ "The section '" <> (show name) <> "' takes no arguments") + parsePackageConfig = do + packageCfg <- lift $ parseFieldGrammar cabalSpec fields (packageConfigFieldGrammar programNames) + args' <- lift $ parseProgramArgs programDb fields + paths <- lift $ parseProgramPaths programDb fields + return packageCfg{packageConfigProgramPaths = paths, packageConfigProgramArgs = args'} + +stanzas :: Set BS.ByteString +stanzas = Set.fromList ["source-repository-package", "program-options", "program-locations", "repository", "package"] + +-- | Currently a duplicate of 'Distribution.Client.Config.postProcessRepo' but migrated to Parsec ParseResult. +postProcessRemoteRepo :: Position -> RemoteRepo -> ParseResult src (Either LocalRepo RemoteRepo) +postProcessRemoteRepo pos repo = case uriScheme (remoteRepoURI repo) of + -- TODO: check that there are no authority, query or fragment + -- Note: the trailing colon is important + "file+noindex:" -> do + let uri = normaliseFileNoIndexURI buildOS $ remoteRepoURI repo + return $ Left $ LocalRepo (remoteRepoName repo) (uriPath uri) (uriFragment uri == "#shared-cache") + _ -> do + when (remoteRepoKeyThreshold repo > length (remoteRepoRootKeys repo)) $ + warning $ + "'key-threshold' for repository " + ++ show (remoteRepoName repo) + ++ " higher than number of keys" + + when (not (null (remoteRepoRootKeys repo)) && remoteRepoSecure repo /= Just True) $ + warning $ + "'root-keys' for repository " + ++ show (remoteRepoName repo) + ++ " non-empty, but 'secure' not set to True." + + return $ Right repo + where + warning msg = parseWarning pos PWTOther msg + +parseRepoName :: Position -> [SectionArg Position] -> ParseResult src (Maybe RepoName) +parseRepoName pos args = case args of + [SecArgName _ secName] -> parseName secName + [SecArgStr _ secName] -> parseName secName + [SecArgOther _ secName] -> parseName secName + _ -> return Nothing + where + parseName :: BS.ByteString -> ParseResult src (Maybe RepoName) + parseName str = + let repoNameStr = fromUTF8BS str + in case eitherParsec repoNameStr of + Left _ -> do + parseFailure pos ("Invalid repository name" ++ repoNameStr) + return Nothing + Right name -> return $ Just name + +data PackageConfigTarget = AllPackages | SpecificPackage !PackageName + +parsePackageName :: Position -> [SectionArg Position] -> ParseResult src (Maybe PackageConfigTarget) +parsePackageName pos args = case args of + [SecArgName _ secName] -> parseName secName + [SecArgStr _ secName] -> parseName secName + [SecArgOther _ secName] -> parseName secName + _ -> return Nothing + where + parseName secName = case runParsecParser parser "" (fieldLineStreamFromBS secName) of + Left _ -> do + parseFailure pos ("Invalid package name" ++ fromUTF8BS secName) + return Nothing + Right cfgTarget -> return $ pure cfgTarget + parser :: ParsecParser PackageConfigTarget + parser = + P.choice [P.try (P.char '*' >> return AllPackages), SpecificPackage <$> parsec] + +-- | Parse fields of a program-options stanza. +parseProgramArgs :: ProgramDb -> Fields Position -> ParseResult src (MapMappend String [String]) +parseProgramArgs programDb fields = foldM parseField mempty (filter hasOptionsSuffix $ Map.toList fields) + where + parseField programArgs (fieldName, fieldLines) = do + case readProgramName "-options" programDb fieldName of + Nothing -> warnUnknownFields fieldName fieldLines >> return programArgs + Just program -> do + args <- parseProgramArgsField $ reverse fieldLines + return $ programArgs <> MapMappend (Map.singleton program args) + hasOptionsSuffix (fieldName, _) = BS.isSuffixOf "-options" fieldName + +-- | Parse fields of a program-locations stanza. +parseProgramPaths :: ProgramDb -> Fields Position -> ParseResult src (MapLast String FilePath) +parseProgramPaths programDb fields = foldM parseField mempty (filter hasLocationSuffix $ Map.toList fields) + where + parseField paths (fieldName, fieldLines) = do + case readProgramName "-location" programDb fieldName of + Nothing -> warnUnknownFields fieldName fieldLines >> return paths + Just program -> do + case fieldLines of + (MkNamelessField pos lines') : _ -> do + fp <- runFieldParser pos parsecFilePath cabalSpec lines' + return $ paths <> MapLast (Map.singleton program fp) + [] -> return mempty + hasLocationSuffix (fieldName, _) = BS.isSuffixOf "-location" fieldName + +-- | Parse all arguments to a single program in program-options stanza. +-- By processing '[NamelessField Position]', we support multiple occurrences of the field, concatenating the arguments. +parseProgramArgsField :: [NamelessField Position] -> ParseResult src ([String]) +parseProgramArgsField fieldLines = + concat <$> mapM (\(MkNamelessField _ lines') -> parseProgramArgsFieldLines lines') fieldLines + +-- | Parse all fieldLines of a single field occurrence in a program-options stanza. +parseProgramArgsFieldLines :: [FieldLine Position] -> ParseResult src [String] +parseProgramArgsFieldLines lines' = return $ splitArgs strLines + where + strLines = fieldLinesToString lines' + +type FieldSuffix = String + +-- | Extract the program name of a field, allow it to have a suffix such as '-options' and check whether the 'ProgramDB' contains it. +readProgramName :: FieldSuffix -> ProgramDb -> FieldName -> Maybe String +readProgramName suffix programDb fieldName = + parseProgramName suffix fieldName >>= ((flip lookupKnownProgram) programDb) >>= pure . programName + +parseProgramName :: FieldSuffix -> FieldName -> Maybe String +parseProgramName suffix fieldName = case runParsecParser parser "" fieldNameStream of + Left _ -> Nothing + Right str -> Just str + where + parser = P.manyTill P.anyChar (P.try ((P.string suffix)) <* P.eof) + fieldNameStream = fieldLineStreamFromBS fieldName + +-- | Issue a 'PWTUnknownField' warning at all occurrences of a field. +warnUnknownFields :: FieldName -> [NamelessField Position] -> ParseResult src () +warnUnknownFields fieldName fieldLines = for_ fieldLines (\field -> parseWarning (pos field) PWTUnknownField message) + where + message = "Unknown field: " ++ show fieldName + pos = namelessFieldAnn + +cabalSpec :: CabalSpecVersion +cabalSpec = cabalSpecLatest diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs index 1a2b6ae2fa6..220834a331c 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} @@ -10,6 +11,8 @@ module Distribution.Client.ProjectConfig.Types , ProjectConfigShared (..) , ProjectConfigProvenance (..) , PackageConfig (..) + , ProjectFileParser (..) + , defaultProjectFileParser -- * Resolving configuration , SolverSettings (..) @@ -168,6 +171,7 @@ data ProjectConfigBuildOnly = ProjectConfigBuildOnly , projectConfigReportPlanningFailure :: Flag Bool , projectConfigSymlinkBinDir :: Flag FilePath , projectConfigNumJobs :: Flag (Maybe Int) + -- ^ Use 'Just n' for number of jobs, 'Nothing' for number of jobs equal to the number of CPUs and 'NoFlag' if flag is not given. , projectConfigUseSemaphore :: Flag Bool , projectConfigKeepGoing :: Flag Bool , projectConfigOfflineMode :: Flag Bool @@ -187,6 +191,7 @@ data ProjectConfigShared = ProjectConfigShared , projectConfigConfigFile :: Flag FilePath , projectConfigProjectDir :: Flag FilePath , projectConfigProjectFile :: Flag FilePath + , projectConfigProjectFileParser :: Flag ProjectFileParser , projectConfigIgnoreProject :: Flag Bool , projectConfigHcFlavor :: Flag CompilerFlavor , projectConfigHcPath :: Flag FilePath @@ -237,6 +242,20 @@ data ProjectConfigShared = ProjectConfigShared } deriving (Eq, Show, Generic) +data ProjectFileParser + = LegacyParser + | ParsecParser + | FallbackParser + | CompareParser + deriving (Eq, Show, Generic) + +defaultProjectFileParser :: ProjectFileParser +#ifdef LEGACY_COMPARISON +defaultProjectFileParser = CompareParser +#else +defaultProjectFileParser = FallbackParser +#endif + -- | Specifies the provenance of project configuration, whether defaults were -- used or if the configuration was read from an explicit file path. data ProjectConfigProvenance @@ -327,12 +346,14 @@ instance Binary ProjectConfigBuildOnly instance Binary ProjectConfigShared instance Binary ProjectConfigProvenance instance Binary PackageConfig +instance Binary ProjectFileParser instance Structured ProjectConfig instance Structured ProjectConfigBuildOnly instance Structured ProjectConfigShared instance Structured ProjectConfigProvenance instance Structured PackageConfig +instance Structured ProjectFileParser -- | Newtype wrapper for 'Map' that provides a 'Monoid' instance that takes -- the last value rather than the first value for overlapping keys. diff --git a/cabal-install/src/Distribution/Client/ProjectFlags.hs b/cabal-install/src/Distribution/Client/ProjectFlags.hs index 29b964bb99b..9214b6a37cf 100644 --- a/cabal-install/src/Distribution/Client/ProjectFlags.hs +++ b/cabal-install/src/Distribution/Client/ProjectFlags.hs @@ -10,9 +10,10 @@ module Distribution.Client.ProjectFlags ) where import Distribution.Client.Compat.Prelude +import Distribution.Client.ProjectConfig.Types (ProjectFileParser (..), defaultProjectFileParser) import Prelude () -import Distribution.ReadE (succeedReadE) +import Distribution.ReadE (ReadE (..), succeedReadE) import Distribution.Simple.Command ( MkOptDescr , OptionField (optionName) @@ -47,6 +48,8 @@ data ProjectFlags = ProjectFlags , flagIgnoreProject :: Flag Bool -- ^ Whether to ignore the local project (i.e. don't search for cabal.project) -- The exact interpretation might be slightly different per command. + , flagProjectFileParser :: Flag ProjectFileParser + -- ^ The parser to use for the project file. } deriving (Show, Generic) @@ -56,7 +59,8 @@ defaultProjectFlags = { flagProjectDir = mempty , flagProjectFile = mempty , flagIgnoreProject = toFlag False - -- Should we use 'Last' here? + , -- Should we use 'Last' here? + flagProjectFileParser = mempty } projectFlagsOptions :: ShowOrParseArgs -> [OptionField ProjectFlags] @@ -88,8 +92,32 @@ projectFlagsOptions showOrParseArgs = } ) (yesNoOpt showOrParseArgs) + , option + [] + ["project-file-parser"] + "Set the parser to use for the project file" + flagProjectFileParser + (\pf flags -> flags{flagProjectFileParser = pf}) + (reqArg "PARSER" (fmap Flag $ ReadE parseProjectFileParser) projectFileParserPrinter) ] +parseProjectFileParser :: String -> Either String ProjectFileParser +parseProjectFileParser "legacy" = pure LegacyParser +parseProjectFileParser "fallback" = pure FallbackParser +parseProjectFileParser "default" = pure defaultProjectFileParser +parseProjectFileParser "parsec" = pure ParsecParser +parseProjectFileParser "compare" = pure CompareParser +parseProjectFileParser _ = Left "Invalid project file parser" + +projectFileParserPrinter :: Flag ProjectFileParser -> [String] +projectFileParserPrinter (Flag parser) = + case parser of + LegacyParser -> ["legacy"] + FallbackParser -> ["fallback"] + ParsecParser -> ["parsec"] + CompareParser -> ["compare"] +projectFileParserPrinter NoFlag = [] + -- | As almost all commands use 'ProjectFlags' but not all can honour -- "ignore-project" flag, provide this utility to remove the flag -- parsing from the help message. diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index fbd4f758d67..d30a553a4bb 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -122,6 +122,7 @@ import Distribution.Client.JobControl import Distribution.Client.PackageHash import Distribution.Client.ProjectConfig import Distribution.Client.ProjectConfig.Legacy +import Distribution.Client.ProjectConfig.Types (defaultProjectFileParser) import Distribution.Client.ProjectPlanOutput import Distribution.Client.ProjectPlanning.SetupPolicy ( NonSetupLibDepSolverPlanPackage (..) @@ -380,6 +381,7 @@ rebuildProjectConfig ( configPath , distProjectFile "" , (projectConfigHcFlavor, projectConfigHcPath, projectConfigHcPkg) + , projectConfigProjectFileParser , progsearchpath , packageConfigProgramPaths , packageConfigProgramPathExtra @@ -412,7 +414,7 @@ rebuildProjectConfig return (projectConfig <> cliConfig, localPackages) where - ProjectConfigShared{projectConfigHcFlavor, projectConfigHcPath, projectConfigHcPkg, projectConfigIgnoreProject, projectConfigConfigFile} = + ProjectConfigShared{projectConfigHcFlavor, projectConfigHcPath, projectConfigHcPkg, projectConfigProjectFileParser, projectConfigIgnoreProject, projectConfigConfigFile} = projectConfigShared cliConfig PackageConfig{packageConfigProgramPaths, packageConfigProgramPathExtra} = @@ -420,10 +422,12 @@ rebuildProjectConfig -- Read the cabal.project (or implicit config) and combine it with -- arguments from the command line + + configFileParser = fromFlagOrDefault defaultProjectFileParser projectConfigProjectFileParser -- phaseReadProjectConfig :: Rebuild ProjectConfigSkeleton phaseReadProjectConfig = do - readProjectConfig verbosity httpTransport projectConfigIgnoreProject projectConfigConfigFile distDirLayout + readProjectConfig verbosity configFileParser httpTransport projectConfigIgnoreProject projectConfigConfigFile distDirLayout -- Look for all the cabal packages in the project -- some of which may be local src dirs, tarballs etc diff --git a/cabal-install/src/Distribution/Client/ScriptUtils.hs b/cabal-install/src/Distribution/Client/ScriptUtils.hs index 87c36280503..1c78d537c19 100644 --- a/cabal-install/src/Distribution/Client/ScriptUtils.hs +++ b/cabal-install/src/Distribution/Client/ScriptUtils.hs @@ -477,7 +477,7 @@ updateContextAndWriteProjectFile ctx scriptPath scriptExecutable = do updateContextAndWriteProjectFile' ctx sourcePackage -parseScriptBlock :: BS.ByteString -> ParseResult Executable +parseScriptBlock :: BS.ByteString -> ParseResult src Executable parseScriptBlock str = case readFields str of Right fs -> do diff --git a/cabal-install/src/Distribution/Client/Types/AllowNewer.hs b/cabal-install/src/Distribution/Client/Types/AllowNewer.hs index 0a5700174b8..53c55ef08b6 100644 --- a/cabal-install/src/Distribution/Client/Types/AllowNewer.hs +++ b/cabal-install/src/Distribution/Client/Types/AllowNewer.hs @@ -101,6 +101,12 @@ instance Pretty RelaxedDep where instance Parsec RelaxedDep where parsec = P.char '*' *> relaxedDepStarP <|> (parsec >>= relaxedDepPkgidP) +instance Parsec AllowOlder where + parsec = AllowOlder <$> parsec + +instance Parsec AllowNewer where + parsec = AllowNewer <$> parsec + -- continuation after * relaxedDepStarP :: CabalParsing m => m RelaxedDep relaxedDepStarP = diff --git a/cabal-install/src/Distribution/Client/Types/Repo.hs b/cabal-install/src/Distribution/Client/Types/Repo.hs index 83ecc454863..d3caac872db 100644 --- a/cabal-install/src/Distribution/Client/Types/Repo.hs +++ b/cabal-install/src/Distribution/Client/Types/Repo.hs @@ -4,6 +4,10 @@ module Distribution.Client.Types.Repo ( -- * Remote repository RemoteRepo (..) , emptyRemoteRepo + , remoteRepoKeyThresholdLens + , remoteRepoRootKeysLens + , remoteRepoSecureLens + , remoteRepoURILens -- * Local repository (no-index) , LocalRepo (..) @@ -17,6 +21,7 @@ module Distribution.Client.Types.Repo , maybeRepoRemote -- * Windows + , asPosixPath , normaliseFileNoIndexURI ) where @@ -29,6 +34,7 @@ import Distribution.Simple.Utils (toUTF8BS) import Distribution.System (OS (Windows)) import Distribution.Client.HashValue (hashValue, showHashValue, truncateHash) +import Distribution.Compat.Lens import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Distribution.Compat.CharParsing as P @@ -97,6 +103,22 @@ instance Parsec RemoteRepo where emptyRemoteRepo :: RepoName -> RemoteRepo emptyRemoteRepo name = RemoteRepo name nullURI Nothing [] 0 False +remoteRepoURILens :: Lens' RemoteRepo URI +remoteRepoURILens f s = fmap (\x -> s{remoteRepoURI = x}) (f (remoteRepoURI s)) +{-# INLINE remoteRepoURILens #-} + +remoteRepoSecureLens :: Lens' RemoteRepo (Maybe Bool) +remoteRepoSecureLens f s = fmap (\x -> s{remoteRepoSecure = x}) (f (remoteRepoSecure s)) +{-# INLINE remoteRepoSecureLens #-} + +remoteRepoRootKeysLens :: Lens' RemoteRepo [String] +remoteRepoRootKeysLens f s = fmap (\x -> s{remoteRepoRootKeys = x}) (f (remoteRepoRootKeys s)) +{-# INLINE remoteRepoRootKeysLens #-} + +remoteRepoKeyThresholdLens :: Lens' RemoteRepo Int +remoteRepoKeyThresholdLens f s = fmap (\x -> s{remoteRepoKeyThreshold = x}) (f (remoteRepoKeyThreshold s)) +{-# INLINE remoteRepoKeyThresholdLens #-} + ------------------------------------------------------------------------------- -- Local repository ------------------------------------------------------------------------------- @@ -230,8 +252,10 @@ normaliseFileNoIndexURI os uri@(URI scheme _auth path query fragment) , Windows <- os = URI scheme Nothing (asPosixPath path) query fragment | otherwise = uri - where - asPosixPath p = - -- We don't use 'isPathSeparator' because @Windows.isPathSeparator - -- Posix.pathSeparator == True@. - [if x == Windows.pathSeparator then Posix.pathSeparator else x | x <- p] + +-- | Convert a path to POSIX-style. +asPosixPath :: FilePath -> FilePath +asPosixPath p = + -- We don't use 'isPathSeparator' because @Windows.isPathSeparator + -- Posix.pathSeparator == True@. + [if x == Windows.pathSeparator then Posix.pathSeparator else x | x <- p] diff --git a/cabal-install/src/Distribution/Client/Types/WriteGhcEnvironmentFilesPolicy.hs b/cabal-install/src/Distribution/Client/Types/WriteGhcEnvironmentFilesPolicy.hs index 042b62d997a..6db210025ee 100644 --- a/cabal-install/src/Distribution/Client/Types/WriteGhcEnvironmentFilesPolicy.hs +++ b/cabal-install/src/Distribution/Client/Types/WriteGhcEnvironmentFilesPolicy.hs @@ -5,6 +5,8 @@ module Distribution.Client.Types.WriteGhcEnvironmentFilesPolicy ) where import Distribution.Client.Compat.Prelude +import qualified Distribution.Compat.CharParsing as P +import Distribution.Parsec import Prelude () -- | Whether 'v2-build' should write a .ghc.environment file after @@ -19,3 +21,16 @@ data WriteGhcEnvironmentFilesPolicy instance Binary WriteGhcEnvironmentFilesPolicy instance Structured WriteGhcEnvironmentFilesPolicy + +instance Parsec WriteGhcEnvironmentFilesPolicy where + parsec = do + token <- parsecToken + case token of + "always" -> return AlwaysWriteGhcEnvironmentFiles + "never" -> return NeverWriteGhcEnvironmentFiles + "ghc8.4.4+" -> return WriteGhcEnvironmentFilesOnlyForGhc844AndNewer + policy -> + P.unexpected $ + "Cannot parse the GHC environment file write policy '" + <> policy + <> "'" diff --git a/cabal-install/src/Distribution/Client/Utils/Newtypes.hs b/cabal-install/src/Distribution/Client/Utils/Newtypes.hs new file mode 100644 index 00000000000..aa60eea697c --- /dev/null +++ b/cabal-install/src/Distribution/Client/Utils/Newtypes.hs @@ -0,0 +1,120 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +-- | This module provides @newtype@ wrappers to be used with "Distribution.FieldGrammar". +-- Whenever we can not provide a Parsec instance for a type, we need to wrap it in a newtype and define the instance. +module Distribution.Client.Utils.Newtypes + ( NumJobs (..) + , PackageDBNT (..) + , AllowNewerNT (..) + , AllowOlderNT (..) + , ProjectConstraints (..) + , MaxBackjumps (..) + , URI_NT (..) + , KeyThreshold (..) + ) +where + +import Distribution.Client.Compat.Prelude +import Distribution.Client.Targets (UserConstraint) +import Distribution.Client.Types.AllowNewer (AllowNewer (..), AllowOlder (..)) +import Distribution.Compat.CharParsing +import Distribution.Compat.Newtype +import Distribution.Parsec +import Distribution.Simple.Compiler (PackageDBCWD, interpretPackageDB, readPackageDb) +import Distribution.Solver.Types.ConstraintSource (ConstraintSource (..)) +import Network.URI (URI, parseURI) + +newtype PackageDBNT = PackageDBNT {getPackageDBNT :: Maybe PackageDBCWD} + +instance Newtype (Maybe PackageDBCWD) PackageDBNT + +instance Parsec PackageDBNT where + parsec = parsecPackageDB + +parsecPackageDB :: CabalParsing m => m PackageDBNT +parsecPackageDB = PackageDBNT . fmap (interpretPackageDB Nothing) . readPackageDb <$> parsecToken + +newtype NumJobs = NumJobs {getNumJobs :: Maybe Int} + +instance Newtype (Maybe Int) NumJobs + +instance Parsec NumJobs where + parsec = parsecNumJobs + +parsecNumJobs :: CabalParsing m => m NumJobs +parsecNumJobs = ncpus <|> numJobs + where + ncpus = string "$ncpus" >> return (NumJobs Nothing) + numJobs = do + num <- integral + if num < (1 :: Int) + then do + parsecWarning PWTOther "The number of jobs should be 1 or more." + return (NumJobs Nothing) + else return (NumJobs $ Just num) + +newtype URI_NT = URI_NT {getURI_NT :: URI} + +instance Newtype (URI) URI_NT + +instance Parsec URI_NT where + parsec = parsecURI_NT + +parsecURI_NT :: CabalParsing m => m URI_NT +parsecURI_NT = do + token <- parsecToken' + case parseURI token of + Nothing -> fail $ "failed to parse URI " <> token + Just uri -> return $ URI_NT uri + +newtype KeyThreshold = KeyThreshold {getKeyThreshold :: Int} + +instance Newtype Int KeyThreshold + +instance Parsec KeyThreshold where + parsec = KeyThreshold <$> integral + +newtype ProjectConstraints = ProjectConstraints {getProjectConstraints :: (UserConstraint, ConstraintSource)} + +instance Newtype (UserConstraint, ConstraintSource) ProjectConstraints + +instance Parsec ProjectConstraints where + parsec = parsecProjectConstraints + +-- | Parse 'ProjectConstraints'. As the 'CabalParsing' class does not have access to the file we parse, +-- ConstraintSource is first unknown and we set it afterwards +parsecProjectConstraints :: CabalParsing m => m ProjectConstraints +parsecProjectConstraints = do + userConstraint <- parsec + return $ ProjectConstraints (userConstraint, ConstraintSourceUnknown) + +newtype MaxBackjumps = MaxBackjumps {getMaxBackjumps :: Int} + +instance Newtype Int MaxBackjumps + +instance Parsec MaxBackjumps where + parsec = parseMaxBackjumps + +parseMaxBackjumps :: CabalParsing m => m MaxBackjumps +parseMaxBackjumps = MaxBackjumps <$> integral + +newtype AllowNewerNT = AllowNewerNT {getAllowNewerNT :: Maybe AllowNewer} + +instance Newtype (Maybe AllowNewer) AllowNewerNT + +instance Parsec AllowNewerNT where + parsec = parsecAllowNewer + +parsecAllowNewer :: CabalParsing m => m AllowNewerNT +parsecAllowNewer = AllowNewerNT . Just <$> parsec + +newtype AllowOlderNT = AllowOlderNT {getAllowOlderNT :: Maybe AllowOlder} + +instance Newtype (Maybe AllowOlder) AllowOlderNT + +instance Parsec AllowOlderNT where + parsec = parsecAllowOlder + +parsecAllowOlder :: CabalParsing m => m AllowOlderNT +parsecAllowOlder = AllowOlderNT . Just <$> parsec diff --git a/cabal-install/src/Distribution/Client/Utils/Parsec.hs b/cabal-install/src/Distribution/Client/Utils/Parsec.hs index abc9ddd1321..d7fcbf4778d 100644 --- a/cabal-install/src/Distribution/Client/Utils/Parsec.hs +++ b/cabal-install/src/Distribution/Client/Utils/Parsec.hs @@ -1,105 +1,90 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + module Distribution.Client.Utils.Parsec - ( renderParseError + ( remoteRepoGrammar + + -- ** Flag + , alaFlag + , Flag' + + -- ** NubList + , alaNubList + , alaNubList' + , NubList' + + -- ** Newtype wrappers + , module Distribution.Client.Utils.Newtypes ) where import Distribution.Client.Compat.Prelude -import System.FilePath (normalise) +import Distribution.Compat.Newtype import Prelude () -import qualified Data.ByteString as BS -import qualified Data.ByteString.Char8 as BS8 - -import Distribution.Parsec (PError (..), PWarning (..), Position (..), showPos, zeroPos) -import Distribution.Simple.Utils (fromUTF8BS) - --- | Render parse error highlighting the part of the input file. -renderParseError - :: FilePath - -> BS.ByteString - -> NonEmpty PError - -> [PWarning] - -> String -renderParseError filepath contents errors warnings = - unlines $ - [ "Errors encountered when parsing cabal file " <> filepath <> ":" - ] - ++ renderedErrors - ++ renderedWarnings - where - filepath' = normalise filepath - - -- lines of the input file. 'lines' is taken, so they are called rows - -- contents, line number, whether it's empty line - rows :: [(String, Int, Bool)] - rows = zipWith f (BS8.lines contents) [1 ..] - where - f bs i = let s = fromUTF8BS bs in (s, i, isEmptyOrComment s) - - rowsZipper = listToZipper rows - - isEmptyOrComment :: String -> Bool - isEmptyOrComment s = case dropWhile (== ' ') s of - "" -> True -- empty - ('-' : '-' : _) -> True -- comment - _ -> False - - renderedErrors = concatMap renderError errors - renderedWarnings = concatMap renderWarning warnings - - renderError :: PError -> [String] - renderError (PError pos@(Position row col) msg) - -- if position is 0:0, then it doesn't make sense to show input - -- looks like, Parsec errors have line-feed in them - | pos == zeroPos = msgs - | otherwise = msgs ++ formatInput row col - where - msgs = ["", filepath' ++ ":" ++ showPos pos ++ ": error:", trimLF msg, ""] - - renderWarning :: PWarning -> [String] - renderWarning (PWarning _ pos@(Position row col) msg) - | pos == zeroPos = msgs - | otherwise = msgs ++ formatInput row col - where - msgs = ["", filepath' ++ ":" ++ showPos pos ++ ": warning:", trimLF msg, ""] - - -- sometimes there are (especially trailing) newlines. - trimLF :: String -> String - trimLF = dropWhile (== '\n') . reverse . dropWhile (== '\n') . reverse - - -- format line: prepend the given line number - formatInput :: Int -> Int -> [String] - formatInput row col = case advance (row - 1) rowsZipper of - Zipper xs ys -> before ++ after - where - before = case span (\(_, _, b) -> b) xs of - (_, []) -> [] - (zs, z : _) -> map formatInputLine $ z : reverse zs - - after = case ys of - [] -> [] - (z : _zs) -> - [ formatInputLine z -- error line - , " | " ++ replicate (col - 1) ' ' ++ "^" -- pointer: ^ - ] - -- do we need rows after? - -- ++ map formatInputLine (take 1 zs) -- one row after - - formatInputLine :: (String, Int, Bool) -> String - formatInputLine (str, row, _) = leftPadShow row ++ " | " ++ str - - -- hopefully we don't need to work with over 99999 lines .cabal files - -- at that point small glitches in error messages are hopefully fine. - leftPadShow :: Int -> String - leftPadShow n = let s = show n in replicate (5 - length s) ' ' ++ s - -data Zipper a = Zipper [a] [a] - -listToZipper :: [a] -> Zipper a -listToZipper = Zipper [] - -advance :: Int -> Zipper a -> Zipper a -advance n z@(Zipper xs ys) - | n <= 0 = z - | otherwise = case ys of - [] -> z - (y : ys') -> advance (n - 1) $ Zipper (y : xs) ys' +import Distribution.Client.Types.Repo +import Distribution.Client.Types.RepoName +import Distribution.Client.Utils.Newtypes +import Distribution.FieldGrammar +import Distribution.Simple.Flag +import Distribution.Utils.NubList (NubList (..)) +import qualified Distribution.Utils.NubList as NubList + +-- | Like 'List' for usage with a 'FieldGrammar', but for 'Flag'. +-- This enables to parse type aliases such as 'FilePath' that do not have 'Parsec' instances +-- by using newtype variants such as 'FilePathNT'. +-- For example, if you need to parse a 'Flag FilePath', you can use 'alaFlag' FilePathNT'. +newtype Flag' b a = Flag' {_getFlag :: Flag a} + +-- | 'Flag'' constructor, with additional phantom argument to constrain the resulting type +alaFlag :: (a -> b) -> Flag a -> Flag' b a +alaFlag _ = Flag' + +instance Newtype (Flag a) (Flag' wrapper a) + +instance (Newtype a b, Parsec b) => Parsec (Flag' b a) where + parsec = pack . toFlag . (unpack :: b -> a) <$> parsec + +instance (Newtype a b, Pretty b) => Pretty (Flag' b a) where + pretty = pretty . (pack :: a -> b) . fromFlag . unpack + +-- | Like 'List' for usage with a 'FieldGrammar', but for 'NubList'. +newtype NubList' sep b a = NubList' {_getNubList :: NubList a} + +-- | 'alaNubList' and 'alaNubList'' are simply 'NubList'' constructor, with additional phantom +-- arguments to constrain the resulting type +-- +-- >>> :t alaNubList VCat +-- alaNubList VCat :: NubList a -> NubList' VCat (Identity a) a +-- +-- >>> :t alaNubList' FSep Token +-- alaNubList' FSep Token +-- :: NubList String -> NubList' FSep Token String +-- +-- >>> unpack' (alaNubList' FSep Token) <$> eitherParsec "foo bar foo" +-- Right ["foo","bar"] +alaNubList :: sep -> NubList a -> NubList' sep (Identity a) a +alaNubList _ = NubList' + +-- | More general version of 'alaNubList'. +alaNubList' :: sep -> (a -> b) -> NubList a -> NubList' sep b a +alaNubList' _ _ = NubList' + +instance Newtype (NubList a) (NubList' sep wrapper a) + +instance (Newtype a b, Ord a, Sep sep, Parsec b) => Parsec (NubList' sep b a) where + parsec = pack . NubList.toNubList . map (unpack :: b -> a) <$> parseSep (Proxy :: Proxy sep) parsec + +instance (Newtype a b, Sep sep, Pretty b) => Pretty (NubList' sep b a) where + pretty = prettySep (Proxy :: Proxy sep) . map (pretty . (pack :: a -> b)) . NubList.fromNubList . unpack + +remoteRepoGrammar :: RepoName -> ParsecFieldGrammar RemoteRepo RemoteRepo +remoteRepoGrammar name = + RemoteRepo + <$> pure name + <*> uniqueFieldAla "url" URI_NT remoteRepoURILens + <*> optionalField "secure" remoteRepoSecureLens + <*> monoidalFieldAla "root-keys" (alaList' FSep Token) remoteRepoRootKeysLens + <*> optionalFieldDefAla "key-threshold" KeyThreshold remoteRepoKeyThresholdLens 0 + <*> pure False -- we don't parse remoteRepoShouldTryHttps diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs index bf69b20ee04..caee779671b 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs @@ -606,6 +606,7 @@ instance Arbitrary ProjectConfigShared where projectConfigConfigFile <- arbitraryFlag arbitraryShortToken projectConfigProjectDir <- arbitraryFlag arbitraryShortToken projectConfigProjectFile <- arbitraryFlag arbitraryShortToken + projectConfigProjectFileParser <- arbitraryFlag arbitrary projectConfigIgnoreProject <- arbitrary projectConfigHcFlavor <- arbitrary projectConfigHcPath <- arbitraryFlag arbitraryShortToken @@ -652,6 +653,7 @@ instance Arbitrary ProjectConfigShared where <*> shrinker projectConfigConfigFile <*> shrinker projectConfigProjectDir <*> shrinker projectConfigProjectFile + <*> shrinker projectConfigProjectFileParser <*> shrinker projectConfigIgnoreProject <*> shrinker projectConfigHcFlavor <*> shrinkerAla (fmap NonEmpty) projectConfigHcPath @@ -691,6 +693,9 @@ instance Arbitrary ProjectConfigShared where projectConfigConstraintSource :: ConstraintSource projectConfigConstraintSource = ConstraintSourceProjectConfig nullProjectConfigPath +instance Arbitrary ProjectFileParser where + arbitrary = elements [ParsecParser, LegacyParser, FallbackParser, CompareParser] + instance Arbitrary ProjectConfigProvenance where arbitrary = elements [Implicit, Explicit (ProjectConfigPath $ "cabal.project" :| [])] diff --git a/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs b/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs index 179fef5688a..ef4f9fb7c9f 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs @@ -63,6 +63,7 @@ instance ToExpr ProjectConfig instance ToExpr ProjectConfigBuildOnly instance ToExpr ProjectConfigProvenance instance ToExpr ProjectConfigShared +instance ToExpr ProjectFileParser instance ToExpr RelaxDepMod instance ToExpr RelaxDeps instance ToExpr RelaxDepScope diff --git a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out index 95a718c5798..32c0ffc2a2f 100644 --- a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out +++ b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out @@ -1,4 +1,8 @@ # cabal check -Warning: pkg.cabal:0:0: "name" field missing Error: [Cabal-7035] -parse error +Error encountered when parsing cabal file pkg.cabal: + +pkg.cabal:0:0: error: +"name" field missing + + diff --git a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out index e2b3c055434..4e01a9cfc38 100644 --- a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out +++ b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out @@ -1,4 +1,8 @@ # cabal check -Warning: pkg.cabal:0:0: "version" field missing Error: [Cabal-7035] -parse error +Error encountered when parsing cabal file pkg.cabal: + +pkg.cabal:0:0: error: +"version" field missing + + diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out index 73d7d091749..a31f6fa12fb 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out @@ -12,54 +12,100 @@ Installing executable some-exe in Warning: The directory /cabal.dist/home/.cabal/store/ghc-/incoming/new-/cabal.dist/home/.cabal/store/ghc-/-/bin is not in the system search path. # checking cyclical loopback of a project importing itself # cabal v2-build -Error: [Cabal-7090] -Error parsing project file cyclical-0-self.project: - - cyclical import of cyclical-0-self.project; - cyclical-0-self.project - imported by: cyclical-0-self.project +Error: [Cabal-7167] +Error encountered when parsing project file cyclical-0-self.project: + +cyclical-0-self.project:3:1: error: +cyclical import of cyclical-0-self.project; + cyclical-0-self.project + imported by: cyclical-0-self.project + + 1 | packages: . + 2 | + 3 | import: cyclical-0-self.project + | ^ + # checking cyclical with hops; out and back # cabal v2-build -Error: [Cabal-7090] -Error parsing project file cyclical-1-out-back.config: - - cyclical import of cyclical-1-out-back.project; - cyclical-1-out-back.project - imported by: cyclical-1-out-back.config - imported by: cyclical-1-out-back.project +Error: [Cabal-7167] +Error encountered when parsing project file cyclical-1-out-back.config: + imported by: cyclical-1-out-back.project + +cyclical-1-out-back.config:1:1: error: +cyclical import of cyclical-1-out-back.project; + cyclical-1-out-back.project + imported by: cyclical-1-out-back.config + imported by: cyclical-1-out-back.project + + 1 | import: cyclical-1-out-back.project + | ^ + # checking cyclical with hops; out to a config that imports itself # cabal v2-build -Error: [Cabal-7090] -Error parsing project file cyclical-1-out-self.config: - - cyclical import of cyclical-1-out-self.config; - cyclical-1-out-self.config - imported by: cyclical-1-out-self.config - imported by: cyclical-1-out-self.project +Error: [Cabal-7167] +Error encountered when parsing project file cyclical-1-out-self.config: + imported by: cyclical-1-out-self.project + +cyclical-1-out-self.config:1:1: error: +cyclical import of cyclical-1-out-self.config; + cyclical-1-out-self.config + imported by: cyclical-1-out-self.config + imported by: cyclical-1-out-self.project + + 1 | import: cyclical-1-out-self.config + | ^ + # checking cyclical with hops; out, out, twice back # cabal v2-build -Error: [Cabal-7090] -Error parsing project file cyclical-2-out-out-backback-b.config: - - cyclical import of cyclical-2-out-out-backback.project; - cyclical-2-out-out-backback.project - imported by: cyclical-2-out-out-backback-b.config - imported by: cyclical-2-out-out-backback-a.config - imported by: cyclical-2-out-out-backback.project +Error: [Cabal-7167] +Error encountered when parsing project file cyclical-2-out-out-backback-b.config: + imported by: cyclical-2-out-out-backback-a.config + imported by: cyclical-2-out-out-backback.project + +cyclical-2-out-out-backback-b.config:1:1: error: +cyclical import of cyclical-2-out-out-backback.project; + cyclical-2-out-out-backback.project + imported by: cyclical-2-out-out-backback-b.config + imported by: cyclical-2-out-out-backback-a.config + imported by: cyclical-2-out-out-backback.project + + 1 | import: cyclical-2-out-out-backback.project + | ^ + # checking cyclical with hops; out, out, once back # cabal v2-build -Error: [Cabal-7090] -Error parsing project file cyclical-2-out-out-back-b.config: - - cyclical import of cyclical-2-out-out-back-a.config; - cyclical-2-out-out-back-a.config - imported by: cyclical-2-out-out-back-b.config - imported by: cyclical-2-out-out-back-a.config - imported by: cyclical-2-out-out-back.project +Error: [Cabal-7167] +Error encountered when parsing project file cyclical-2-out-out-back-b.config: + imported by: cyclical-2-out-out-back-a.config + imported by: cyclical-2-out-out-back.project + +cyclical-2-out-out-back-b.config:1:1: error: +cyclical import of cyclical-2-out-out-back-a.config; + cyclical-2-out-out-back-a.config + imported by: cyclical-2-out-out-back-b.config + imported by: cyclical-2-out-out-back-a.config + imported by: cyclical-2-out-out-back.project + + 1 | import: cyclical-2-out-out-back-a.config + | ^ + # checking cyclical with hops; out, out to a config that imports itself # cabal v2-build -Error: [Cabal-7090] -Error parsing project file cyclical-2-out-out-self-b.config: - - cyclical import of cyclical-2-out-out-self-b.config; - cyclical-2-out-out-self-b.config - imported by: cyclical-2-out-out-self-b.config - imported by: cyclical-2-out-out-self-a.config - imported by: cyclical-2-out-out-self.project +Error: [Cabal-7167] +Error encountered when parsing project file cyclical-2-out-out-self-b.config: + imported by: cyclical-2-out-out-self-a.config + imported by: cyclical-2-out-out-self.project + +cyclical-2-out-out-self-b.config:1:1: error: +cyclical import of cyclical-2-out-out-self-b.config; + cyclical-2-out-out-self-b.config + imported by: cyclical-2-out-out-self-b.config + imported by: cyclical-2-out-out-self-a.config + imported by: cyclical-2-out-out-self.project + + 1 | import: cyclical-2-out-out-self-b.config + | ^ + # checking that cyclical check doesn't false-positive on same file names in different folders; hoping within a folder and then into a subfolder # cabal v2-build Resolving dependencies... @@ -74,31 +120,55 @@ Building library for my-0.1... Up to date # checking that cyclical check catches a same file name that imports itself # cabal v2-build -Error: [Cabal-7090] -Error parsing project file same-filename/cyclical-same-filename-out-out-self.config: - - cyclical import of same-filename/cyclical-same-filename-out-out-self.config; - same-filename/cyclical-same-filename-out-out-self.config - imported by: same-filename/cyclical-same-filename-out-out-self.config - imported by: cyclical-same-filename-out-out-self.config - imported by: cyclical-same-filename-out-out-self.project +Error: [Cabal-7167] +Error encountered when parsing project file same-filename/cyclical-same-filename-out-out-self.config: + imported by: cyclical-same-filename-out-out-self.config + imported by: cyclical-same-filename-out-out-self.project + +same-filename/cyclical-same-filename-out-out-self.config:1:1: error: +cyclical import of same-filename/cyclical-same-filename-out-out-self.config; + same-filename/cyclical-same-filename-out-out-self.config + imported by: same-filename/cyclical-same-filename-out-out-self.config + imported by: cyclical-same-filename-out-out-self.config + imported by: cyclical-same-filename-out-out-self.project + + 1 | import: cyclical-same-filename-out-out-self.config + | ^ + # checking that cyclical check catches importing its importer (with the same file name) # cabal v2-build -Error: [Cabal-7090] -Error parsing project file same-filename/cyclical-same-filename-out-out-backback.config: - - cyclical import of cyclical-same-filename-out-out-backback.project; - cyclical-same-filename-out-out-backback.project - imported by: same-filename/cyclical-same-filename-out-out-backback.config - imported by: cyclical-same-filename-out-out-backback.config - imported by: cyclical-same-filename-out-out-backback.project +Error: [Cabal-7167] +Error encountered when parsing project file same-filename/cyclical-same-filename-out-out-backback.config: + imported by: cyclical-same-filename-out-out-backback.config + imported by: cyclical-same-filename-out-out-backback.project + +same-filename/cyclical-same-filename-out-out-backback.config:1:1: error: +cyclical import of cyclical-same-filename-out-out-backback.project; + cyclical-same-filename-out-out-backback.project + imported by: same-filename/cyclical-same-filename-out-out-backback.config + imported by: cyclical-same-filename-out-out-backback.config + imported by: cyclical-same-filename-out-out-backback.project + + 1 | import: ../cyclical-same-filename-out-out-backback.project + | ^ + # checking that cyclical check catches importing its importer's importer (hopping over same file names) # cabal v2-build -Error: [Cabal-7090] -Error parsing project file same-filename/cyclical-same-filename-out-out-back.config: - - cyclical import of cyclical-same-filename-out-out-back.config; - cyclical-same-filename-out-out-back.config - imported by: same-filename/cyclical-same-filename-out-out-back.config - imported by: cyclical-same-filename-out-out-back.config - imported by: cyclical-same-filename-out-out-back.project +Error: [Cabal-7167] +Error encountered when parsing project file same-filename/cyclical-same-filename-out-out-back.config: + imported by: cyclical-same-filename-out-out-back.config + imported by: cyclical-same-filename-out-out-back.project + +same-filename/cyclical-same-filename-out-out-back.config:1:1: error: +cyclical import of cyclical-same-filename-out-out-back.config; + cyclical-same-filename-out-out-back.config + imported by: same-filename/cyclical-same-filename-out-out-back.config + imported by: cyclical-same-filename-out-out-back.config + imported by: cyclical-same-filename-out-out-back.project + + 1 | import: ../cyclical-same-filename-out-out-back.config + | ^ + # checking that imports work skipping into a subfolder and then back out again and again # cabal v2-build Up to date @@ -129,8 +199,12 @@ After searching the rest of the dependency tree exhaustively, these were the goa Up to date # checking bad conditional # cabal v2-build -Error: [Cabal-7090] -Error parsing project file /bad-conditional.project: - - Cannot set compiler in a conditional clause of a cabal project file +Error: [Cabal-7167] +Error encountered when parsing project file bad-conditional.project: + +bad-conditional.project:0:0: error: +Cannot set compiler in a conditional clause of a cabal project file + + # checking that missing package message lists configuration provenance # cabal v2-build diff --git a/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out b/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out index 8d891bd421e..8f2609456ba 100644 --- a/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out +++ b/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out @@ -1,6 +1,6 @@ # cabal v2-update Downloading the latest package list from test-local-repo -Warning: In /cabal.dist/repo/my-lib-1.0.tar.gz: Errors encountered when parsing cabal file my-lib-1.0/my-lib.cabal: +Warning: In /cabal.dist/repo/my-lib-1.0.tar.gz: Error encountered when parsing cabal file my-lib-1.0/my-lib.cabal: my-lib-1.0/my-lib.cabal:4:22: error: unexpected Unknown SPDX license identifier: 'puppy' @@ -15,5 +15,6 @@ Unknown field: "puppy" 4 | license: puppy license :) 5 | puppy: teehee! | ^ + Error: [Cabal-7046] Failed to read my-lib-1.0/my-lib.cabal from archive /cabal.dist/repo/my-lib-1.0.tar.gz diff --git a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out index b94b99a9a08..17e4efd7a0e 100644 --- a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out +++ b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out @@ -1,4 +1,13 @@ # cabal build -Error: [Cabal-7090] -Error parsing project file cabal.project:4: - - 'source-repository-package' is a stanza, not a field. Remove the trailing ':' to parse a stanza. +Error: [Cabal-7167] +Error encountered when parsing project file cabal.project: + +cabal.project:4:1: error: +'source-repository-package' is a stanza, not a field. Remove the trailing ':' to parse a stanza. + + 1 | packages: . + 2 | + 3 | -- This is an error; a trailing `:` is syntax for a field, not a stanza! + 4 | source-repository-package: + | ^ + diff --git a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs index 39636819157..07abb78a40c 100644 --- a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs +++ b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs @@ -2,5 +2,5 @@ import Test.Cabal.Prelude main = cabalTest $ do result <- fails $ cabal' "build" [] - assertOutputContains "Error parsing project file" result + assertOutputContains "Error encountered when parsing project file" result assertOutputContains "'source-repository-package' is a stanza, not a field." result diff --git a/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out b/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out index d3c6ddb66c6..3949eaa47d6 100644 --- a/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out +++ b/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out @@ -1,25 +1,39 @@ # cabal v2-build Warnings found while parsing the project file, else.project: - - dir-else/else.config: Unrecognized section '_' on line 3 + - dir-else/else.config:3:5: Invalid subsection "_" # cabal v2-build -Error: [Cabal-7090] -Error parsing project file cabal.project:3: - - Failed to parse 'if(_)' with error: - "" (line 1, column 1): unexpected SecArgName (Position 1 4) "_" +Error: [Cabal-7167] +Error encountered when parsing project file cabal.project: + +cabal.project:3:4: error: +unexpected SecArgName (Position 3 4) "_" + + 3 | if _ + | ^ + # cabal v2-build -Error: [Cabal-7090] -Error parsing project file dir-if/if.config:3: - - dir-if/if.config - imported by: if.project - Failed to parse 'if(_)' with error: - "" (line 1, column 1): unexpected SecArgName (Position 1 4) "_" +Error: [Cabal-7167] +Error encountered when parsing project file dir-if/if.config: + imported by: if.project + +dir-if/if.config:3:4: error: +unexpected SecArgName (Position 3 4) "_" + + 3 | if _ + | ^ + # cabal v2-build -Error: [Cabal-7090] -Error parsing project file dir-elif/elif.config:4: - - dir-elif/elif.config - imported by: elif.project - Failed to parse 'elif(_)' with error: - "" (line 1, column 1): unexpected SecArgName (Position 1 6) "_" +Error: [Cabal-7167] +Error encountered when parsing project file dir-elif/elif.config: + imported by: elif.project + +dir-elif/elif.config:4:6: error: +unexpected SecArgName (Position 4 6) "_" + + 3 | if false + 4 | elif _ + | ^ + # cabal v2-build Warnings found while parsing the project file, else.project: - - dir-else/else.config: Unrecognized section '_' on line 3 + - dir-else/else.config:3:5: Invalid subsection "_" diff --git a/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.test.hs b/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.test.hs index ca3bce1c59d..34d06d8f93a 100644 --- a/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.test.hs +++ b/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.test.hs @@ -9,20 +9,20 @@ main = cabalTest . recordMode RecordMarked $ do outDefault <- fails $ cabal' "v2-build" [ "all", "--dry-run", "--project-file=cabal.project" ] - assertOutputContains "Error parsing project file cabal.project:3" outDefault + assertOutputContains "cabal.project:3:4: error:" outDefault assertOutputDoesNotContain "imported by:" outDefault outIf <- fails $ cabal' "v2-build" [ "all", "--dry-run", "--project-file=if.project" ] - assertOutputContains (normalizeWindowsOutput "Error parsing project file dir-if/if.config:3") outIf + assertOutputContains (normalizeWindowsOutput "dir-if/if.config:3:4: error:") outIf assertOutputContains "imported by:" outIf outElif <- fails $ cabal' "v2-build" [ "all", "--dry-run", "--project-file=elif.project" ] - assertOutputContains (normalizeWindowsOutput "Error parsing project file dir-elif/elif.config:4") outElif + assertOutputContains (normalizeWindowsOutput "dir-elif/elif.config:4:6: error:") outElif assertOutputContains "imported by:" outElif outElse <- fails $ cabal' "v2-build" [ "all", "--dry-run", "--project-file=else.project" ] assertOutputContains "Warnings found while parsing the project file, else.project:" outElse - assertOutputContains (normalizeWindowsOutput "- dir-else/else.config: Unrecognized section '_' on line 3") outElse + assertOutputContains (normalizeWindowsOutput "- dir-else/else.config:3:5: Invalid subsection \"_\"") outElse assertOutputContains "When using configuration from:" outElse return () diff --git a/cabal-testsuite/PackageTests/ProjectImport/ParseWarningProvenance/cabal.out b/cabal-testsuite/PackageTests/ProjectImport/ParseWarningProvenance/cabal.out index 90265370e7a..6fb7a1906b3 100644 --- a/cabal-testsuite/PackageTests/ProjectImport/ParseWarningProvenance/cabal.out +++ b/cabal-testsuite/PackageTests/ProjectImport/ParseWarningProvenance/cabal.out @@ -1,10 +1,10 @@ # cabal v2-build Warnings found while parsing the project file, cabal.project: - - dir-x/a.config: Unrecognized section '-' on line 1 - - dir-x/a.config: Unrecognized section '-' on line 2 - - dir-x/a.config: Unrecognized section '-' on line 3 - - dir-y/a.config: Unrecognized section '-' on line 123 - - x.config: Unrecognized section '-' on line 1 - - x.config: Unrecognized section '-' on line 2 - - x.config: Unrecognized section '-' on line 3 - - y.config: Unrecognized section '-' on line 123 + - dir-x/a.config:1:1: Invalid subsection "-" + - dir-x/a.config:2:1: Invalid subsection "-" + - dir-x/a.config:3:1: Invalid subsection "-" + - dir-y/a.config:123:1: Invalid subsection "-" + - x.config:1:1: Invalid subsection "-" + - x.config:2:1: Invalid subsection "-" + - x.config:3:1: Invalid subsection "-" + - y.config:123:1: Invalid subsection "-" diff --git a/cabal-testsuite/PackageTests/Regression/T5213/cabal.out b/cabal-testsuite/PackageTests/Regression/T5213/cabal.out index fb0e7ad7a9a..bcbb8421e34 100644 --- a/cabal-testsuite/PackageTests/Regression/T5213/cabal.out +++ b/cabal-testsuite/PackageTests/Regression/T5213/cabal.out @@ -1,4 +1,6 @@ # cabal new-test +Warnings found while parsing the project file, cabal.project: + - cabal.project:4:3: The field "library-coverage" is deprecated in the Cabal specification version 1.22. Please use 'coverage' field instead. Resolving dependencies... Build profile: -w ghc- -O1 In order, the following will be built: diff --git a/cabal-testsuite/PackageTests/Regression/T5213/cabal.project b/cabal-testsuite/PackageTests/Regression/T5213/cabal.project index e1c33e00303..45f18c1f5cb 100644 --- a/cabal-testsuite/PackageTests/Regression/T5213/cabal.project +++ b/cabal-testsuite/PackageTests/Regression/T5213/cabal.project @@ -1,4 +1,4 @@ packages: . package cabal-gh5213 - library-coverage: true + library-coverage: True diff --git a/cabal-validate/src/Main.hs b/cabal-validate/src/Main.hs index bf5e30a26c2..a9ab2bc8db4 100644 --- a/cabal-validate/src/Main.hs +++ b/cabal-validate/src/Main.hs @@ -359,6 +359,14 @@ cliTests opts = do ++ tastyArgs opts ) + timedCabalBin + opts + "cabal-install" + "test:parser-tests" + ( jobsArgs opts + ++ tastyArgs opts + ) + timedCabalBin opts "cabal-install" diff --git a/cabal.validate.project b/cabal.validate.project index 5075458c1b8..2fbc461042b 100644 --- a/cabal.validate.project +++ b/cabal.validate.project @@ -9,6 +9,8 @@ program-options ghc-options: -Werror -- if you are developing on a system without TH, use a `cabal.validate.project.local` --- to disable this +-- to disable the git-rev flag. +-- The +legacy-comparision flag is to set the default project file parser to compare +-- the result of the legacy and parsec parser and fail if they are not equal. package cabal-install - flags: +git-rev + flags: +git-rev +legacy-comparison diff --git a/changelog.d/pr-8889 b/changelog.d/pr-8889 new file mode 100644 index 00000000000..0c3ec7ce79d --- /dev/null +++ b/changelog.d/pr-8889 @@ -0,0 +1,27 @@ +synopsis: Replace cabal project parsing with parsec parser +packages: cabal-install Cabal Cabal-syntax +prs: #8889 +issues: #6101 #7748 +significance: significant + +description: { + +Replaced the legacy cabal.project parser with a new implementation based on the same +parsing infrastructure as cabal files. + +The new parser replicates the grammar of the legacy parser, ensuring that it generates identical `ProjectConfig` values. +The implementation leverages existing Parsec infrastructure, including FieldGrammar and other utilities from the .cabal file parser. +The error messages are now more accurate and include the line and column of the error. + +There is a new flag `--project-file-parser=` which can be used to select the parser to use. + +* `legacy` - the old parser +* `default` - the default, by default the `fallback` strategy is used unless you have compiled `cabal-install` with `-f+legacy-comparison`. +* `parsec` - the new parser using parsec +* `fallback` - the new parser using parsec, but falling back to the old parser if it fails +* `compare` - the new parser using parsec, but comparing the results with the old parser + +In the next release we plan to remove the legacy parser. + + +} diff --git a/doc/cabal-project-description-file.rst b/doc/cabal-project-description-file.rst index 7b9bd5d5241..adae4514b14 100644 --- a/doc/cabal-project-description-file.rst +++ b/doc/cabal-project-description-file.rst @@ -413,6 +413,22 @@ package, and thus apply globally: This option can only be specified from the command line. +.. _cmdoption-project-file-parser: +.. option:: --project-file-parser=PARSER + + :since: 3.18 + + Specifies the parser to use for reading the project file. The available + parsers are: + + * ``legacy`` - the old parser (will be removed in a future release) + * ``default`` - the default parser (uses ``fallback`` unless compiled with ``-f+legacy-comparison``) + * ``parsec`` - the new parser using Parsec + * ``fallback`` - the new parser using Parsec, but falling back to the old parser if it fails + * ``compare`` - the new parser using Parsec, but comparing the results with the old parser + + This option can only be specified from the command line. + .. option:: -z, --ignore-project Ignores the local ``cabal.project`` file and uses the default @@ -1824,7 +1840,7 @@ Advanced global configuration options ``--build-summary=TEMPLATE``. Undocumented fields: ``root-cmd``, ``symlink-bindir``, ``build-log``, -``remote-build-reporting``, ``report-planned-failure``, ``offline``. +``remote-build-reporting``, ``report-planning-failure``, ``offline``. Advanced solver options ^^^^^^^^^^^^^^^^^^^^^^^ From f548cef9cd79b3edd5d0e91c8e7e77261b82e6a1 Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Tue, 22 Jul 2025 11:07:04 +0100 Subject: [PATCH 27/39] Error messages: Tweak how parse errors from parsec parser are displayed * Error messages are indented by 2 spaces * 0:0 positions are never shown * More attempts are made to remove extra newlines. --- .../src/Distribution/Client/Errors/Parser.hs | 57 ++++--- .../src/Distribution/Client/ProjectFlags.hs | 3 +- .../ConfiguredPackage/Sanity/NoName/cabal.out | 6 +- .../Sanity/NoVersion/cabal.out | 6 +- .../ConditionalAndImport/cabal.out | 146 +++++++++--------- .../IndexCabalFileParseError/cabal.out | 22 +-- .../FieldStanzaConfusion/cabal.out | 14 +- .../FieldStanzaConfusion/cabal.test.hs | 2 +- .../ParseErrorProvenance/cabal.out | 26 ++-- 9 files changed, 149 insertions(+), 133 deletions(-) diff --git a/cabal-install/src/Distribution/Client/Errors/Parser.hs b/cabal-install/src/Distribution/Client/Errors/Parser.hs index b2e19397925..9b917db9886 100644 --- a/cabal-install/src/Distribution/Client/Errors/Parser.hs +++ b/cabal-install/src/Distribution/Client/Errors/Parser.hs @@ -154,7 +154,7 @@ renderInstalledPackageInfoSourceMsgs PInstalledPackageInfo (errors, warnings) = renderParseErrorNoFile :: String -> [PError] -> [PWarning] -> String renderParseErrorNoFile herald errors warnings = - renderParseErrorGeneral herald "" Nothing (const []) errors warnings + renderParseErrorGeneral herald Nothing Nothing (const []) errors warnings -- | Render a parse error which resulted from a file on disk renderParseErrorFile @@ -169,7 +169,7 @@ renderParseErrorFile -> ([PError], [PWarning]) -> String renderParseErrorFile herald filepath provenance contents (errors, warnings) = - renderParseErrorGeneral (herald <> " file " <> filepath) (filepath' <> ":") provenance formatInput errors warnings + renderParseErrorGeneral (herald <> " file " <> filepath) (Just (filepath' <> ":")) provenance formatInput errors warnings where filepath' = normalise filepath @@ -217,8 +217,12 @@ renderParseErrorFile herald filepath provenance contents (errors, warnings) = -- | A generic rendering function which can render from many sources. renderParseErrorGeneral :: String - -> String + -- ^ What we were parsing when the error occurred. + -> Maybe String + -- ^ A simpler/shorter header to display when displaying each error (normally a filepath) -> Maybe String + -- ^ Provenance, used to print additional context about what file failed (used to print the import path of a project + -- file which failed to parse) -> (Position -> [String]) -- ^ Extra information to render based on the position -> [PError] @@ -226,11 +230,12 @@ renderParseErrorGeneral -> String renderParseErrorGeneral header err_header provenance extra_info errors warnings = unlines $ - [ warningsOrErrors <> " encountered when parsing" <> header' <> ":" + [ warningsOrErrors <> " parsing" <> header' <> ":" ] ++ [p | Just p <- [provenance]] - ++ renderedErrors - ++ renderedWarnings + ++ [""] -- Place a newline between the header and the errors/warnings + -- Place a newline between each error and warning + ++ intersperse "" (renderedWarnings ++ renderedErrors) where warningsOrErrors = case errors of [] -> case warnings of @@ -241,24 +246,36 @@ renderParseErrorGeneral header err_header provenance extra_info errors warnings header' = if null header then "" else (" " <> header) - renderedErrors = concatMap renderError (sortBy (comparing perrorPosition) errors) - renderedWarnings = concatMap renderWarning (sortBy (comparing pwarningPosition) warnings) + renderedErrors = map renderError (sortBy (comparing perrorPosition) errors) + renderedWarnings = map renderWarning (sortBy (comparing pwarningPosition) warnings) - renderError :: PError -> [String] - renderError (PError pos msg) + renderErrorOrWarning :: String -> Position -> String -> String + renderErrorOrWarning err_type pos msg -- if position is 0:0, then it doesn't make sense to show input -- looks like, Parsec errors have line-feed in them - | pos == zeroPos = msgs - | otherwise = msgs ++ extra_info pos - where - msgs = ["", err_header ++ showPos pos ++ ": error:", trimLF msg, ""] - - renderWarning :: PWarning -> [String] - renderWarning (PWarning _ pos msg) - | pos == zeroPos = msgs - | otherwise = msgs ++ extra_info pos + | pos == zeroPos = unlines (herald : map indent user_msg) + | otherwise = unlines (herald : map indent (user_msg ++ extra_info pos)) where - msgs = ["", err_header ++ showPos pos ++ ": warning:", trimLF msg, ""] + herald = renderErrorHerald pos ++ err_type ++ ":" + user_msg = lines (trimLF msg) + + indent :: String -> String + indent s = replicate 2 ' ' ++ s + + -- Don't render the 0:0 position + renderErrorHerald :: Position -> String + renderErrorHerald pos = + case (err_header, pos == zeroPos) of + (Nothing, True) -> "" + (Nothing, False) -> showPos pos ++ ": " + (Just herald, True) -> herald ++ " " + (Just herald, False) -> herald ++ showPos pos ++ ": " + + renderError :: PError -> String + renderError (PError pos msg) = renderErrorOrWarning "error" pos msg + + renderWarning :: PWarning -> String + renderWarning (PWarning _ pos msg) = renderErrorOrWarning "warning" pos msg -- sometimes there are (especially trailing) newlines. trimLF :: String -> String diff --git a/cabal-install/src/Distribution/Client/ProjectFlags.hs b/cabal-install/src/Distribution/Client/ProjectFlags.hs index 9214b6a37cf..9060f3b863b 100644 --- a/cabal-install/src/Distribution/Client/ProjectFlags.hs +++ b/cabal-install/src/Distribution/Client/ProjectFlags.hs @@ -59,8 +59,7 @@ defaultProjectFlags = { flagProjectDir = mempty , flagProjectFile = mempty , flagIgnoreProject = toFlag False - , -- Should we use 'Last' here? - flagProjectFileParser = mempty + , flagProjectFileParser = mempty } projectFlagsOptions :: ShowOrParseArgs -> [OptionField ProjectFlags] diff --git a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out index 32c0ffc2a2f..decf3cfcca4 100644 --- a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out +++ b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoName/cabal.out @@ -1,8 +1,8 @@ # cabal check Error: [Cabal-7035] -Error encountered when parsing cabal file pkg.cabal: +Error parsing cabal file pkg.cabal: -pkg.cabal:0:0: error: -"name" field missing +pkg.cabal: error: + "name" field missing diff --git a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out index 4e01a9cfc38..a539f19dbd2 100644 --- a/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out +++ b/cabal-testsuite/PackageTests/Check/ConfiguredPackage/Sanity/NoVersion/cabal.out @@ -1,8 +1,8 @@ # cabal check Error: [Cabal-7035] -Error encountered when parsing cabal file pkg.cabal: +Error parsing cabal file pkg.cabal: -pkg.cabal:0:0: error: -"version" field missing +pkg.cabal: error: + "version" field missing diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out index a31f6fa12fb..4e2cfe368c3 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out @@ -13,98 +13,98 @@ Warning: The directory /cabal.dist/home/.cabal/store/ghc-/incoming # checking cyclical loopback of a project importing itself # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file cyclical-0-self.project: +Error parsing project file cyclical-0-self.project: cyclical-0-self.project:3:1: error: -cyclical import of cyclical-0-self.project; - cyclical-0-self.project - imported by: cyclical-0-self.project + cyclical import of cyclical-0-self.project; + cyclical-0-self.project + imported by: cyclical-0-self.project + 1 | packages: . + 2 | + 3 | import: cyclical-0-self.project + | ^ - 1 | packages: . - 2 | - 3 | import: cyclical-0-self.project - | ^ # checking cyclical with hops; out and back # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file cyclical-1-out-back.config: +Error parsing project file cyclical-1-out-back.config: imported by: cyclical-1-out-back.project cyclical-1-out-back.config:1:1: error: -cyclical import of cyclical-1-out-back.project; - cyclical-1-out-back.project - imported by: cyclical-1-out-back.config - imported by: cyclical-1-out-back.project + cyclical import of cyclical-1-out-back.project; + cyclical-1-out-back.project + imported by: cyclical-1-out-back.config + imported by: cyclical-1-out-back.project + 1 | import: cyclical-1-out-back.project + | ^ - 1 | import: cyclical-1-out-back.project - | ^ # checking cyclical with hops; out to a config that imports itself # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file cyclical-1-out-self.config: +Error parsing project file cyclical-1-out-self.config: imported by: cyclical-1-out-self.project cyclical-1-out-self.config:1:1: error: -cyclical import of cyclical-1-out-self.config; - cyclical-1-out-self.config - imported by: cyclical-1-out-self.config - imported by: cyclical-1-out-self.project + cyclical import of cyclical-1-out-self.config; + cyclical-1-out-self.config + imported by: cyclical-1-out-self.config + imported by: cyclical-1-out-self.project + 1 | import: cyclical-1-out-self.config + | ^ - 1 | import: cyclical-1-out-self.config - | ^ # checking cyclical with hops; out, out, twice back # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file cyclical-2-out-out-backback-b.config: +Error parsing project file cyclical-2-out-out-backback-b.config: imported by: cyclical-2-out-out-backback-a.config imported by: cyclical-2-out-out-backback.project cyclical-2-out-out-backback-b.config:1:1: error: -cyclical import of cyclical-2-out-out-backback.project; - cyclical-2-out-out-backback.project - imported by: cyclical-2-out-out-backback-b.config - imported by: cyclical-2-out-out-backback-a.config - imported by: cyclical-2-out-out-backback.project + cyclical import of cyclical-2-out-out-backback.project; + cyclical-2-out-out-backback.project + imported by: cyclical-2-out-out-backback-b.config + imported by: cyclical-2-out-out-backback-a.config + imported by: cyclical-2-out-out-backback.project + 1 | import: cyclical-2-out-out-backback.project + | ^ - 1 | import: cyclical-2-out-out-backback.project - | ^ # checking cyclical with hops; out, out, once back # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file cyclical-2-out-out-back-b.config: +Error parsing project file cyclical-2-out-out-back-b.config: imported by: cyclical-2-out-out-back-a.config imported by: cyclical-2-out-out-back.project cyclical-2-out-out-back-b.config:1:1: error: -cyclical import of cyclical-2-out-out-back-a.config; - cyclical-2-out-out-back-a.config - imported by: cyclical-2-out-out-back-b.config - imported by: cyclical-2-out-out-back-a.config - imported by: cyclical-2-out-out-back.project + cyclical import of cyclical-2-out-out-back-a.config; + cyclical-2-out-out-back-a.config + imported by: cyclical-2-out-out-back-b.config + imported by: cyclical-2-out-out-back-a.config + imported by: cyclical-2-out-out-back.project + 1 | import: cyclical-2-out-out-back-a.config + | ^ - 1 | import: cyclical-2-out-out-back-a.config - | ^ # checking cyclical with hops; out, out to a config that imports itself # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file cyclical-2-out-out-self-b.config: +Error parsing project file cyclical-2-out-out-self-b.config: imported by: cyclical-2-out-out-self-a.config imported by: cyclical-2-out-out-self.project cyclical-2-out-out-self-b.config:1:1: error: -cyclical import of cyclical-2-out-out-self-b.config; - cyclical-2-out-out-self-b.config - imported by: cyclical-2-out-out-self-b.config - imported by: cyclical-2-out-out-self-a.config - imported by: cyclical-2-out-out-self.project + cyclical import of cyclical-2-out-out-self-b.config; + cyclical-2-out-out-self-b.config + imported by: cyclical-2-out-out-self-b.config + imported by: cyclical-2-out-out-self-a.config + imported by: cyclical-2-out-out-self.project + 1 | import: cyclical-2-out-out-self-b.config + | ^ - 1 | import: cyclical-2-out-out-self-b.config - | ^ # checking that cyclical check doesn't false-positive on same file names in different folders; hoping within a folder and then into a subfolder # cabal v2-build @@ -121,53 +121,53 @@ Up to date # checking that cyclical check catches a same file name that imports itself # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file same-filename/cyclical-same-filename-out-out-self.config: +Error parsing project file same-filename/cyclical-same-filename-out-out-self.config: imported by: cyclical-same-filename-out-out-self.config imported by: cyclical-same-filename-out-out-self.project same-filename/cyclical-same-filename-out-out-self.config:1:1: error: -cyclical import of same-filename/cyclical-same-filename-out-out-self.config; - same-filename/cyclical-same-filename-out-out-self.config - imported by: same-filename/cyclical-same-filename-out-out-self.config - imported by: cyclical-same-filename-out-out-self.config - imported by: cyclical-same-filename-out-out-self.project + cyclical import of same-filename/cyclical-same-filename-out-out-self.config; + same-filename/cyclical-same-filename-out-out-self.config + imported by: same-filename/cyclical-same-filename-out-out-self.config + imported by: cyclical-same-filename-out-out-self.config + imported by: cyclical-same-filename-out-out-self.project + 1 | import: cyclical-same-filename-out-out-self.config + | ^ - 1 | import: cyclical-same-filename-out-out-self.config - | ^ # checking that cyclical check catches importing its importer (with the same file name) # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file same-filename/cyclical-same-filename-out-out-backback.config: +Error parsing project file same-filename/cyclical-same-filename-out-out-backback.config: imported by: cyclical-same-filename-out-out-backback.config imported by: cyclical-same-filename-out-out-backback.project same-filename/cyclical-same-filename-out-out-backback.config:1:1: error: -cyclical import of cyclical-same-filename-out-out-backback.project; - cyclical-same-filename-out-out-backback.project - imported by: same-filename/cyclical-same-filename-out-out-backback.config - imported by: cyclical-same-filename-out-out-backback.config - imported by: cyclical-same-filename-out-out-backback.project + cyclical import of cyclical-same-filename-out-out-backback.project; + cyclical-same-filename-out-out-backback.project + imported by: same-filename/cyclical-same-filename-out-out-backback.config + imported by: cyclical-same-filename-out-out-backback.config + imported by: cyclical-same-filename-out-out-backback.project + 1 | import: ../cyclical-same-filename-out-out-backback.project + | ^ - 1 | import: ../cyclical-same-filename-out-out-backback.project - | ^ # checking that cyclical check catches importing its importer's importer (hopping over same file names) # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file same-filename/cyclical-same-filename-out-out-back.config: +Error parsing project file same-filename/cyclical-same-filename-out-out-back.config: imported by: cyclical-same-filename-out-out-back.config imported by: cyclical-same-filename-out-out-back.project same-filename/cyclical-same-filename-out-out-back.config:1:1: error: -cyclical import of cyclical-same-filename-out-out-back.config; - cyclical-same-filename-out-out-back.config - imported by: same-filename/cyclical-same-filename-out-out-back.config - imported by: cyclical-same-filename-out-out-back.config - imported by: cyclical-same-filename-out-out-back.project + cyclical import of cyclical-same-filename-out-out-back.config; + cyclical-same-filename-out-out-back.config + imported by: same-filename/cyclical-same-filename-out-out-back.config + imported by: cyclical-same-filename-out-out-back.config + imported by: cyclical-same-filename-out-out-back.project + 1 | import: ../cyclical-same-filename-out-out-back.config + | ^ - 1 | import: ../cyclical-same-filename-out-out-back.config - | ^ # checking that imports work skipping into a subfolder and then back out again and again # cabal v2-build @@ -200,10 +200,10 @@ Up to date # checking bad conditional # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file bad-conditional.project: +Error parsing project file bad-conditional.project: -bad-conditional.project:0:0: error: -Cannot set compiler in a conditional clause of a cabal project file +bad-conditional.project: error: + Cannot set compiler in a conditional clause of a cabal project file # checking that missing package message lists configuration provenance diff --git a/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out b/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out index 8f2609456ba..485ef98565a 100644 --- a/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out +++ b/cabal-testsuite/PackageTests/IndexCabalFileParseError/cabal.out @@ -1,20 +1,20 @@ # cabal v2-update Downloading the latest package list from test-local-repo -Warning: In /cabal.dist/repo/my-lib-1.0.tar.gz: Error encountered when parsing cabal file my-lib-1.0/my-lib.cabal: +Warning: In /cabal.dist/repo/my-lib-1.0.tar.gz: Error parsing cabal file my-lib-1.0/my-lib.cabal: -my-lib-1.0/my-lib.cabal:4:22: error: -unexpected Unknown SPDX license identifier: 'puppy' +my-lib-1.0/my-lib.cabal:5:1: warning: + Unknown field: "puppy" + 4 | license: puppy license :) + 5 | puppy: teehee! + | ^ - 3 | version: 1.0 - 4 | license: puppy license :) - | ^ -my-lib-1.0/my-lib.cabal:5:1: warning: -Unknown field: "puppy" +my-lib-1.0/my-lib.cabal:4:22: error: + unexpected Unknown SPDX license identifier: 'puppy' + 3 | version: 1.0 + 4 | license: puppy license :) + | ^ - 4 | license: puppy license :) - 5 | puppy: teehee! - | ^ Error: [Cabal-7046] Failed to read my-lib-1.0/my-lib.cabal from archive /cabal.dist/repo/my-lib-1.0.tar.gz diff --git a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out index 17e4efd7a0e..5423e2454ec 100644 --- a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out +++ b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.out @@ -1,13 +1,13 @@ # cabal build Error: [Cabal-7167] -Error encountered when parsing project file cabal.project: +Error parsing project file cabal.project: cabal.project:4:1: error: -'source-repository-package' is a stanza, not a field. Remove the trailing ':' to parse a stanza. + 'source-repository-package' is a stanza, not a field. Remove the trailing ':' to parse a stanza. + 1 | packages: . + 2 | + 3 | -- This is an error; a trailing `:` is syntax for a field, not a stanza! + 4 | source-repository-package: + | ^ - 1 | packages: . - 2 | - 3 | -- This is an error; a trailing `:` is syntax for a field, not a stanza! - 4 | source-repository-package: - | ^ diff --git a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs index 07abb78a40c..39636819157 100644 --- a/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs +++ b/cabal-testsuite/PackageTests/ProjectConfig/FieldStanzaConfusion/cabal.test.hs @@ -2,5 +2,5 @@ import Test.Cabal.Prelude main = cabalTest $ do result <- fails $ cabal' "build" [] - assertOutputContains "Error encountered when parsing project file" result + assertOutputContains "Error parsing project file" result assertOutputContains "'source-repository-package' is a stanza, not a field." result diff --git a/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out b/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out index 3949eaa47d6..b2d1fd35fc7 100644 --- a/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out +++ b/cabal-testsuite/PackageTests/ProjectImport/ParseErrorProvenance/cabal.out @@ -3,36 +3,36 @@ Warnings found while parsing the project file, else.project: - dir-else/else.config:3:5: Invalid subsection "_" # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file cabal.project: +Error parsing project file cabal.project: cabal.project:3:4: error: -unexpected SecArgName (Position 3 4) "_" + unexpected SecArgName (Position 3 4) "_" + 3 | if _ + | ^ - 3 | if _ - | ^ # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file dir-if/if.config: +Error parsing project file dir-if/if.config: imported by: if.project dir-if/if.config:3:4: error: -unexpected SecArgName (Position 3 4) "_" + unexpected SecArgName (Position 3 4) "_" + 3 | if _ + | ^ - 3 | if _ - | ^ # cabal v2-build Error: [Cabal-7167] -Error encountered when parsing project file dir-elif/elif.config: +Error parsing project file dir-elif/elif.config: imported by: elif.project dir-elif/elif.config:4:6: error: -unexpected SecArgName (Position 4 6) "_" + unexpected SecArgName (Position 4 6) "_" + 3 | if false + 4 | elif _ + | ^ - 3 | if false - 4 | elif _ - | ^ # cabal v2-build Warnings found while parsing the project file, else.project: From 8c46f48db9d9170d54f0587e5e7ac0776015a9bc Mon Sep 17 00:00:00 2001 From: Artem Pelenitsyn Date: Tue, 15 Jul 2025 12:12:13 -0400 Subject: [PATCH 28/39] CI: add GHC 8.6.5 to validate-old-ghcs fix #11073 --- .github/workflows/validate.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index abf19bffab8..8ced64a916f 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -55,11 +55,11 @@ jobs: - { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } - { os: ubuntu-22.04, shell: bash } - { os: macos-latest, shell: bash } - # If you remove something from here, then add it to the old-ghcs job. - # Also a removed GHC from here means that we are actually dropping - # support, so the PR *must* have a changelog entry. ghc: [ + # IMPORTANT: If you remove a version from this list, then add it to the old-ghcs job below. + # Also a removed GHC from here means that we are actually dropping + # support, so the PR *must* have a changelog entry. "9.12.2", "9.10.2", "9.8.4", @@ -251,7 +251,7 @@ jobs: strategy: matrix: extra-ghc: - ["8.4.4", "8.2.2", "8.0.2"] + ["8.6.5", "8.4.4", "8.2.2", "8.0.2"] ## GHC 7.10.3 does not install on ubuntu-22.04 with ghcup. ## Older GHCs are not supported by ghcup in the first place. fail-fast: false From 6c14a4500e568c8f5b9a5e03622854ac14ca4097 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Tue, 22 Jul 2025 13:06:49 +0800 Subject: [PATCH 29/39] Mark Terminate package test as flaky wrt #11087 --- .../PackageTests/NewBuild/CmdRun/Terminate/cabal.test.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/Terminate/cabal.test.hs b/cabal-testsuite/PackageTests/NewBuild/CmdRun/Terminate/cabal.test.hs index 8c3277174b8..cf8f179d70f 100644 --- a/cabal-testsuite/PackageTests/NewBuild/CmdRun/Terminate/cabal.test.hs +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/Terminate/cabal.test.hs @@ -19,7 +19,7 @@ without forking in the future.) main :: IO () main = do skipIfWindows "depends on `unix`" - cabalTest $ do + cabalTest $ flakyIfCI 8416 $ do -- timestamped logging to aid with #8416 let logIO msg = do ts <- Time.getCurrentTime From cf306c913d48f5a233b1f56e8845315a8ed9ea60 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 23 Jul 2025 10:09:48 -0400 Subject: [PATCH 30/39] Typo in Makefile .PHONY target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e488ce17cf2..2c6ab052461 100644 --- a/Makefile +++ b/Makefile @@ -189,7 +189,7 @@ custom-setup-tests : hackage-parsec-tests : $(CABALRUN) hackage-tests -- parsec +RTS -s -qg -I0 -A64M -N${THREADS} -RTS ${TEST} -.PHONY: hackage-rountrip-tests +.PHONY: hackage-roundtrip-tests hackage-roundtrip-tests : $(CABALRUN) hackage-tests -- roundtrip +RTS -s -qg -I0 -A64M -N${THREADS} -RTS ${TEST} From c7f6b506a35fd5a75938d1b9f98f93a679e3f341 Mon Sep 17 00:00:00 2001 From: JMCT Date: Wed, 23 Jul 2025 13:03:03 -0400 Subject: [PATCH 31/39] Add Tikhon's key --- cabal-install/src/Distribution/Client/Config.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index 77348dbc638..4e8b241779a 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -935,6 +935,8 @@ defaultHackageRemoteRepoKeys = "be75553f3c7ba1dbe298da81f1d1b05c9d39dd8ed2616c9bddf1525ca8c03e48" , -- Joachim Breitner (5iUgwqZCWrCJktqMx0bBMIuoIyT4A1RYGozzchRN9rA=) "d26e46f3b631aae1433b89379a6c68bd417eb5d1c408f0643dcc07757fece522" + , -- Tikhon Jelvis (06nM6r1kOYt49YE5e1+j8VKiiYUFjFQ6HrOpPZu+fDE=) + "c7de58fc6a224b92b5b513f26fbb8b370f2d97c7cfe0075a951314a55734be93" ] -- | The required threshold of root key signatures for hackage.haskell.org From bcc974e11225271427b09f4b284d430654cd0fcf Mon Sep 17 00:00:00 2001 From: JMCT Date: Wed, 23 Jul 2025 13:08:40 -0400 Subject: [PATCH 32/39] Remove Mathieu's key --- cabal-install/src/Distribution/Client/Config.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index 4e8b241779a..72848232b96 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -931,8 +931,6 @@ defaultHackageRemoteRepoKeys = "0a5c7ea47cd1b15f01f5f51a33adda7e655bc0f0b0615baa8e271f4c3351e21d" , -- Norman Ramsey (ZI8di3a9Un0s2RBrt5GwVRvfOXVuywADfXGPZfkiDb0=) "51f0161b906011b52c6613376b1ae937670da69322113a246a09f807c62f6921" - , -- Mathieu Boespflug (ydN1nGGQ79K1Q0nN+ul+Ln8MxikTB95w0YdGd3v3kmg=) - "be75553f3c7ba1dbe298da81f1d1b05c9d39dd8ed2616c9bddf1525ca8c03e48" , -- Joachim Breitner (5iUgwqZCWrCJktqMx0bBMIuoIyT4A1RYGozzchRN9rA=) "d26e46f3b631aae1433b89379a6c68bd417eb5d1c408f0643dcc07757fece522" , -- Tikhon Jelvis (06nM6r1kOYt49YE5e1+j8VKiiYUFjFQ6HrOpPZu+fDE=) From 3a368806f83280504fe9d716aaa013c9eb46cf0e Mon Sep 17 00:00:00 2001 From: JMCT Date: Wed, 23 Jul 2025 13:20:54 -0400 Subject: [PATCH 33/39] Update Changelog for PR #11095 --- changelog.d/pr-11095.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 changelog.d/pr-11095.md diff --git a/changelog.d/pr-11095.md b/changelog.d/pr-11095.md new file mode 100644 index 00000000000..0f2db8458eb --- /dev/null +++ b/changelog.d/pr-11095.md @@ -0,0 +1,12 @@ +synopsis: Update Hackage root key set +packages: cabal-install +prs: #11095 + +description: { + +One new [Hackage root keyholder](https://github.com/haskell-infra/hackage-root-keys/tree/master/root-keys) was added to the bootstrap set and one was removed. + +- Added Hackage root key for Tikhon Jelvis +- Removed Hackage root key for Mathieu Boespflug + +} From 621c92badb7b21ff172eb209d6f4faa6897c0503 Mon Sep 17 00:00:00 2001 From: brandon s allbery kf8nh Date: Fri, 25 Jul 2025 19:45:25 -0400 Subject: [PATCH 34/39] work around git locking issue on Windows Simply disabling the step causes all non-Windows to fail, so we now skip it only on Windows. --- .github/workflows/validate.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 8ced64a916f..dd02a577687 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -143,6 +143,11 @@ jobs: key: ${{ runner.os }}-${{ matrix.ghc }}-${{ github.sha }} restore-keys: ${{ runner.os }}-${{ matrix.ghc }}- + - name: "Work around git problem https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586 (cabal PR #8546)" + # alternatively, use 'continue-on-error: true' + if: runner.os != 'Windows' + run: git config --global protocol.file.allow always + # The tool is not essential to the rest of the test suite. If # hackage-repo-tool is not present, any test that requires it will # be skipped. From ad989af0e8ab3307e6d942b2f35e7d35d10a41f5 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 27 Jul 2025 14:18:44 -0400 Subject: [PATCH 35/39] Typos in release script comments --- scripts/release/create-release-metadata-for-ghcup.sh | 2 +- scripts/release/download-cabal-install-release-binaries.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/release/create-release-metadata-for-ghcup.sh b/scripts/release/create-release-metadata-for-ghcup.sh index 6cf3415ce75..a74a990b2e6 100755 --- a/scripts/release/create-release-metadata-for-ghcup.sh +++ b/scripts/release/create-release-metadata-for-ghcup.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # This script, when passed the cabal release number as the first and only argument -# generates the metadata in the correct format to be useable as is by GHCup +# generates the metadata in the correct format to be usable as is by GHCup # for eg:- # $ create-release-metadata-for-ghcup.sh 3.10.2.0 or "3.10.2.0" diff --git a/scripts/release/download-cabal-install-release-binaries.sh b/scripts/release/download-cabal-install-release-binaries.sh index 4547fd910e8..f5716ef468e 100755 --- a/scripts/release/download-cabal-install-release-binaries.sh +++ b/scripts/release/download-cabal-install-release-binaries.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash # A script to download the release binary files for a given cabal release -# from upstream "downlods.haskell.org". +# from upstream "downloads.haskell.org". # It accepts the first and only argument as the release number. # -# useage:- +# usage:- # $ download-cabal-install-release-binaries.sh "3.10.1.0" # -# This was initally made to be used with ./create-release-metadata-for-ghcup.sh +# This was initially made to be used with ./create-release-metadata-for-ghcup.sh set -eu set -o pipefail From 598b322b9ea70c56b923bd7dbaa0e953247cf13b Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Mon, 7 Jul 2025 18:07:27 +0800 Subject: [PATCH 36/39] Skip T5634 on Alpine See https://github.com/haskell/cabal/issues/11041 --- .../PackageTests/Backpack/T5634/setup.test.hs | 5 ++++- cabal-testsuite/cabal-testsuite.cabal | 1 + cabal-testsuite/src/Test/Cabal/Prelude.hs | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cabal-testsuite/PackageTests/Backpack/T5634/setup.test.hs b/cabal-testsuite/PackageTests/Backpack/T5634/setup.test.hs index d897955dd74..d12ab76f136 100644 --- a/cabal-testsuite/PackageTests/Backpack/T5634/setup.test.hs +++ b/cabal-testsuite/PackageTests/Backpack/T5634/setup.test.hs @@ -1,5 +1,8 @@ import Test.Cabal.Prelude -main = setupAndCabalTest $ do +main = do + -- TODO: this might be a GHC bug that needs fixing + skipIfAlpine "bug #11041" + setupAndCabalTest $ do skipUnlessGhcVersion ">= 8.1" setup "configure" [] setup "build" [] diff --git a/cabal-testsuite/cabal-testsuite.cabal b/cabal-testsuite/cabal-testsuite.cabal index b59e8dbf8da..50ea756fcdf 100644 --- a/cabal-testsuite/cabal-testsuite.cabal +++ b/cabal-testsuite/cabal-testsuite.cabal @@ -75,6 +75,7 @@ library , network-uri >= 2.6.0.2 && < 2.7 , network-wait ^>= 0.1.2.0 || ^>= 0.2.0.0 , optparse-applicative ^>= 0.14.3.0 || ^>=0.15.1.0 || ^>=0.16.0.0 || ^>= 0.17.0.0 || ^>= 0.18.1.0 + , os-release ^>= 1.0.2.1 , process ^>= 1.2.1.0 || ^>= 1.4.2.0 || ^>= 1.6.1.0 , regex-base ^>= 0.94.0.1 , regex-tdfa ^>= 1.2.3.1 || ^>=1.3.1.0 diff --git a/cabal-testsuite/src/Test/Cabal/Prelude.hs b/cabal-testsuite/src/Test/Cabal/Prelude.hs index 3a12298608c..c85f1205fc7 100644 --- a/cabal-testsuite/src/Test/Cabal/Prelude.hs +++ b/cabal-testsuite/src/Test/Cabal/Prelude.hs @@ -75,6 +75,7 @@ import Control.Retry (exponentialBackoff, limitRetriesByCumulativeDelay) import Network.Wait (waitTcpVerbose) import System.Environment import qualified System.FilePath.Glob as Glob (globDir1, compile) +import qualified System.OsRelease as OSR import System.Process import System.IO import qualified System.FilePath.Posix as Posix @@ -1079,6 +1080,15 @@ isJavaScript = buildArch == JavaScript skipIfWindows :: String -> IO () skipIfWindows why = skipIfIO ("Windows " <> why) isWindows +skipIfAlpine :: String -> IO () +skipIfAlpine why = do + mres <- OSR.parseOsRelease + let b = case mres of + Just (OSR.OsReleaseResult { OSR.osRelease = OSR.OsRelease { OSR.id = osId } }) + | isLinux -> osId == "alpine" + _ -> False + skipIfIO ("Alpine " <> why) b + skipUnlessWindows :: IO () skipUnlessWindows = skipIfIO "Only interesting in Windows" (not isWindows) From 2fb8dd40a96e15dc030e9e92509368e16ad29fcf Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 29 Jul 2025 16:13:57 -0400 Subject: [PATCH 37/39] Fix typo folowing --- cabal-testsuite/cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal-testsuite/cabal.project b/cabal-testsuite/cabal.project index 17b04a2b56f..eb3498bf999 100644 --- a/cabal-testsuite/cabal.project +++ b/cabal-testsuite/cabal.project @@ -1,6 +1,6 @@ -- This intercepting project is here to avoid tests picking up a cabal.project -- from a parent directory, such as the one in the root of the `haskell/cabal` --- project itself. Having `optional-packages: .` avoids the folowing warning +-- project itself. Having `optional-packages: .` avoids the following warning -- being added to the `.out` file: -- Warning: There are no packages or optional-packages in the project optional-packages: . From 7b67d02830a76af4af15c4fdd94b031579ae3612 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 23 Jul 2025 06:06:15 -0400 Subject: [PATCH 38/39] Follow hlint suggestions with single counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Follow hlint suggestion: Use isJust - Follow hlint suggestion: Use isNothing - Follow hlint suggestion: Redundant == - Follow hlint suggestion: Redundant fmap - Follow hlint suggestion: Use minimumBy - Follow hlint suggestion: Use lefts - Follow hlint suggestion: Use fromRight - Follow hlint suggestion: Use for - Follow hlint suggestion: Use forM_ - Follow hlint suggestion: Use when - Follow hlint suggestion: Use uncurry - Follow hlint suggestion: Use traverse - Follow hlint suggestion: Use ?~ - Follow hlint suggestion: Fuse traverse_/map - Follow hlint suggestion: Fuse traverse_/fmap - Follow hlint suggestion: Use replicateM - Follow hlint suggestion: Missing NOINLINE pragma - Rerun hlint --default - Redo hlint counts after rebase - Use Data.Foldable.minimumBy Co-Authored-By: ˌbodʲɪˈɡrʲim --- .hlint.yaml | 39 ++++++------------- .../src/Distribution/Compat/Parsing.hs | 3 +- .../tests/UnitTests/Distribution/Version.hs | 2 +- Cabal/src/Distribution/Simple/BuildPaths.hs | 2 +- Cabal/src/Distribution/Simple/Configure.hs | 33 +++++++--------- Cabal/src/Distribution/Simple/GHCJS.hs | 2 +- Cabal/src/Distribution/Simple/Haddock.hs | 4 +- .../Client/BuildReports/Upload.hs | 5 +-- .../src/Distribution/Client/CmdOutdated.hs | 4 +- .../src/Distribution/Client/CmdRepl.hs | 5 ++- .../Client/ProjectOrchestration.hs | 5 +-- .../Distribution/Client/ProjectPlanOutput.hs | 3 +- .../Distribution/Client/ProjectPlanning.hs | 6 +-- cabal-install/src/Distribution/Client/Run.hs | 2 +- .../src/Distribution/Client/Utils.hs | 1 + .../Deprecated/ViewAsFieldDescr.hs | 4 +- 16 files changed, 49 insertions(+), 71 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index c827093b84f..5e453bac7c9 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -2,67 +2,55 @@ - ignore: {name: "Avoid NonEmpty.unzip"} # 1 hint - ignore: {name: "Avoid lambda"} # 50 hints - ignore: {name: "Avoid lambda using `infix`"} # 23 hints -- ignore: {name: "Eta reduce"} # 132 hints +- ignore: {name: "Eta reduce"} # 136 hints - ignore: {name: "Evaluate"} # 10 hints - ignore: {name: "Functor law"} # 10 hints - ignore: {name: "Fuse concatMap/map"} # 3 hints - ignore: {name: "Fuse foldr/map"} # 3 hints -- ignore: {name: "Fuse traverse_/fmap"} # 1 hint -- ignore: {name: "Fuse traverse_/map"} # 1 hint - ignore: {name: "Hoist not"} # 16 hints -- ignore: {name: "Missing NOINLINE pragma"} # 1 hint - ignore: {name: "Monoid law, left identity"} # 3 hints - ignore: {name: "Monoid law, right identity"} # 3 hints - ignore: {name: "Move filter"} # 4 hints - ignore: {name: "Move guards forward"} # 4 hints -- ignore: {name: "Redundant $"} # 192 hints +- ignore: {name: "Redundant $"} # 202 hints - ignore: {name: "Redundant $!"} # 1 hint -- ignore: {name: "Redundant <$>"} # 17 hints -- ignore: {name: "Redundant =="} # 1 hint -- ignore: {name: "Redundant bracket"} # 260 hints -- ignore: {name: "Redundant fmap"} # 1 hint +- ignore: {name: "Redundant <$>"} # 18 hints +- ignore: {name: "Redundant bracket"} # 274 hints - ignore: {name: "Redundant guard"} # 2 hints - ignore: {name: "Redundant if"} # 6 hints - ignore: {name: "Redundant lambda"} # 19 hints - ignore: {name: "Redundant maybe"} # 2 hints - ignore: {name: "Redundant multi-way if"} # 1 hint -- ignore: {name: "Redundant return"} # 9 hints +- ignore: {name: "Redundant return"} # 10 hints - ignore: {name: "Replace case with fromMaybe"} # 4 hints - ignore: {name: "Replace case with maybe"} # 10 hints - ignore: {name: "Use $>"} # 5 hints - ignore: {name: "Use ++"} # 4 hints - ignore: {name: "Use :"} # 28 hints - ignore: {name: "Use <$"} # 2 hints -- ignore: {name: "Use <$>"} # 86 hints -- ignore: {name: "Use <&>"} # 15 hints +- ignore: {name: "Use <$>"} # 88 hints +- ignore: {name: "Use <&>"} # 16 hints - ignore: {name: "Use <=<"} # 4 hints - ignore: {name: "Use =<<"} # 7 hints - ignore: {name: "Use =="} # 3 hints - ignore: {name: "Use >=>"} # 3 hints -- ignore: {name: "Use ?~"} # 1 hint - ignore: {name: "Use Down"} # 3 hints - ignore: {name: "Use bimap"} # 7 hints -- ignore: {name: "Use camelCase"} # 94 hints -- ignore: {name: "Use catMaybes"} # 3 hints +- ignore: {name: "Use camelCase"} # 96 hints +- ignore: {name: "Use catMaybes"} # 4 hints - ignore: {name: "Use concatMap"} # 2 hints - ignore: {name: "Use const"} # 37 hints - ignore: {name: "Use elem"} # 2 hints - ignore: {name: "Use first"} # 5 hints - ignore: {name: "Use fmap"} # 24 hints - ignore: {name: "Use fold"} # 1 hint -- ignore: {name: "Use for"} # 1 hint -- ignore: {name: "Use forM_"} # 1 hint - ignore: {name: "Use fromMaybe"} # 5 hints -- ignore: {name: "Use fromRight"} # 1 hint - ignore: {name: "Use fst"} # 2 hints - ignore: {name: "Use infix"} # 20 hints - ignore: {name: "Use isAsciiLower"} # 2 hints - ignore: {name: "Use isAsciiUpper"} # 2 hints - ignore: {name: "Use isDigit"} # 2 hints -- ignore: {name: "Use isJust"} # 1 hint -- ignore: {name: "Use isNothing"} # 1 hint - ignore: {name: "Use lambda-case"} # 58 hints -- ignore: {name: "Use lefts"} # 1 hint - ignore: {name: "Use list comprehension"} # 19 hints - ignore: {name: "Use list literal"} # 3 hints - ignore: {name: "Use list literal pattern"} # 11 hints @@ -71,24 +59,19 @@ - ignore: {name: "Use mapMaybe"} # 13 hints - ignore: {name: "Use max"} # 2 hints - ignore: {name: "Use maybe"} # 8 hints -- ignore: {name: "Use minimumBy"} # 1 hint -- ignore: {name: "Use newtype instead of data"} # 29 hints +- ignore: {name: "Use newtype instead of data"} # 31 hints - ignore: {name: "Use notElem"} # 9 hints - ignore: {name: "Use null"} # 2 hints - ignore: {name: "Use record patterns"} # 16 hints -- ignore: {name: "Use replicateM"} # 1 hint - ignore: {name: "Use replicateM_"} # 2 hints - ignore: {name: "Use rights"} # 2 hints - ignore: {name: "Use second"} # 7 hints - ignore: {name: "Use section"} # 18 hints -- ignore: {name: "Use traverse"} # 1 hint -- ignore: {name: "Use tuple-section"} # 27 hints +- ignore: {name: "Use tuple-section"} # 28 hints - ignore: {name: "Use typeRep"} # 2 hints -- ignore: {name: "Use uncurry"} # 1 hint - ignore: {name: "Use unless"} # 23 hints - ignore: {name: "Use unwords"} # 8 hints - ignore: {name: "Use void"} # 23 hints -- ignore: {name: "Use when"} # 1 hint - arguments: - --ignore-glob=Cabal-syntax/src/Distribution/Fields/Lexer.hs diff --git a/Cabal-syntax/src/Distribution/Compat/Parsing.hs b/Cabal-syntax/src/Distribution/Compat/Parsing.hs index 4cb8c9bca18..b08dd7b0303 100644 --- a/Cabal-syntax/src/Distribution/Compat/Parsing.hs +++ b/Cabal-syntax/src/Distribution/Compat/Parsing.hs @@ -58,6 +58,7 @@ import Control.Monad.Trans.Writer.Lazy as Lazy import Control.Monad.Trans.Writer.Strict as Strict import Data.Foldable (asum) +import Control.Monad (replicateM) import qualified Data.List.NonEmpty as NE import qualified Text.Parsec as Parsec @@ -141,7 +142,7 @@ endBy p sep = many (p <* sep) count :: Applicative m => Int -> m a -> m [a] count n p | n <= 0 = pure [] - | otherwise = sequenceA (replicate n p) + | otherwise = replicateM n p {-# INLINE count #-} -- | @chainr p op x@ parses /zero/ or more occurrences of @p@, diff --git a/Cabal-tests/tests/UnitTests/Distribution/Version.hs b/Cabal-tests/tests/UnitTests/Distribution/Version.hs index 48c281cf079..2a6410f1087 100644 --- a/Cabal-tests/tests/UnitTests/Distribution/Version.hs +++ b/Cabal-tests/tests/UnitTests/Distribution/Version.hs @@ -195,7 +195,7 @@ prop_anyVersion v' = prop_noVersion :: Version -> Bool prop_noVersion v' = - withinRange v' noVersion == False + not (withinRange v' noVersion) prop_thisVersion :: Version -> Version -> Bool prop_thisVersion v v' = diff --git a/Cabal/src/Distribution/Simple/BuildPaths.hs b/Cabal/src/Distribution/Simple/BuildPaths.hs index 0875de87c91..076d468bf20 100644 --- a/Cabal/src/Distribution/Simple/BuildPaths.hs +++ b/Cabal/src/Distribution/Simple/BuildPaths.hs @@ -320,7 +320,7 @@ getSourceFiles -> [SymbolicPathX allowAbsolute Pkg (Dir Source)] -> [ModuleName.ModuleName] -> IO [(ModuleName.ModuleName, SymbolicPathX allowAbsolute Pkg File)] -getSourceFiles verbosity mbWorkDir dirs modules = flip traverse modules $ \m -> +getSourceFiles verbosity mbWorkDir dirs modules = for modules $ \m -> fmap ((,) m) $ findFileCwdWithExtension mbWorkDir diff --git a/Cabal/src/Distribution/Simple/Configure.hs b/Cabal/src/Distribution/Simple/Configure.hs index 4b29f27b159..2ecd6516940 100644 --- a/Cabal/src/Distribution/Simple/Configure.hs +++ b/Cabal/src/Distribution/Simple/Configure.hs @@ -883,7 +883,7 @@ configurePackage cfg lbc0 pkg_descr00 flags enabled comp platform programDb0 pac let unknownBuildTools = [ buildTool | buildTool <- buildTools bi - , Nothing == desugarBuildTool pkg_descr0 buildTool + , isNothing (desugarBuildTool pkg_descr0 buildTool) ] externBuildToolDeps ++ unknownBuildTools @@ -2766,7 +2766,7 @@ checkPackageProblems verbosity dir gpkg pkg = do (errors, warnings) = partitionEithers (M.mapMaybe classEW $ pureChecks ++ ioChecks) if null errors - then traverse_ (warn verbosity) (map ppPackageCheck warnings) + then traverse_ (warn verbosity . ppPackageCheck) warnings else dieWithException verbosity $ CheckPackageProblems (map ppPackageCheck errors) where -- Classify error/warnings. Left: error, Right: warning. @@ -2836,22 +2836,19 @@ checkRelocatable verbosity pkg lbi = p = prefix installDirs relativeInstallDirs (InstallDirs{..}) = all - isJust - ( fmap - (stripPrefix p) - [ bindir - , libdir - , dynlibdir - , libexecdir - , includedir - , datadir - , docdir - , mandir - , htmldir - , haddockdir - , sysconfdir - ] - ) + (isJust . stripPrefix p) + [ bindir + , libdir + , dynlibdir + , libexecdir + , includedir + , datadir + , docdir + , mandir + , htmldir + , haddockdir + , sysconfdir + ] -- Check if the library dirs of the dependencies that are in the package -- database to which the package is installed are relative to the diff --git a/Cabal/src/Distribution/Simple/GHCJS.hs b/Cabal/src/Distribution/Simple/GHCJS.hs index 56a4b120b63..a73d47f7a6e 100644 --- a/Cabal/src/Distribution/Simple/GHCJS.hs +++ b/Cabal/src/Distribution/Simple/GHCJS.hs @@ -537,7 +537,7 @@ buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do whenStaticLib forceStatic = when (forceStatic || withStaticLib lbi) -- whenGHCiLib = when (withGHCiLib lbi) - forRepl = maybe False (const True) mReplFlags + forRepl = isJust mReplFlags -- ifReplLib = when forRepl comp = compiler lbi implInfo = getImplInfo comp diff --git a/Cabal/src/Distribution/Simple/Haddock.hs b/Cabal/src/Distribution/Simple/Haddock.hs index 9d6c20dd4d7..b24ee8da5af 100644 --- a/Cabal/src/Distribution/Simple/Haddock.hs +++ b/Cabal/src/Distribution/Simple/Haddock.hs @@ -89,7 +89,7 @@ import Distribution.Version import Control.Monad import Data.Bool (bool) -import Data.Either (rights) +import Data.Either (lefts, rights) import System.Directory (doesDirectoryExist, doesFileExist) import System.FilePath (isAbsolute, normalise) import System.IO (hClose, hPutStrLn, hSetEncoding, utf8) @@ -1386,7 +1386,7 @@ haddockPackagePaths ipkgs mkHtmlPath = do , pkgName pkgid `notElem` noHaddockWhitelist ] - let missing = [pkgid | Left pkgid <- interfaces] + let missing = lefts interfaces warning = "The following packages have no Haddock documentation " ++ "installed. No links will be generated to these packages: " diff --git a/cabal-install/src/Distribution/Client/BuildReports/Upload.hs b/cabal-install/src/Distribution/Client/BuildReports/Upload.hs index 061df748365..36032d0849e 100644 --- a/cabal-install/src/Distribution/Client/BuildReports/Upload.hs +++ b/cabal-install/src/Distribution/Client/BuildReports/Upload.hs @@ -21,6 +21,7 @@ import Network.TCP (HandleStream) -} import Network.URI (URI, uriPath) -- parseRelativeReference, relativeTo) +import Data.Foldable (forM_) import Distribution.Client.BuildReports.Anonymous (BuildReport, showBuildReport) import qualified Distribution.Client.BuildReports.Anonymous as BuildReport import Distribution.Client.Errors @@ -41,9 +42,7 @@ uploadReports :: Verbosity -> RepoContext -> Auth -> URI -> [(BuildReport, Maybe uploadReports verbosity repoCtxt auth uri reports = do for_ reports $ \(report, mbBuildLog) -> do buildId <- postBuildReport verbosity repoCtxt auth uri report - case mbBuildLog of - Just buildLog -> putBuildLog verbosity repoCtxt auth buildId buildLog - Nothing -> return () + forM_ mbBuildLog (putBuildLog verbosity repoCtxt auth buildId) postBuildReport :: Verbosity -> RepoContext -> Auth -> URI -> BuildReport -> IO BuildReportId postBuildReport verbosity repoCtxt auth uri buildReport = do diff --git a/cabal-install/src/Distribution/Client/CmdOutdated.hs b/cabal-install/src/Distribution/Client/CmdOutdated.hs index 56b5017fa85..7b6c61e32b9 100644 --- a/cabal-install/src/Distribution/Client/CmdOutdated.hs +++ b/cabal-install/src/Distribution/Client/CmdOutdated.hs @@ -266,9 +266,7 @@ outdatedAction flags targetStrings globalFlags = (ListOutdatedSettings ignorePred minorPred) when (not quiet) $ showResult verbosity outdatedDeps simpleOutput - if exitCode && (not . null $ outdatedDeps) - then exitFailure - else return () + when (exitCode && (not . null $ outdatedDeps)) exitFailure where OutdatedFlags{..} = extraFlags flags verbosity = diff --git a/cabal-install/src/Distribution/Client/CmdRepl.hs b/cabal-install/src/Distribution/Client/CmdRepl.hs index 7fb3f700d09..8e4b814d67e 100644 --- a/cabal-install/src/Distribution/Client/CmdRepl.hs +++ b/cabal-install/src/Distribution/Client/CmdRepl.hs @@ -297,8 +297,9 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g let sourcePackage = fakeProjectSourcePackage projectRoot - & lSrcpkgDescription . L.condLibrary - .~ Just (CondNode library [baseDep] []) + & ( (lSrcpkgDescription . L.condLibrary) + ?~ (CondNode library [baseDep] []) + ) library = emptyLibrary{libBuildInfo = lBuildInfo} lBuildInfo = emptyBuildInfo diff --git a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs index 9b12f570252..d0dfb10601e 100644 --- a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs +++ b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs @@ -296,10 +296,7 @@ establishProjectBaseContextWithRoot verbosity cliConfig projectRoot currentComma } = projectConfigShared projectConfig mlogsDir = Setup.flagToMaybe projectConfigLogsDir - mstoreDir <- - sequenceA $ - makeAbsolute - <$> Setup.flagToMaybe projectConfigStoreDir + mstoreDir <- traverse makeAbsolute (Setup.flagToMaybe projectConfigStoreDir) cabalDirLayout <- mkCabalDirLayout mstoreDir mlogsDir diff --git a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs index 3654e9c05fd..a4ce230d984 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs @@ -68,6 +68,7 @@ import Prelude () import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as BS +import Data.Either (fromRight) import qualified Data.Map as Map import qualified Data.Set as Set @@ -768,7 +769,7 @@ readPackagesUpToDateCacheFile DistDirLayout{distProjectCacheFile} = withBinaryFile (distProjectCacheFile "up-to-date") ReadMode $ \hnd -> Binary.decodeOrFailIO =<< BS.hGetContents hnd where - handleDecodeFailure = fmap (either (const Set.empty) id) + handleDecodeFailure = fmap (fromRight Set.empty) -- | Helper for writing the package up-to-date cache file. -- diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index d30a553a4bb..968b915ee2a 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -1191,9 +1191,9 @@ getPackageSourceHashes verbosity withRepoCtx solverPlan = do (repoTarballPkgsWithMetadata, repoTarballPkgsToDownloadWithMeta) <- fmap partitionEithers $ liftIO $ - withRepoCtx $ \repoctx -> flip concatMapM (Map.toList repoTarballPkgsWithMetadataUnvalidatedMap) $ - \(repo, pkgids) -> - verifyFetchedTarballs verbosity repoctx repo pkgids + withRepoCtx $ \repoctx -> + flip concatMapM (Map.toList repoTarballPkgsWithMetadataUnvalidatedMap) $ + uncurry (verifyFetchedTarballs verbosity repoctx) -- For tarballs from repos that do not have hashes available we now have -- to check if the packages were downloaded already. diff --git a/cabal-install/src/Distribution/Client/Run.hs b/cabal-install/src/Distribution/Client/Run.hs index 1dd9db32c99..baa6264abe4 100644 --- a/cabal-install/src/Distribution/Client/Run.hs +++ b/cabal-install/src/Distribution/Client/Run.hs @@ -80,7 +80,7 @@ splitRunArgs verbosity lbi args = " Interpreting all parameters to `run` as a parameter to" ++ " the default executable." -- If there is a warning, print it together with the addition. - warn verbosity `traverse_` fmap (++ addition) maybeWarning + traverse_ (warn verbosity . (++ addition)) maybeWarning return (exe, xs) where pkg_descr = localPkgDescr lbi diff --git a/cabal-install/src/Distribution/Client/Utils.hs b/cabal-install/src/Distribution/Client/Utils.hs index f4a1fb67a51..07db71a9f9d 100644 --- a/cabal-install/src/Distribution/Client/Utils.hs +++ b/cabal-install/src/Distribution/Client/Utils.hs @@ -239,6 +239,7 @@ logDirChange l (Just d) m = do -- program, so unsafePerformIO is safe here. numberOfProcessors :: Int numberOfProcessors = unsafePerformIO getNumProcessors +{-# NOINLINE numberOfProcessors #-} -- | Determine the number of jobs to use given the value of the '-j' flag. determineNumJobs :: Flag (Maybe Int) -> Int diff --git a/cabal-install/src/Distribution/Deprecated/ViewAsFieldDescr.hs b/cabal-install/src/Distribution/Deprecated/ViewAsFieldDescr.hs index 35c2564e531..fb7ea8f91f6 100644 --- a/cabal-install/src/Distribution/Deprecated/ViewAsFieldDescr.hs +++ b/cabal-install/src/Distribution/Deprecated/ViewAsFieldDescr.hs @@ -5,7 +5,7 @@ module Distribution.Deprecated.ViewAsFieldDescr import Distribution.Client.Compat.Prelude hiding (get) import Prelude () -import qualified Data.List.NonEmpty as NE +import Data.Foldable (minimumBy) import Distribution.ReadE (parsecToReadE) import Distribution.Simple.Command import Text.PrettyPrint (cat, comma, punctuate, text) @@ -20,7 +20,7 @@ viewAsFieldDescr (OptionField _n []) = error "Distribution.command.viewAsFieldDescr: unexpected" viewAsFieldDescr (OptionField n (d : dd)) = FieldDescr n get set where - optDescr = head $ NE.sortBy cmp (d :| dd) + optDescr = minimumBy cmp (d :| dd) cmp :: OptDescr a -> OptDescr a -> Ordering ReqArg{} `cmp` ReqArg{} = EQ From ad38164fbc174a5f3afd87b6823e95aaa4e608c0 Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Tue, 5 Aug 2025 14:22:20 -0400 Subject: [PATCH 39/39] Bump time upper bound To allow time-1.15. --- Cabal-syntax/Cabal-syntax.cabal | 2 +- Cabal/Cabal.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cabal-syntax/Cabal-syntax.cabal b/Cabal-syntax/Cabal-syntax.cabal index 1d651252b45..85137dc147c 100644 --- a/Cabal-syntax/Cabal-syntax.cabal +++ b/Cabal-syntax/Cabal-syntax.cabal @@ -40,7 +40,7 @@ library , parsec >= 3.1.13.0 && < 3.2 , pretty >= 1.1.1 && < 1.2 , text (>= 1.2.3.0 && < 1.3) || (>= 2.0 && < 2.2) - , time >= 1.4.0.1 && < 1.15 + , time >= 1.4.0.1 && < 1.16 -- transformers-0.4.0.0 doesn't have record syntax e.g. for Identity -- See also https://github.com/ekmett/transformers-compat/issues/35 , transformers (>= 0.3 && < 0.4) || (>=0.4.1.0 && <0.7) diff --git a/Cabal/Cabal.cabal b/Cabal/Cabal.cabal index e62c6a5505d..8ec6542bd47 100644 --- a/Cabal/Cabal.cabal +++ b/Cabal/Cabal.cabal @@ -49,7 +49,7 @@ library , filepath >= 1.3.0.1 && < 1.6 , pretty >= 1.1.1 && < 1.2 , process >= 1.2.1.0 && < 1.7 - , time >= 1.4.0.1 && < 1.15 + , time >= 1.4.0.1 && < 1.16 if os(windows) build-depends: