From 6ae908d9fa1d0e7fba3a151e64584cd737012518 Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Thu, 20 Mar 2025 17:00:49 +0000 Subject: [PATCH 1/2] Implement v2-gen-bounds function This commit implements project-aware functionality for the `cabal gen-bounds` command, allowing it to work correctly in multi-package projects. Previously, running `gen-bounds` from within a package directory that depends on another local package would fail because it couldn't find the local dependency. The implementation follows the same pattern as other v2 commands, creating a full project context that knows about all packages defined in the cabal.project file. This allows `gen-bounds` to properly analyze dependencies between local packages and suggest appropriate bounds. ``` cabal gen-bounds ``` Fixes #7504 #8654 #9752 #5932 --- cabal-install/cabal-install.cabal | 1 + .../src/Distribution/Client/CmdGenBounds.hs | 256 ++++++++++++++++++ .../src/Distribution/Client/Errors.hs | 4 + .../src/Distribution/Client/GenBounds.hs | 2 + cabal-install/src/Distribution/Client/Main.hs | 4 +- .../GenBounds/Issue6290/cabal.out | 2 +- .../GenBounds/Issue7504/cabal.project | 2 + .../GenBounds/Issue7504/cabal.test.hs | 11 + .../GenBounds/Issue7504/package-a/LICENSE | 28 ++ .../Issue7504/package-a/package-a.cabal | 15 + .../Issue7504/package-a/src/ModuleA.hs | 5 + .../GenBounds/Issue7504/package-b/LICENSE | 28 ++ .../GenBounds/Issue7504/package-b/exe/Main.hs | 6 + .../Issue7504/package-b/package-b.cabal | 24 ++ .../Issue7504/package-b/src/ModuleB.hs | 7 + changelog.d/pr-10840.md | 28 ++ doc/cabal-commands.rst | 72 +++-- 17 files changed, 475 insertions(+), 20 deletions(-) create mode 100644 cabal-install/src/Distribution/Client/CmdGenBounds.hs create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.project create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/LICENSE create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/package-a.cabal create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/src/ModuleA.hs create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/LICENSE create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/exe/Main.hs create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/package-b.cabal create mode 100644 cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/src/ModuleB.hs create mode 100644 changelog.d/pr-10840.md diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal index 5aedb23aeed..19d71168367 100644 --- a/cabal-install/cabal-install.cabal +++ b/cabal-install/cabal-install.cabal @@ -118,6 +118,7 @@ library Distribution.Client.CmdTarget Distribution.Client.CmdTest Distribution.Client.CmdUpdate + Distribution.Client.CmdGenBounds Distribution.Client.Compat.Directory Distribution.Client.Compat.ExecutablePath Distribution.Client.Compat.Orphans diff --git a/cabal-install/src/Distribution/Client/CmdGenBounds.hs b/cabal-install/src/Distribution/Client/CmdGenBounds.hs new file mode 100644 index 00000000000..ee15a95bfee --- /dev/null +++ b/cabal-install/src/Distribution/Client/CmdGenBounds.hs @@ -0,0 +1,256 @@ +{-# LANGUAGE NamedFieldPuns #-} + +module Distribution.Client.CmdGenBounds + ( genBounds + , genBoundsCommand + , genBoundsAction + , GenBoundsFlags (..) + , defaultGenBoundsFlags + ) where + +import Distribution.Client.Compat.Prelude +import Prelude () + +import qualified Data.Map as Map + +import Control.Monad (mapM_) + +import Distribution.Client.Errors + +import Distribution.Client.ProjectPlanning hiding (pruneInstallPlanToTargets) +import Distribution.Client.ProjectPlanning.Types +import Distribution.Client.Types.ConfiguredId (confInstId) +import Distribution.Client.Utils hiding (pvpize) +import Distribution.InstalledPackageInfo (InstalledPackageInfo, installedComponentId) +import Distribution.Package +import Distribution.PackageDescription +import Distribution.Simple.Utils +import Distribution.Version + +import Distribution.Client.Setup (CommonSetupFlags (..), ConfigFlags (..), GlobalFlags (..)) + +-- Project orchestration imports + +import Distribution.Client.CmdErrorMessages +import Distribution.Client.GenBounds +import qualified Distribution.Client.InstallPlan as InstallPlan +import Distribution.Client.NixStyleOptions +import Distribution.Client.ProjectFlags +import Distribution.Client.ProjectOrchestration +import Distribution.Client.ScriptUtils +import Distribution.Client.TargetProblem +import Distribution.Simple.Command +import Distribution.Simple.Flag +import Distribution.Types.Component +import Distribution.Verbosity + +-- | The data type for gen-bounds command flags +data GenBoundsFlags = GenBoundsFlags {} + +-- | Default values for the gen-bounds flags +defaultGenBoundsFlags :: GenBoundsFlags +defaultGenBoundsFlags = GenBoundsFlags{} + +-- | The @gen-bounds@ command definition +genBoundsCommand :: CommandUI (NixStyleFlags GenBoundsFlags) +genBoundsCommand = + CommandUI + { commandName = "v2-gen-bounds" + , commandSynopsis = "Generate dependency bounds for packages in the project." + , commandUsage = usageAlternatives "v2-gen-bounds" ["[TARGETS] [FLAGS]"] + , commandDescription = Just $ \_ -> + "Generate PVP-compliant dependency bounds for packages in the project." + , commandNotes = Just $ \pname -> + "Examples:\n" + ++ " " + ++ pname + ++ " v2-gen-bounds\n" + ++ " Generate bounds for the package in the current directory " + ++ "or all packages in the project\n" + ++ " " + ++ pname + ++ " v2-gen-bounds pkgname\n" + ++ " Generate bounds for the package named pkgname in the project\n" + ++ " " + ++ pname + ++ " v2-gen-bounds ./pkgfoo\n" + ++ " Generate bounds for the package in the ./pkgfoo directory\n" + , commandDefaultFlags = defaultNixStyleFlags defaultGenBoundsFlags + , commandOptions = + removeIgnoreProjectOption + . nixStyleOptions (const []) + } + +-- | The action for the @gen-bounds@ command when used in a project context. +genBoundsAction :: NixStyleFlags GenBoundsFlags -> [String] -> GlobalFlags -> IO () +genBoundsAction flags targetStrings globalFlags = + withContextAndSelectors RejectNoTargets Nothing flags targetStrings globalFlags OtherCommand $ \targetCtx ctx targetSelectors -> do + let verbosity = fromFlagOrDefault normal (setupVerbosity $ configCommonFlags $ configFlags flags) + + baseCtx <- case targetCtx of + ProjectContext -> return ctx + GlobalContext -> return ctx + ScriptContext path _ -> + dieWithException verbosity $ + GenBoundsDoesNotSupportScript path + + let ProjectBaseContext{distDirLayout, cabalDirLayout, projectConfig, localPackages} = baseCtx + + -- Step 1: Create the install plan for the project. + (_, elaboratedPlan, _, _, _) <- + rebuildInstallPlan + verbosity + distDirLayout + cabalDirLayout + projectConfig + localPackages + Nothing + + -- Step 2: Resolve the targets for the gen-bounds command. + targets <- + either (reportGenBoundsTargetProblems verbosity) return $ + resolveTargets + selectPackageTargets + selectComponentTarget + elaboratedPlan + Nothing + targetSelectors + + -- Step 3: Prune the install plan to the targets. + let elaboratedPlan' = + pruneInstallPlanToTargets + TargetActionBuild + targets + elaboratedPlan + + let + -- Step 4a: Find the local packages from the install plan. These are the + -- candidates for which we will generate bounds. + localPkgs :: [ElaboratedConfiguredPackage] + localPkgs = mapMaybe (InstallPlan.foldPlanPackage (const Nothing) (\p -> Just p)) (InstallPlan.toList elaboratedPlan') + + -- Step 4b: Extract which versions we chose for each package from the pruned install plan. + pkgVersionMap :: Map.Map ComponentId PackageIdentifier + pkgVersionMap = Map.fromList (map (InstallPlan.foldPlanPackage externalVersion localVersion) (InstallPlan.toList elaboratedPlan')) + + externalVersion :: InstalledPackageInfo -> (ComponentId, PackageIdentifier) + externalVersion pkg = (installedComponentId pkg, packageId pkg) + + localVersion :: ElaboratedConfiguredPackage -> (ComponentId, PackageIdentifier) + localVersion pkg = (elabComponentId pkg, packageId pkg) + + let genBoundsActionForPkg :: ElaboratedConfiguredPackage -> [GenBoundsResult] + genBoundsActionForPkg pkg = + -- Step 5: Match up the user specified targets with the local packages. + case Map.lookup (installedUnitId pkg) targets of + Nothing -> [] + Just tgts -> + map (\(tgt, _) -> getBoundsForComponent tgt pkg pkgVersionMap) tgts + + -- Process each package to find the ones needing bounds + let boundsActions = concatMap genBoundsActionForPkg localPkgs + + if (any isBoundsNeeded boundsActions) + then do + notice verbosity boundsNeededMsg + mapM_ (renderBoundsResult verbosity) boundsActions + else notice verbosity "All bounds up-to-date" + +data GenBoundsResult = GenBoundsResult PackageIdentifier ComponentTarget (Maybe [PackageIdentifier]) + +isBoundsNeeded :: GenBoundsResult -> Bool +isBoundsNeeded (GenBoundsResult _ _ Nothing) = False +isBoundsNeeded _ = True + +renderBoundsResult :: Verbosity -> GenBoundsResult -> IO () +renderBoundsResult verbosity (GenBoundsResult pid tgt bounds) = + case bounds of + Nothing -> + notice + verbosity + ("Congratulations, all dependencies for " ++ prettyShow (packageName pid) ++ ":" ++ showComponentTarget pid tgt ++ " have upper bounds!") + Just pkgBounds -> do + notice verbosity $ + "For component " ++ prettyShow (pkgName pid) ++ ":" ++ showComponentTarget pid tgt ++ ":" + let padTo = maximum $ map (length . unPackageName . packageName) pkgBounds + traverse_ (notice verbosity . (++ ",") . showBounds padTo) pkgBounds + +-- | Process a single BuildInfo to identify and report missing upper bounds +getBoundsForComponent + :: ComponentTarget + -> ElaboratedConfiguredPackage + -> Map.Map ComponentId PackageIdentifier + -> GenBoundsResult +getBoundsForComponent tgt pkg pkgVersionMap = + if null needBounds + then boundsResult Nothing + else -- All the things we depend on. + + let componentDeps = elabLibDependencies pkg + -- Match these up to package names, this is a list of Package name to versions. + -- Now just match that up with what the user wrote in the build-depends section. + depsWithVersions = mapMaybe (\cid -> Map.lookup (confInstId $ fst cid) pkgVersionMap) componentDeps + isNeeded = hasElem needBounds . packageName + in boundsResult (Just (filter isNeeded depsWithVersions)) + where + pd = elabPkgDescription pkg + -- Extract the build-depends for the right part of the cabal file. + bi = buildInfoForTarget pd tgt + + -- We need to generate bounds if + -- \* the dependency does not have an upper bound + -- \* the dependency is not the same package as the one we are processing + boundFilter dep = + (not (hasUpperBound (depVerRange dep))) + && packageName pd /= depPkgName dep + + -- The dependencies that need bounds. + needBounds = map depPkgName $ filter boundFilter $ targetBuildDepends bi + + boundsResult = GenBoundsResult (packageId pkg) tgt + +buildInfoForTarget :: PackageDescription -> ComponentTarget -> BuildInfo +buildInfoForTarget pd (ComponentTarget cname _) = componentBuildInfo $ getComponent pd cname + +-- | This defines what a 'TargetSelector' means for the @gen-bounds@ command. +-- Copy of selectPackageTargets from CmdBuild.hs +selectPackageTargets + :: TargetSelector + -> [AvailableTarget k] + -> Either TargetProblem' [k] +selectPackageTargets targetSelector targets + -- If there are any buildable targets then we select those + | not (null targetsBuildable) = + Right targetsBuildable + -- If there are targets but none are buildable then we report those + | not (null targets) = + Left (TargetProblemNoneEnabled targetSelector targets') + -- If there are no targets at all then we report that + | otherwise = + Left (TargetProblemNoTargets targetSelector) + where + targets' = forgetTargetsDetail targets + targetsBuildable = + selectBuildableTargetsWith + (buildable targetSelector) + targets + + -- When there's a target filter like "pkg:tests" then we do select tests, + -- but if it's just a target like "pkg" then we don't build tests unless + -- they are requested by default (i.e. by using --enable-tests) + buildable (TargetPackage _ _ Nothing) TargetNotRequestedByDefault = False + buildable (TargetAllPackages Nothing) TargetNotRequestedByDefault = False + buildable _ _ = True + +-- | For a 'TargetComponent' 'TargetSelector', check if the component can be +-- selected. Copy of selectComponentTarget from CmdBuild.hs +selectComponentTarget + :: SubComponentTarget + -> AvailableTarget k + -> Either TargetProblem' k +selectComponentTarget = selectComponentTargetBasic + +-- | Report target problems for gen-bounds command +reportGenBoundsTargetProblems :: Verbosity -> [TargetProblem'] -> IO a +reportGenBoundsTargetProblems verbosity problems = + reportTargetProblems verbosity "gen-bounds" problems diff --git a/cabal-install/src/Distribution/Client/Errors.hs b/cabal-install/src/Distribution/Client/Errors.hs index ff9ad369bef..06f965fd972 100644 --- a/cabal-install/src/Distribution/Client/Errors.hs +++ b/cabal-install/src/Distribution/Client/Errors.hs @@ -186,6 +186,7 @@ data CabalInstallException | MissingPackageList Repo.RemoteRepo | CmdPathAcceptsNoTargets | CmdPathCommandDoesn'tSupportDryRun + | GenBoundsDoesNotSupportScript FilePath deriving (Show) exceptionCodeCabalInstall :: CabalInstallException -> Int @@ -338,6 +339,7 @@ exceptionCodeCabalInstall e = case e of MissingPackageList{} -> 7160 CmdPathAcceptsNoTargets{} -> 7161 CmdPathCommandDoesn'tSupportDryRun -> 7163 + GenBoundsDoesNotSupportScript{} -> 7164 exceptionMessageCabalInstall :: CabalInstallException -> String exceptionMessageCabalInstall e = case e of @@ -860,6 +862,8 @@ exceptionMessageCabalInstall e = case e of "The 'path' command accepts no target arguments." CmdPathCommandDoesn'tSupportDryRun -> "The 'path' command doesn't support the flag '--dry-run'." + GenBoundsDoesNotSupportScript{} -> + "The 'gen-bounds' command does not support script targets." instance Exception (VerboseException CabalInstallException) where displayException :: VerboseException CabalInstallException -> [Char] diff --git a/cabal-install/src/Distribution/Client/GenBounds.hs b/cabal-install/src/Distribution/Client/GenBounds.hs index 1139bf69aed..da8d06c70dc 100644 --- a/cabal-install/src/Distribution/Client/GenBounds.hs +++ b/cabal-install/src/Distribution/Client/GenBounds.hs @@ -10,6 +10,8 @@ -- The cabal gen-bounds command for generating PVP-compliant version bounds. module Distribution.Client.GenBounds ( genBounds + , boundsNeededMsg + , showBounds ) where import Distribution.Client.Compat.Prelude diff --git a/cabal-install/src/Distribution/Client/Main.hs b/cabal-install/src/Distribution/Client/Main.hs index 39caec854cd..1e4a24692e4 100644 --- a/cabal-install/src/Distribution/Client/Main.hs +++ b/cabal-install/src/Distribution/Client/Main.hs @@ -120,6 +120,7 @@ import qualified Distribution.Client.CmdClean as CmdClean import qualified Distribution.Client.CmdConfigure as CmdConfigure import qualified Distribution.Client.CmdExec as CmdExec import qualified Distribution.Client.CmdFreeze as CmdFreeze +import qualified Distribution.Client.CmdGenBounds as CmdGenBounds import qualified Distribution.Client.CmdHaddock as CmdHaddock import qualified Distribution.Client.CmdHaddockProject as CmdHaddockProject import qualified Distribution.Client.CmdInstall as CmdInstall @@ -436,7 +437,6 @@ mainWorker args = do , regularCmd initCommand initAction , regularCmd userConfigCommand userConfigAction , regularCmd CmdPath.pathCommand CmdPath.pathAction - , regularCmd genBoundsCommand genBoundsAction , regularCmd CmdOutdated.outdatedCommand CmdOutdated.outdatedAction , wrapperCmd hscolourCommand hscolourCommonFlags , hiddenCmd formatCommand formatAction @@ -462,7 +462,9 @@ mainWorker args = do , newCmd CmdClean.cleanCommand CmdClean.cleanAction , newCmd CmdSdist.sdistCommand CmdSdist.sdistAction , newCmd CmdTarget.targetCommand CmdTarget.targetAction + , newCmd CmdGenBounds.genBoundsCommand CmdGenBounds.genBoundsAction , legacyCmd configureExCommand configureAction + , legacyCmd genBoundsCommand genBoundsAction , legacyCmd buildCommand buildAction , legacyCmd replCommand replAction , legacyCmd freezeCommand freezeAction diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue6290/cabal.out b/cabal-testsuite/PackageTests/GenBounds/Issue6290/cabal.out index 08a8512a6df..009df997267 100644 --- a/cabal-testsuite/PackageTests/GenBounds/Issue6290/cabal.out +++ b/cabal-testsuite/PackageTests/GenBounds/Issue6290/cabal.out @@ -1,3 +1,3 @@ # cabal gen-bounds Resolving dependencies... -Congratulations, all your dependencies have upper bounds! +All bounds up-to-date diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.project b/cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.project new file mode 100644 index 00000000000..8ed8df66ab7 --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.project @@ -0,0 +1,2 @@ +packages: package-a + package-b diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.test.hs b/cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.test.hs new file mode 100644 index 00000000000..f2934953cfe --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/cabal.test.hs @@ -0,0 +1,11 @@ +import System.Directory (setCurrentDirectory) +import Test.Cabal.Prelude + +main = cabalTest $ recordMode DoNotRecord $ do + r <- cabal' "gen-bounds" ["all"] + assertOutputContains "For component package-a:lib:package-a:" r + assertOutputContains "For component package-b:lib:package-b:" r + assertOutputContains "For component package-b:exe:package-b:" r + assertOutputContains "text >=" r + assertOutputContains "package-a >= 0.1.0 && < 0.2" r + diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/LICENSE b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/LICENSE new file mode 100644 index 00000000000..00dedf4caaa --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2023, Cabal Team + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Cabal Team nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/package-a.cabal b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/package-a.cabal new file mode 100644 index 00000000000..c1397374da1 --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/package-a.cabal @@ -0,0 +1,15 @@ +cabal-version: 2.2 +name: package-a +version: 0.1.0.0 +synopsis: A simple package for testing gen-bounds +license: BSD-3-Clause +license-file: LICENSE +author: Cabal Team +maintainer: cabal-dev@haskell.org +build-type: Simple + +library + default-language: Haskell2010 + hs-source-dirs: src + exposed-modules: ModuleA + build-depends: base >= 4.8 && < 5, text diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/src/ModuleA.hs b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/src/ModuleA.hs new file mode 100644 index 00000000000..1113126f402 --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-a/src/ModuleA.hs @@ -0,0 +1,5 @@ +module ModuleA (getMessage) where + +-- | Return a simple greeting message +getMessage :: String +getMessage = "Hello from package-a!" diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/LICENSE b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/LICENSE new file mode 100644 index 00000000000..00dedf4caaa --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2023, Cabal Team + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Cabal Team nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/exe/Main.hs b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/exe/Main.hs new file mode 100644 index 00000000000..d6a4ff7d19a --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/exe/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import ModuleB (getEnhancedMessage) + +main :: IO () +main = putStrLn getEnhancedMessage diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/package-b.cabal b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/package-b.cabal new file mode 100644 index 00000000000..dd30f82d872 --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/package-b.cabal @@ -0,0 +1,24 @@ +cabal-version: 2.2 +name: package-b +version: 0.1.0.0 +synopsis: A package that depends on package-a for testing gen-bounds +license: BSD-3-Clause +license-file: LICENSE +author: Cabal Team +maintainer: cabal-dev@haskell.org +build-type: Simple + +library + default-language: Haskell2010 + hs-source-dirs: src + exposed-modules: ModuleB + build-depends: base, + package-a + +executable package-b + default-language: Haskell2010 + hs-source-dirs: exe + main-is: Main.hs + build-depends: base, + package-a, + package-b diff --git a/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/src/ModuleB.hs b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/src/ModuleB.hs new file mode 100644 index 00000000000..5ba308d35b0 --- /dev/null +++ b/cabal-testsuite/PackageTests/GenBounds/Issue7504/package-b/src/ModuleB.hs @@ -0,0 +1,7 @@ +module ModuleB (getEnhancedMessage) where + +import ModuleA (getMessage) + +-- | Return an enhanced message that uses ModuleA's functionality +getEnhancedMessage :: String +getEnhancedMessage = getMessage ++ " Enhanced by package-b!" diff --git a/changelog.d/pr-10840.md b/changelog.d/pr-10840.md new file mode 100644 index 00000000000..0652ba03ca2 --- /dev/null +++ b/changelog.d/pr-10840.md @@ -0,0 +1,28 @@ +--- +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/doc/cabal-commands.rst b/doc/cabal-commands.rst index 7f0b37ac48f..2e4004c3691 100644 --- a/doc/cabal-commands.rst +++ b/doc/cabal-commands.rst @@ -532,38 +532,74 @@ users see a consistent set of dependencies. For libraries, this is not recommended: users often need to build against different versions of libraries than what you developed against. +.. _cabal-gen-bounds: + cabal gen-bounds -^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ -``cabal gen-bounds [FLAGS]`` generates bounds for all dependencies that do not -currently have them. Generated bounds are printed to stdout. You can then -paste them into your .cabal file. -The generated bounds conform to the `Package Versioning Policy`_, which is -a recommended versioning system for publicly released Cabal packages. +:: -.. code-block:: console + cabal gen-bounds [TARGETS] [FLAGS] + +Generate PVP-compliant dependency bounds for packages in the project based +on currently installed versions. This is helpful when creating or updating +package dependencies to ensure compatibility with specific version ranges. + +To use it, run `cabal gen-bounds` in a directory containing a cabal.project file or +within a subdirectory of a multi-package project. The command will analyze +the project structure and suggest appropriate version bounds for dependencies based +on the currently installed versions of those packages. + +The suggested bounds follow the Package Versioning Policy (PVP) convention, +allowing changes in the last segment of the version number. These suggestions +are formatted as Cabal constraint expressions that can be directly copied +into your .cabal file in the appropriate `build-depends` section. + +You can also specify particular packages to analyze with `cabal gen-bounds package-name`. +The command supports the same targets as `cabal build`. + +Examples: + +Basic usage: + +:: $ cabal gen-bounds -For example, given the following dependencies without bounds specified in -:pkg-field:`build-depends`: +In a multi-package project: :: - build-depends: - base, - mtl, - transformers, + $ cat cabal.project + packages: package-a/ + package-b/ + + $ cabal gen-bounds all + Configuration is affected by the following files: + - cabal.project + Resolving dependencies... + + Congratulations, all dependencies for package-a:lib:package-a are up-to-date. -``gen-bounds`` might suggest changing them to the following: + 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, + +You can also specify particular target to analyze: :: - build-depends: - base >= 4.15.0 && < 4.16, - mtl >= 2.2.2 && < 2.3, - transformers >= 0.5.6 && < 0.6, + $ cabal gen-bounds package-a + +The command output provides suggested version bounds for each component's +dependencies that lack proper bounds. For each component, dependencies that +need bounds are listed along with the suggested bounds, like: + +:: + For component my-package:lib:my-package: + some-dependency >= 1.2.3 && < 1.3, + another-dependency >= 2.0.0 && < 2.1, cabal outdated ^^^^^^^^^^^^^^ From aafd6885b19746c137664f18584a4b5175b5475c Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Thu, 27 Mar 2025 13:44:02 +0000 Subject: [PATCH 2/2] Implement v2-outdated command for cabal-install This commit updates the outdated command to the new v2 architecture: - Transform the old `outdated` command into a new v2-style command `v2-outdated` - Support three modes of operation: - Check for outdated dependencies in v1-style freeze file - Check for outdated dependencies in project context (cabal.project & cabal.project.freeze) - Check for outdated dependencies in local packages Since the `cabal outdated` command now supports the v2-architecture, you can request to run the `v2-outdated` command on any target. I also introduced the `resolveTargetsFromLocalPackages` which resolves which local packages targets refer to without having to run the solver. This will be useful for `cabal check` as well. A change in behaviour from before is that the package description is flattened, so all bounds will be warned about rather than those in conditional branches being ignored. Fixes #8283 --- .../Solver/Types/ProjectConfigPath.hs | 5 +- .../src/Distribution/Client/CmdBench.hs | 2 +- .../src/Distribution/Client/CmdBuild.hs | 2 +- .../src/Distribution/Client/CmdGenBounds.hs | 2 +- .../src/Distribution/Client/CmdHaddock.hs | 2 +- .../Distribution/Client/CmdHaddockProject.hs | 4 +- .../src/Distribution/Client/CmdInstall.hs | 6 +- .../src/Distribution/Client/CmdListBin.hs | 2 +- .../src/Distribution/Client/CmdOutdated.hs | 436 +++++++++--------- .../src/Distribution/Client/CmdRepl.hs | 2 +- .../src/Distribution/Client/CmdRun.hs | 2 +- .../src/Distribution/Client/CmdTarget.hs | 2 +- .../src/Distribution/Client/CmdTest.hs | 2 +- cabal-install/src/Distribution/Client/Main.hs | 2 +- .../Client/ProjectOrchestration.hs | 182 +++++++- .../Client/ProjectPlanning/Types.hs | 5 + .../src/Distribution/Client/ScriptUtils.hs | 4 +- cabal-install/tests/IntegrationTests2.hs | 8 +- .../Outdated/Issue8283/cabal.project | 4 + .../Outdated/Issue8283/cabal.test.hs | 27 ++ .../Outdated/Issue8283/package-a/LICENSE | 28 ++ .../Issue8283/package-a/package-a.cabal | 24 + .../Issue8283/package-a/src/ModuleA.hs | 4 + .../Outdated/Issue8283/package-b/LICENSE | 28 ++ .../Issue8283/package-b/package-b.cabal | 17 + .../Issue8283/package-b/src/ModuleB.hs | 6 + .../Issue8283/repo/base-3.0.3.1/base.cabal | 154 +++++++ .../Issue8283/repo/base-3.0.3.2/base.cabal | 160 +++++++ .../Issue8283/repo/base-4.0.0.0/base.cabal | 174 +++++++ .../repo/binary-0.8.5.0/binary.cabal | 35 ++ .../repo/binary-0.8.6.0/binary.cabal | 35 ++ .../repo/binary-0.9.0.0/binary.cabal | 35 ++ .../Issue8283/repo/preferred-versions | 1 + .../template-haskell.cabal | 22 + .../template-haskell.cabal | 22 + .../template-haskell.cabal | 32 ++ .../PackageTests/Outdated/outdated_freeze.out | 45 +- .../Outdated/outdated_freeze.test.hs | 2 +- 38 files changed, 1243 insertions(+), 282 deletions(-) create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.project create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/LICENSE create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/package-a.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/src/ModuleA.hs create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/LICENSE create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/package-b.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/src/ModuleB.hs create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.1/base.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.2/base.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-4.0.0.0/base.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.5.0/binary.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.6.0/binary.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.9.0.0/binary.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/preferred-versions create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.0/template-haskell.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.1/template-haskell.cabal create mode 100644 cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.4.0.0/template-haskell.cabal diff --git a/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs b/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs index 17543b5f2de..a6d80dddab6 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs @@ -38,7 +38,7 @@ import qualified System.FilePath.Posix as Posix import qualified System.FilePath.Windows as Windows import qualified Data.List.NonEmpty as NE import Distribution.Solver.Modular.Version (VR) -import Distribution.Pretty (prettyShow) +import Distribution.Pretty (prettyShow, Pretty(..)) import Distribution.Utils.String (trim) import Text.PrettyPrint import Distribution.Simple.Utils (ordNub) @@ -58,6 +58,9 @@ import Distribution.System (OS(Windows), buildOS) newtype ProjectConfigPath = ProjectConfigPath (NonEmpty FilePath) deriving (Eq, Show, Generic) +instance Pretty ProjectConfigPath where + pretty = docProjectConfigPath + -- | Sorts URIs after local file paths and longer file paths after shorter ones -- as measured by the number of path segments. If still equal, then sorting is -- lexical. diff --git a/cabal-install/src/Distribution/Client/CmdBench.hs b/cabal-install/src/Distribution/Client/CmdBench.hs index 05634141288..f6fc6e55d9d 100644 --- a/cabal-install/src/Distribution/Client/CmdBench.hs +++ b/cabal-install/src/Distribution/Client/CmdBench.hs @@ -132,7 +132,7 @@ benchAction flags@NixStyleFlags{..} targetStrings globalFlags = do -- (as opposed to say build or haddock targets). targets <- either (reportTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdBuild.hs b/cabal-install/src/Distribution/Client/CmdBuild.hs index 44f1c4e0f27..14cda34d160 100644 --- a/cabal-install/src/Distribution/Client/CmdBuild.hs +++ b/cabal-install/src/Distribution/Client/CmdBuild.hs @@ -157,7 +157,7 @@ buildAction flags@NixStyleFlags{extraFlags = buildFlags, ..} targetStrings globa -- (as opposed to say repl or haddock targets). targets <- either (reportBuildTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdGenBounds.hs b/cabal-install/src/Distribution/Client/CmdGenBounds.hs index ee15a95bfee..0d0403526f7 100644 --- a/cabal-install/src/Distribution/Client/CmdGenBounds.hs +++ b/cabal-install/src/Distribution/Client/CmdGenBounds.hs @@ -109,7 +109,7 @@ genBoundsAction flags targetStrings globalFlags = -- Step 2: Resolve the targets for the gen-bounds command. targets <- either (reportGenBoundsTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdHaddock.hs b/cabal-install/src/Distribution/Client/CmdHaddock.hs index 677589e3e35..34a74dc5d12 100644 --- a/cabal-install/src/Distribution/Client/CmdHaddock.hs +++ b/cabal-install/src/Distribution/Client/CmdHaddock.hs @@ -176,7 +176,7 @@ haddockAction relFlags targetStrings globalFlags = do -- haddock targets targets <- either (reportBuildDocumentationTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver (selectPackageTargets haddockFlags) selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdHaddockProject.hs b/cabal-install/src/Distribution/Client/CmdHaddockProject.hs index 0635a77d68e..2ad6d0e0c6d 100644 --- a/cabal-install/src/Distribution/Client/CmdHaddockProject.hs +++ b/cabal-install/src/Distribution/Client/CmdHaddockProject.hs @@ -26,7 +26,7 @@ import Distribution.Client.ProjectOrchestration , ProjectBuildContext (..) , TargetSelector (..) , pruneInstallPlanToTargets - , resolveTargets + , resolveTargetsFromSolver , runProjectPreBuildPhase , selectComponentTargetBasic ) @@ -143,7 +143,7 @@ haddockProjectAction flags _extraArgs globalFlags = do -- (as opposed to say repl or haddock targets). targets <- either reportTargetProblems return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTargetBasic elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdInstall.hs b/cabal-install/src/Distribution/Client/CmdInstall.hs index 63cf59169a3..4a40803384e 100644 --- a/cabal-install/src/Distribution/Client/CmdInstall.hs +++ b/cabal-install/src/Distribution/Client/CmdInstall.hs @@ -825,7 +825,7 @@ partitionToKnownTargetsAndHackagePackages -> IO (TargetsMap, [PackageName]) partitionToKnownTargetsAndHackagePackages verbosity pkgDb elaboratedPlan targetSelectors = do let mTargets = - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan @@ -865,7 +865,7 @@ partitionToKnownTargetsAndHackagePackages verbosity pkgDb elaboratedPlan targetS -- removed (or we've given up). targets <- either (reportBuildTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan @@ -885,7 +885,7 @@ constructProjectBuildContext verbosity baseCtx targetSelectors = do -- Interpret the targets on the command line as build targets targets <- either (reportBuildTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdListBin.hs b/cabal-install/src/Distribution/Client/CmdListBin.hs index df16b98e1a2..797db65c395 100644 --- a/cabal-install/src/Distribution/Client/CmdListBin.hs +++ b/cabal-install/src/Distribution/Client/CmdListBin.hs @@ -106,7 +106,7 @@ listbinAction flags@NixStyleFlags{..} args globalFlags = do -- (as opposed to say repl or haddock targets). targets <- either (reportTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdOutdated.hs b/cabal-install/src/Distribution/Client/CmdOutdated.hs index 028b5e922e5..6050b565c9c 100644 --- a/cabal-install/src/Distribution/Client/CmdOutdated.hs +++ b/cabal-install/src/Distribution/Client/CmdOutdated.hs @@ -1,5 +1,8 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} -- | -- Module : Distribution.Client.CmdOutdated @@ -13,44 +16,22 @@ module Distribution.Client.CmdOutdated , outdatedAction , ListOutdatedSettings (..) , listOutdated + , IgnoreMajorVersionBumps (..) + , showResult ) where import Distribution.Client.Compat.Prelude -import Distribution.Compat.Lens - ( _1 - , _2 - ) import Prelude () +import qualified Data.Set as Set import Distribution.Client.Config ( SavedConfig ( savedConfigureExFlags - , savedConfigureFlags - , savedGlobalFlags ) ) -import Distribution.Client.DistDirLayout - ( DistDirLayout (distProjectFile, distProjectRootDirectory) - , defaultDistDirLayout - ) -import Distribution.Client.IndexUtils as IndexUtils +import qualified Distribution.Client.IndexUtils as IndexUtils import Distribution.Client.ProjectConfig -import Distribution.Client.ProjectConfig.Legacy - ( instantiateProjectConfigSkeletonWithCompiler - ) -import Distribution.Client.ProjectFlags - ( ProjectFlags (..) - , defaultProjectFlags - , projectFlagsOptions - , removeIgnoreProjectOption - ) -import Distribution.Client.RebuildMonad - ( runRebuild - ) -import Distribution.Client.Sandbox - ( loadConfigOrSandboxConfig - ) import Distribution.Client.Sandbox.PackageEnvironment ( loadUserConfig ) @@ -60,83 +41,11 @@ import Distribution.Client.Targets , userToPackageConstraint ) import Distribution.Client.Types.SourcePackageDb as SourcePackageDb -import Distribution.Solver.Types.PackageConstraint - ( packageConstraintToDependency - ) -import Distribution.Utils.Generic - ( safeLast - , wrapText - ) - -import Distribution.Client.HttpUtils import qualified Distribution.Compat.CharParsing as P -import Distribution.Package - ( PackageName - , packageVersion - ) import Distribution.PackageDescription - ( allBuildDepends - ) import Distribution.PackageDescription.Configuration - ( finalizePD - ) -import Distribution.ReadE - ( parsecToReadE - ) -import Distribution.Simple.Command - ( CommandUI (..) - , OptionField - , ShowOrParseArgs - , liftOptionL - , optArg - , option - , reqArg - ) -import Distribution.Simple.Compiler - ( Compiler - , compilerInfo - ) -import Distribution.Simple.Flag - ( Flag (..) - , flagToMaybe - , fromFlagOrDefault - , toFlag - ) -import Distribution.Simple.PackageDescription - ( readGenericPackageDescription - ) -import Distribution.Simple.Setup - ( optionVerbosity - , trueArg - ) -import Distribution.Simple.Utils - ( debug - , dieWithException - , notice - , tryFindPackageDesc - ) -import Distribution.System - ( Platform (..) - ) -import Distribution.Types.ComponentRequestedSpec - ( ComponentRequestedSpec (..) - ) -import Distribution.Types.Dependency - ( Dependency (..) - ) -import Distribution.Types.DependencySatisfaction - ( DependencySatisfaction (..) - ) -import Distribution.Types.PackageVersionConstraint - ( PackageVersionConstraint (..) - , simplifyPackageVersionConstraint - ) -import Distribution.Utils.NubList - ( fromNubList - ) -import Distribution.Verbosity - ( normal - , silent +import Distribution.Solver.Types.PackageConstraint + ( packageConstraintToDependency ) import Distribution.Version ( LowerBound (..) @@ -149,21 +58,47 @@ import Distribution.Version ) import qualified Data.Set as S -import Distribution.Client.Errors -import Distribution.Utils.Path (relativeSymbolicPath) +import Distribution.Client.NixStyleOptions import System.Directory - ( doesFileExist - , getCurrentDirectory + ( getCurrentDirectory ) +import Prelude () + +import Distribution.Client.ProjectOrchestration + +-- import Distribution.Simple.Setup + +import Control.Monad +import Distribution.Client.ScriptUtils +import Distribution.Package +import Distribution.ReadE +import Distribution.Simple.Command +import Distribution.Simple.Flag +import Distribution.Simple.Setup hiding (GlobalFlags (..)) +import Distribution.Simple.Utils +import Distribution.Types.PackageVersionConstraint +import Distribution.Verbosity + +import qualified Data.Map.Strict as Map +import Distribution.Client.CmdErrorMessages +import Distribution.Client.ProjectPlanning.Types +import Distribution.Client.TargetProblem +import Distribution.Client.Types.PackageSpecifier +import Distribution.Solver.Types.ConstraintSource +import Distribution.Solver.Types.SourcePackage +import Distribution.Types.Component +import qualified Text.PrettyPrint as PP + +import Distribution.Client.Errors ------------------------------------------------------------------------------- -- Command ------------------------------------------------------------------------------- -outdatedCommand :: CommandUI (ProjectFlags, OutdatedFlags) +outdatedCommand :: CommandUI (NixStyleFlags OutdatedFlags) outdatedCommand = CommandUI - { commandName = "outdated" + { commandName = "v2-outdated" , commandSynopsis = "Check for outdated dependencies." , commandDescription = Just $ \_ -> wrapText $ @@ -172,12 +107,9 @@ outdatedCommand = , commandNotes = Nothing , commandUsage = \pname -> "Usage: " ++ pname ++ " outdated [FLAGS] [PACKAGES]\n" - , commandDefaultFlags = (defaultProjectFlags, defaultOutdatedFlags) - , commandOptions = \showOrParseArgs -> - map - (liftOptionL _1) - (removeIgnoreProjectOption (projectFlagsOptions showOrParseArgs)) - ++ map (liftOptionL _2) (outdatedOptions showOrParseArgs) + , commandDefaultFlags = defaultNixStyleFlags defaultOutdatedFlags + , commandOptions = nixStyleOptions $ \showOrParseArgs -> + outdatedOptions showOrParseArgs } ------------------------------------------------------------------------------- @@ -202,8 +134,7 @@ instance Semigroup IgnoreMajorVersionBumps where IgnoreMajorVersionBumpsSome (a ++ b) data OutdatedFlags = OutdatedFlags - { outdatedVerbosity :: Flag Verbosity - , outdatedFreezeFile :: Flag Bool + { outdatedFreezeFile :: Flag Bool , outdatedNewFreezeFile :: Flag Bool , outdatedSimpleOutput :: Flag Bool , outdatedExitCode :: Flag Bool @@ -215,8 +146,7 @@ data OutdatedFlags = OutdatedFlags defaultOutdatedFlags :: OutdatedFlags defaultOutdatedFlags = OutdatedFlags - { outdatedVerbosity = toFlag normal - , outdatedFreezeFile = mempty + { outdatedFreezeFile = mempty , outdatedNewFreezeFile = mempty , outdatedSimpleOutput = mempty , outdatedExitCode = mempty @@ -227,10 +157,7 @@ defaultOutdatedFlags = outdatedOptions :: ShowOrParseArgs -> [OptionField OutdatedFlags] outdatedOptions _showOrParseArgs = - [ optionVerbosity - outdatedVerbosity - (\v flags -> flags{outdatedVerbosity = v}) - , option + [ option [] ["freeze-file", "v1-freeze-file"] "Act on the freeze file" @@ -239,8 +166,8 @@ outdatedOptions _showOrParseArgs = trueArg , option [] - ["v2-freeze-file", "new-freeze-file"] - "Act on the new-style freeze file (default: cabal.project.freeze)" + ["project-context", "v2-freeze-file", "new-freeze-file"] + "Check for outdated dependencies in the project context, for example, dependencies specified in cabal.project orcabal.project.freeze." outdatedNewFreezeFile (\v flags -> flags{outdatedNewFreezeFile = v}) trueArg @@ -307,54 +234,53 @@ outdatedOptions _showOrParseArgs = -- Action ------------------------------------------------------------------------------- --- | Entry point for the 'outdated' command. -outdatedAction :: (ProjectFlags, OutdatedFlags) -> [String] -> GlobalFlags -> IO () -outdatedAction (ProjectFlags{flagProjectDir, flagProjectFile}, OutdatedFlags{..}) _targetStrings globalFlags = do - config <- loadConfigOrSandboxConfig verbosity globalFlags - let globalFlags' = savedGlobalFlags config `mappend` globalFlags - configFlags = savedConfigureFlags config - withRepoContext verbosity globalFlags' $ \repoContext -> do - when (not newFreezeFile && (isJust mprojectDir || isJust mprojectFile)) $ - dieWithException verbosity OutdatedAction - - sourcePkgDb <- IndexUtils.getSourcePackages verbosity repoContext - (comp, platform, _progdb) <- configCompilerAux' configFlags - deps <- - if freezeFile - then depsFromFreezeFile verbosity - else - if newFreezeFile - then do - httpTransport <- - configureTransport - verbosity - (fromNubList . globalProgPathExtra $ globalFlags) - (flagToMaybe . globalHttpTransport $ globalFlags) - depsFromNewFreezeFile verbosity httpTransport comp platform mprojectDir mprojectFile - else do - depsFromPkgDesc verbosity comp platform - debug verbosity $ - "Dependencies loaded: " - ++ intercalate ", " (map prettyShow deps) - let outdatedDeps = - listOutdated - deps - sourcePkgDb - (ListOutdatedSettings ignorePred minorPred) - when (not quiet) $ - showResult verbosity outdatedDeps simpleOutput - if exitCode && (not . null $ outdatedDeps) - then exitFailure - else return () +getSourcePackages :: Verbosity -> ProjectConfig -> IO SourcePackageDb +getSourcePackages verbosity projectConfig = + projectConfigWithSolverRepoContext + verbosity + (projectConfigShared projectConfig) + (projectConfigBuildOnly projectConfig) + (\ctx -> IndexUtils.getSourcePackages verbosity ctx) + +outdatedAction :: NixStyleFlags OutdatedFlags -> [String] -> GlobalFlags -> IO () +outdatedAction flags targetStrings globalFlags = + withContextAndSelectors + AcceptNoTargets + Nothing + flags + targetStrings + globalFlags + OtherCommand + $ \_targetCtx ctx targetSelectors -> do + deps <- + if + | freezeFile -> depsFromFreezeFile verbosity + | newFreezeFile -> depsFromProjectContext verbosity (projectConfig ctx) + | otherwise -> depsFromLocalPackages verbosity ctx targetSelectors + + debug verbosity $ + "Dependencies loaded: " + ++ intercalate ", " (map prettyShow deps) + + sourcePkgDb <- getSourcePackages verbosity (projectConfig ctx) + let outdatedDeps = + listOutdated + deps + sourcePkgDb + (ListOutdatedSettings ignorePred minorPred) + when (not quiet) $ + showResult verbosity outdatedDeps simpleOutput + if exitCode && (not . null $ outdatedDeps) + then exitFailure + else return () where + OutdatedFlags{..} = extraFlags flags verbosity = if quiet then silent - else fromFlagOrDefault normal outdatedVerbosity + else fromFlagOrDefault normal (setupVerbosity (configCommonFlags (configFlags flags))) freezeFile = fromFlagOrDefault False outdatedFreezeFile newFreezeFile = fromFlagOrDefault False outdatedNewFreezeFile - mprojectDir = flagToMaybe flagProjectDir - mprojectFile = flagToMaybe flagProjectFile simpleOutput = fromFlagOrDefault False outdatedSimpleOutput quiet = fromFlagOrDefault False outdatedQuiet exitCode = fromFlagOrDefault quiet outdatedExitCode @@ -369,93 +295,104 @@ outdatedAction (ProjectFlags{flagProjectDir, flagProjectFile}, OutdatedFlags{..} let minorSet = S.fromList pkgs in \pkgname -> pkgname `S.member` minorSet +reportOutdatedTargetProblem :: Verbosity -> [TargetProblem'] -> IO a +reportOutdatedTargetProblem verbosity problems = + reportTargetProblems verbosity "check outdated dependencies for" problems + -- | Print either the list of all outdated dependencies, or a message -- that there are none. -showResult :: Verbosity -> [(PackageVersionConstraint, Version)] -> Bool -> IO () +showResult :: Verbosity -> [OutdatedDependency] -> Bool -> IO () showResult verbosity outdatedDeps simpleOutput = if not . null $ outdatedDeps then do when (not simpleOutput) $ notice verbosity "Outdated dependencies:" - for_ outdatedDeps $ \(d@(PackageVersionConstraint pn _), v) -> + for_ outdatedDeps $ \(OutdatedDependency d@(PackageVersionConstraint pn _) v src) -> let outdatedDep = if simpleOutput then prettyShow pn - else prettyShow d ++ " (latest: " ++ prettyShow v ++ ")" + else prettyShow d ++ " (latest: " ++ prettyShow v ++ ", " ++ prettyOutdatedDependencySource src ++ ")" in notice verbosity outdatedDep else notice verbosity "All dependencies are up to date." +data OutdatedDependencyX v = OutdatedDependency + { _outdatedDependency :: PackageVersionConstraint + , _outdatedVersion :: v + , _outdatedSource :: OutdatedDependencySource + } + +instance Pretty (OutdatedDependencyX Version) where + pretty (OutdatedDependency dep ver src) = + pretty dep + <+> PP.text "(latest:" + <+> pretty ver + <+> PP.text "," + <+> PP.text "from:" + <+> PP.text (prettyOutdatedDependencySource src) + <+> PP.text ")" + +instance Pretty (OutdatedDependencyX ()) where + pretty (OutdatedDependency dep _ src) = + pretty dep <+> PP.text "(from:" <+> PP.text (prettyOutdatedDependencySource src) `mappend` PP.text ")" + +data OutdatedDependencySource = ConfigSource ConstraintSource | ComponentSource PackageId ComponentTarget + +-- | Pretty print an 'OutdatedDependencySource'. +prettyOutdatedDependencySource :: OutdatedDependencySource -> String +prettyOutdatedDependencySource (ConfigSource src) = showConstraintSource src +prettyOutdatedDependencySource (ComponentSource pkgId ctarget) = prettyShow pkgId ++ ":" ++ showComponentTarget pkgId ctarget + +type CandidateOutdatedDependency = OutdatedDependencyX () + +mkCandidateOutdatedDependency :: PackageVersionConstraint -> OutdatedDependencySource -> CandidateOutdatedDependency +mkCandidateOutdatedDependency dep src = OutdatedDependency dep () src + +type OutdatedDependency = OutdatedDependencyX Version + -- | Convert a list of 'UserConstraint's to a 'Dependency' list. -userConstraintsToDependencies :: [UserConstraint] -> [PackageVersionConstraint] +userConstraintsToDependencies :: [(UserConstraint, ConstraintSource)] -> [CandidateOutdatedDependency] userConstraintsToDependencies ucnstrs = - mapMaybe (packageConstraintToDependency . userToPackageConstraint) ucnstrs + mapMaybe (\(uc, src) -> fmap (flip mkCandidateOutdatedDependency (ConfigSource src)) (packageConstraintToDependency . userToPackageConstraint $ uc)) ucnstrs -- | Read the list of dependencies from the freeze file. -depsFromFreezeFile :: Verbosity -> IO [PackageVersionConstraint] +depsFromFreezeFile :: Verbosity -> IO [CandidateOutdatedDependency] depsFromFreezeFile verbosity = do cwd <- getCurrentDirectory userConfig <- loadUserConfig verbosity cwd Nothing let ucnstrs = - map fst . configExConstraints . savedConfigureExFlags $ + configExConstraints . savedConfigureExFlags $ userConfig deps = userConstraintsToDependencies ucnstrs debug verbosity "Reading the list of dependencies from the freeze file" return deps --- | Read the list of dependencies from the new-style freeze file. -depsFromNewFreezeFile :: Verbosity -> HttpTransport -> Compiler -> Platform -> Maybe FilePath -> Maybe FilePath -> IO [PackageVersionConstraint] -depsFromNewFreezeFile verbosity httpTransport compiler (Platform arch os) mprojectDir mprojectFile = do - projectRoot <- - either throwIO return - =<< findProjectRoot verbosity mprojectDir mprojectFile - let distDirLayout = - defaultDistDirLayout - projectRoot - {- TODO: Support dist dir override -} Nothing - Nothing - projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $ do - pcs <- readProjectLocalFreezeConfig verbosity httpTransport distDirLayout - pure $ instantiateProjectConfigSkeletonWithCompiler os arch (compilerInfo compiler) mempty pcs - let ucnstrs = - map fst . projectConfigConstraints . projectConfigShared $ - projectConfig +-- | Read the list of dependencies from the cabal.project context. +-- This will get dependencies from +-- * cabal.project.freeze +-- * cabal.project.local +-- * cabal.project +-- files +depsFromProjectContext :: Verbosity -> ProjectConfig -> IO [CandidateOutdatedDependency] +depsFromProjectContext verbosity projectConfig = do + let ucnstrs = projectConfigConstraints $ projectConfigShared projectConfig deps = userConstraintsToDependencies ucnstrs - freezeFile = distProjectFile distDirLayout "freeze" - freezeFileExists <- doesFileExist freezeFile - - unless freezeFileExists $ - dieWithException verbosity $ - FreezeFileExistsErr freezeFile - + provenance = projectConfigProvenance projectConfig debug verbosity $ - "Reading the list of dependencies from the new-style freeze file " ++ freezeFile + "Reading the list of dependencies from the project files: " + ++ intercalate ", " [prettyShow p | Explicit p <- Set.toList provenance] return deps -- | Read the list of dependencies from the package description. -depsFromPkgDesc :: Verbosity -> Compiler -> Platform -> IO [PackageVersionConstraint] -depsFromPkgDesc verbosity comp platform = do - path <- tryFindPackageDesc verbosity Nothing - gpd <- readGenericPackageDescription verbosity Nothing (relativeSymbolicPath path) - let cinfo = compilerInfo comp - epd = - finalizePD - mempty - (ComponentRequestedSpec True True) - (const Satisfied) - platform - cinfo - [] - gpd - case epd of - Left _ -> dieWithException verbosity FinalizePDFailed - Right (pd, _) -> do - let bd = allBuildDepends pd - debug - verbosity - "Reading the list of dependencies from the package description" - return $ map toPVC bd +depsFromPkgDesc :: Verbosity -> PackageId -> GenericPackageDescription -> ComponentTarget -> IO [CandidateOutdatedDependency] +depsFromPkgDesc verbosity pkgId gpd t@(ComponentTarget cname _subtarget) = do + let pd = flattenPackageDescription gpd + bd = targetBuildDepends (componentBuildInfo (getComponent pd cname)) + debug + verbosity + "Reading the list of dependencies from the package description" + return $ map toPVC bd where - toPVC (Dependency pn vr _) = PackageVersionConstraint pn vr + toPVC (Dependency pn vr _) = mkCandidateOutdatedDependency (PackageVersionConstraint pn vr) (ComponentSource pkgId t) -- | Various knobs for customising the behaviour of 'listOutdated'. data ListOutdatedSettings = ListOutdatedSettings @@ -467,20 +404,22 @@ data ListOutdatedSettings = ListOutdatedSettings -- | Find all outdated dependencies. listOutdated - :: [PackageVersionConstraint] + :: [CandidateOutdatedDependency] -> SourcePackageDb -> ListOutdatedSettings - -> [(PackageVersionConstraint, Version)] + -> [OutdatedDependency] listOutdated deps sourceDb (ListOutdatedSettings ignorePred minorPred) = - mapMaybe isOutdated $ map simplifyPackageVersionConstraint deps + mapMaybe isOutdated deps where - isOutdated :: PackageVersionConstraint -> Maybe (PackageVersionConstraint, Version) - isOutdated dep@(PackageVersionConstraint pname vr) + isOutdated :: CandidateOutdatedDependency -> Maybe OutdatedDependency + isOutdated (OutdatedDependency dep () src) | ignorePred pname = Nothing | otherwise = let this = map packageVersion $ SourcePackageDb.lookupDependency sourceDb pname vr latest = lookupLatest dep - in (\v -> (dep, v)) `fmap` isOutdated' this latest + in (\v -> OutdatedDependency dep v src) `fmap` isOutdated' this latest + where + PackageVersionConstraint pname vr = simplifyPackageVersionConstraint dep isOutdated' :: [Version] -> [Version] -> Maybe Version isOutdated' [] _ = Nothing @@ -506,3 +445,50 @@ listOutdated deps sourceDb (ListOutdatedSettings ignorePred minorPred) = case upper of NoUpperBound -> vr UpperBound _v1 _ -> majorBoundVersion v0 + +-- | For the outdated command, when a whole package is specified we want +-- to select all buildable components. +selectPackageTargetsForOutdated + :: TargetSelector + -> [AvailableTarget k] + -> Either (TargetProblem') [k] +selectPackageTargetsForOutdated targetSelector targets + -- No targets available at all is an error + | null targets = Left (TargetProblemNoTargets targetSelector) + -- We select all buildable components for a package + | otherwise = Right $ selectBuildableTargets targets + +-- | For the outdated command, when a specific component is specified +-- we simply select that component. +selectComponentTargetForOutdated + :: SubComponentTarget + -> AvailableTarget k + -> Either (TargetProblem') k +selectComponentTargetForOutdated subtarget target = + selectComponentTargetBasic subtarget target + +-- | Read the list of dependencies from local packages +depsFromLocalPackages :: Verbosity -> ProjectBaseContext -> [TargetSelector] -> IO [CandidateOutdatedDependency] +depsFromLocalPackages verbosity ctx targetSelectors = do + when (null targetSelectors) $ + dieWithException verbosity TargetSelectorNoTargetsInCwdTrue + targets <- + either (reportOutdatedTargetProblem verbosity) return $ + resolveTargetsFromLocalPackages + selectPackageTargetsForOutdated + selectComponentTargetForOutdated + (localPackages ctx) + targetSelectors + fmap concat <$> forM (localPackages ctx) $ \pkg -> case pkg of + SpecificSourcePackage pkg' -> do + -- Find the package in the resolved targets + let pkgId = packageId pkg' + let pkgTargets = + case Map.lookup pkgId targets of + Just componentTargets -> map fst componentTargets + Nothing -> [] + -- If no specific components were targeted, use the whole package + -- Get dependencies for each targeted component + fmap concat <$> forM pkgTargets $ \target -> + depsFromPkgDesc verbosity pkgId (srcpkgDescription pkg') target + _ -> return [] diff --git a/cabal-install/src/Distribution/Client/CmdRepl.hs b/cabal-install/src/Distribution/Client/CmdRepl.hs index a75524bbca6..b02d0ef1b82 100644 --- a/cabal-install/src/Distribution/Client/CmdRepl.hs +++ b/cabal-install/src/Distribution/Client/CmdRepl.hs @@ -515,7 +515,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g -- (as opposed to say build or haddock targets). targets <- either (reportTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver (selectPackageTargets multi_repl_enabled) selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdRun.hs b/cabal-install/src/Distribution/Client/CmdRun.hs index 0000a2927a1..e96054fb574 100644 --- a/cabal-install/src/Distribution/Client/CmdRun.hs +++ b/cabal-install/src/Distribution/Client/CmdRun.hs @@ -228,7 +228,7 @@ runAction flags@NixStyleFlags{..} targetAndArgs globalFlags = -- (as opposed to say repl or haddock targets). targets <- either (reportTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdTarget.hs b/cabal-install/src/Distribution/Client/CmdTarget.hs index c2edeeec89c..6fc0f9f973c 100644 --- a/cabal-install/src/Distribution/Client/CmdTarget.hs +++ b/cabal-install/src/Distribution/Client/CmdTarget.hs @@ -172,7 +172,7 @@ targetAction flags@NixStyleFlags{..} ts globalFlags = do targets :: TargetsMap <- either (reportBuildTargetProblems verbosity) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/CmdTest.hs b/cabal-install/src/Distribution/Client/CmdTest.hs index 3812bd6af87..863568acf9d 100644 --- a/cabal-install/src/Distribution/Client/CmdTest.hs +++ b/cabal-install/src/Distribution/Client/CmdTest.hs @@ -144,7 +144,7 @@ testAction flags@NixStyleFlags{..} targetStrings globalFlags = do -- (as opposed to say build or haddock targets). targets <- either (reportTargetProblems verbosity failWhenNoTestSuites) return $ - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan diff --git a/cabal-install/src/Distribution/Client/Main.hs b/cabal-install/src/Distribution/Client/Main.hs index 1e4a24692e4..2ad6b04276f 100644 --- a/cabal-install/src/Distribution/Client/Main.hs +++ b/cabal-install/src/Distribution/Client/Main.hs @@ -437,7 +437,6 @@ mainWorker args = do , regularCmd initCommand initAction , regularCmd userConfigCommand userConfigAction , regularCmd CmdPath.pathCommand CmdPath.pathAction - , regularCmd CmdOutdated.outdatedCommand CmdOutdated.outdatedAction , wrapperCmd hscolourCommand hscolourCommonFlags , hiddenCmd formatCommand formatAction , hiddenCmd actAsSetupCommand actAsSetupAction @@ -463,6 +462,7 @@ mainWorker args = do , newCmd CmdSdist.sdistCommand CmdSdist.sdistAction , newCmd CmdTarget.targetCommand CmdTarget.targetAction , newCmd CmdGenBounds.genBoundsCommand CmdGenBounds.genBoundsAction + , newCmd CmdOutdated.outdatedCommand CmdOutdated.outdatedAction , legacyCmd configureExCommand configureAction , legacyCmd genBoundsCommand genBoundsAction , legacyCmd buildCommand buildAction diff --git a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs index a14d43e4b99..9b12f570252 100644 --- a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs +++ b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs @@ -58,7 +58,8 @@ module Distribution.Client.ProjectOrchestration -- ** Selecting what targets we mean , readTargetSelectors , reportTargetSelectorProblems - , resolveTargets + , resolveTargetsFromSolver + , resolveTargetsFromLocalPackages , TargetsMap , allTargetSelectors , uniqueTargetSelectors @@ -146,6 +147,7 @@ import Distribution.Client.Types import Distribution.Solver.Types.PackageIndex ( lookupPackageName ) +import Distribution.Solver.Types.SourcePackage (SourcePackage (..)) import Distribution.Client.BuildReports.Anonymous (cabalInstallID) import qualified Distribution.Client.BuildReports.Anonymous as BuildReports @@ -169,7 +171,9 @@ import Distribution.Types.UnqualComponentName , packageNameToUnqualComponentName ) +import Distribution.PackageDescription.Configuration import Distribution.Solver.Types.OptionalStanza +import Distribution.Types.Component import Control.Exception (assert) import qualified Data.List.NonEmpty as NE @@ -564,7 +568,9 @@ runProjectPostBuildPhase -- matched this target. Typically this is exactly one, but in general it is -- possible to for different selectors to match the same target. This extra -- information is primarily to help make helpful error messages. -type TargetsMap = Map UnitId [(ComponentTarget, NonEmpty TargetSelector)] +type TargetsMap = TargetsMapX UnitId + +type TargetsMapX u = Map u [(ComponentTarget, NonEmpty TargetSelector)] -- | Get all target selectors. allTargetSelectors :: TargetsMap -> [TargetSelector] @@ -574,6 +580,63 @@ allTargetSelectors = concatMap (NE.toList . snd) . concat . Map.elems uniqueTargetSelectors :: TargetsMap -> [TargetSelector] uniqueTargetSelectors = ordNub . allTargetSelectors +-- | Resolve targets from a solver result. +-- +-- This is a convenience wrapper around 'resolveTargetsFromSolver' that takes an +-- 'ElaboratedInstallPlan' directly, rather than requiring the caller to +-- construct the 'AvailableTargetIndexes' first. +resolveTargetsFromSolver + :: forall err + . ( forall k + . TargetSelector + -> [AvailableTarget k] + -> Either (TargetProblem err) [k] + ) + -> ( forall k + . SubComponentTarget + -> AvailableTarget k + -> Either (TargetProblem err) k + ) + -> ElaboratedInstallPlan + -> Maybe (SourcePackageDb) + -> [TargetSelector] + -> Either [TargetProblem err] TargetsMap +resolveTargetsFromSolver selectPackageTargets selectComponentTarget installPlan sourceDb targetSelectors = + resolveTargets + selectPackageTargets + selectComponentTarget + (availableTargetIndexes installPlan) + sourceDb + targetSelectors + +-- | Resolve targets from local packages. +-- +-- This is a convenience wrapper around 'resolveTargets' that takes a list of +-- 'PackageSpecifier's directly, rather than requiring the caller to +-- construct the 'AvailableTargetIndexes' first. +resolveTargetsFromLocalPackages + :: forall err + . ( forall k + . TargetSelector + -> [AvailableTarget k] + -> Either (TargetProblem err) [k] + ) + -> ( forall k + . SubComponentTarget + -> AvailableTarget k + -> Either (TargetProblem err) k + ) + -> [PackageSpecifier UnresolvedSourcePackage] + -> [TargetSelector] + -> Either [TargetProblem err] (TargetsMapX PackageId) +resolveTargetsFromLocalPackages selectPackageTargets selectComponentTarget pkgSpecifiers targetSelectors = + resolveTargets + selectPackageTargets + selectComponentTarget + (availableTargetIndexesFromSourcePackages pkgSpecifiers) + Nothing + targetSelectors + -- | Given a set of 'TargetSelector's, resolve which 'UnitId's and -- 'ComponentTarget's they ought to refer to. -- @@ -606,8 +669,9 @@ uniqueTargetSelectors = ordNub . allTargetSelectors -- this commands can use 'selectComponentTargetBasic', either directly or as -- a basis for their own @selectComponentTarget@ implementation. resolveTargets - :: forall err - . ( forall k + :: forall u err + . Ord u + => ( forall k . TargetSelector -> [AvailableTarget k] -> Either (TargetProblem err) [k] @@ -617,14 +681,14 @@ resolveTargets -> AvailableTarget k -> Either (TargetProblem err) k ) - -> ElaboratedInstallPlan + -> AvailableTargetIndexes u -> Maybe (SourcePackageDb) -> [TargetSelector] - -> Either [TargetProblem err] TargetsMap + -> Either [TargetProblem err] (TargetsMapX u) resolveTargets selectPackageTargets selectComponentTarget - installPlan + AvailableTargetIndexes{..} mPkgDb = fmap mkTargetsMap . either (Left . toList) Right @@ -632,8 +696,8 @@ resolveTargets . map (\ts -> (,) ts <$> checkTarget ts) where mkTargetsMap - :: [(TargetSelector, [(UnitId, ComponentTarget)])] - -> TargetsMap + :: [(TargetSelector, [(u, ComponentTarget)])] + -> TargetsMapX u mkTargetsMap targets = Map.map nubComponentTargets $ Map.fromListWith @@ -643,9 +707,7 @@ resolveTargets , (uid, ct) <- cts ] - AvailableTargetIndexes{..} = availableTargetIndexes installPlan - - checkTarget :: TargetSelector -> Either (TargetProblem err) [(UnitId, ComponentTarget)] + checkTarget :: TargetSelector -> Either (TargetProblem err) [(u, ComponentTarget)] -- We can ask to build any whole package, project-local or a dependency checkTarget bt@(TargetPackage _ (ordNub -> [pkgid]) mkfilter) @@ -737,19 +799,19 @@ resolveTargets (\(es, xs) -> case es of [] -> Right xs; (e : es') -> Left (e :| es')) . partitionEithers -data AvailableTargetIndexes = AvailableTargetIndexes +data AvailableTargetIndexes u = AvailableTargetIndexes { availableTargetsByPackageIdAndComponentName - :: AvailableTargetsMap (PackageId, ComponentName) + :: AvailableTargetsMap (PackageId, ComponentName) u , availableTargetsByPackageId - :: AvailableTargetsMap PackageId + :: AvailableTargetsMap PackageId u , availableTargetsByPackageName - :: AvailableTargetsMap PackageName + :: AvailableTargetsMap PackageName u , availableTargetsByPackageNameAndComponentName - :: AvailableTargetsMap (PackageName, ComponentName) + :: AvailableTargetsMap (PackageName, ComponentName) u , availableTargetsByPackageNameAndUnqualComponentName - :: AvailableTargetsMap (PackageName, UnqualComponentName) + :: AvailableTargetsMap (PackageName, UnqualComponentName) u } -type AvailableTargetsMap k = Map k [AvailableTarget (UnitId, ComponentName)] +type AvailableTargetsMap k u = Map k [AvailableTarget (u, ComponentName)] -- We define a bunch of indexes to help 'resolveTargets' with resolving -- 'TargetSelector's to specific 'UnitId's. @@ -760,7 +822,7 @@ type AvailableTargetsMap k = Map k [AvailableTarget (UnitId, ComponentName)] -- -- They are all constructed lazily because they are not necessarily all used. -- -availableTargetIndexes :: ElaboratedInstallPlan -> AvailableTargetIndexes +availableTargetIndexes :: ElaboratedInstallPlan -> AvailableTargetIndexes UnitId availableTargetIndexes installPlan = AvailableTargetIndexes{..} where availableTargetsByPackageIdAndComponentName @@ -833,6 +895,86 @@ availableTargetIndexes installPlan = AvailableTargetIndexes{..} ElabPackage _ -> null (pkgComponents (elabPkgDescription pkg)) ] +-- | Create available target indexes from source packages. +-- +-- This is useful when we need to resolve targets before solver resolution. +availableTargetIndexesFromSourcePackages + :: [PackageSpecifier UnresolvedSourcePackage] -> AvailableTargetIndexes PackageId +availableTargetIndexesFromSourcePackages pkgSpecifiers = AvailableTargetIndexes{..} + where + -- Create a map of available targets from source packages + availableTargetsByPackageIdAndComponentName + :: Map (PackageId, ComponentName) [AvailableTarget (PackageId, ComponentName)] + availableTargetsByPackageIdAndComponentName = + Map.fromListWith (++) $ + concat + [ [ ((pkgId, cname), [makeAvailableTarget pkgId cname]) + | let pkgId = packageId pkg + , cname <- map componentName (pkgComponents $ flattenPackageDescription (srcpkgDescription pkg)) + ] + | SpecificSourcePackage pkg <- pkgSpecifiers + ] + + -- Helper to create an available target + makeAvailableTarget pkgId cname = + AvailableTarget + { availableTargetPackageId = pkgId + , availableTargetComponentName = cname + , availableTargetStatus = TargetBuildable (pkgId, cname) TargetRequestedByDefault + , availableTargetLocalToProject = True + } + + -- Derive other indexes from the main one, just like in availableTargetIndexes + availableTargetsByPackageId + :: Map PackageId [AvailableTarget (PackageId, ComponentName)] + availableTargetsByPackageId = + Map.mapKeysWith + (++) + (\(pkgid, _cname) -> pkgid) + availableTargetsByPackageIdAndComponentName + `Map.union` availableTargetsEmptyPackages + + -- Handle empty packages + availableTargetsEmptyPackages = + Map.fromList + [ (packageId pkg, []) + | SpecificSourcePackage pkg <- pkgSpecifiers + , null (pkgComponents (flattenPackageDescription (srcpkgDescription pkg))) + ] + + availableTargetsByPackageName + :: Map PackageName [AvailableTarget (PackageId, ComponentName)] + availableTargetsByPackageName = + Map.mapKeysWith + (++) + packageName + availableTargetsByPackageId + + availableTargetsByPackageNameAndComponentName + :: Map (PackageName, ComponentName) [AvailableTarget (PackageId, ComponentName)] + availableTargetsByPackageNameAndComponentName = + Map.mapKeysWith + (++) + (\(pkgid, cname) -> (packageName pkgid, cname)) + availableTargetsByPackageIdAndComponentName + + availableTargetsByPackageNameAndUnqualComponentName + :: Map (PackageName, UnqualComponentName) [AvailableTarget (PackageId, ComponentName)] + availableTargetsByPackageNameAndUnqualComponentName = + Map.mapKeysWith + (++) + ( \(pkgid, cname) -> + let pname = packageName pkgid + cname' = unqualComponentName pname cname + in (pname, cname') + ) + availableTargetsByPackageIdAndComponentName + where + unqualComponentName :: PackageName -> ComponentName -> UnqualComponentName + unqualComponentName pkgname = + fromMaybe (packageNameToUnqualComponentName pkgname) + . componentNameString + -- TODO: [research required] what if the solution has multiple -- versions of this package? -- e.g. due to setup deps or due to multiple independent sets diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs index 7ee5cb52f41..05aefecc77f 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs @@ -57,6 +57,7 @@ module Distribution.Client.ProjectPlanning.Types , isTestComponentTarget , isBenchComponentTarget , componentOptionalStanza + , componentTargetName -- * Setup script , SetupScriptStyle (..) @@ -861,6 +862,10 @@ data ComponentTarget = ComponentTarget ComponentName SubComponentTarget instance Binary ComponentTarget instance Structured ComponentTarget +-- | Extract the component name from a 'ComponentTarget'. +componentTargetName :: ComponentTarget -> ComponentName +componentTargetName (ComponentTarget cname _) = cname + -- | Unambiguously render a 'ComponentTarget', e.g., to pass -- to a Cabal Setup script. showComponentTarget :: PackageId -> ComponentTarget -> String diff --git a/cabal-install/src/Distribution/Client/ScriptUtils.hs b/cabal-install/src/Distribution/Client/ScriptUtils.hs index 440de3c84ad..177a5d719bb 100644 --- a/cabal-install/src/Distribution/Client/ScriptUtils.hs +++ b/cabal-install/src/Distribution/Client/ScriptUtils.hs @@ -317,13 +317,13 @@ withContextAndSelectors noTargets kind flags@NixStyleFlags{..} targetStrings glo Left (TargetSelectorNoTargetsInCwd{} : _) | [] <- targetStrings , AcceptNoTargets <- noTargets -> - return (tc, ctx, defaultTarget) + return (tc, ctx, []) Left err@(TargetSelectorNoTargetsInProject : _) -- If there are no target selectors and no targets are fine, return -- the context | [] <- targetStrings , AcceptNoTargets <- noTargets -> - return (tc, ctx, defaultTarget) + return (tc, ctx, []) | (script : _) <- targetStrings -> scriptOrError script err Left err@(TargetSelectorNoSuch t _ : _) | TargetString1 script <- t -> scriptOrError script err diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs index 2b25a64b6be..bb697e47239 100644 --- a/cabal-install/tests/IntegrationTests2.hs +++ b/cabal-install/tests/IntegrationTests2.hs @@ -17,7 +17,7 @@ import Distribution.Client.ProjectBuilding import Distribution.Client.ProjectConfig import Distribution.Client.ProjectOrchestration ( distinctTargetComponents - , resolveTargets + , resolveTargetsFromSolver ) import Distribution.Client.ProjectPlanning import Distribution.Client.ProjectPlanning.Types @@ -1751,7 +1751,7 @@ assertProjectDistinctTargets ++ show results where results = - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan @@ -1801,7 +1801,7 @@ assertTargetProblems elaboratedPlan selectPackageTargets selectComponentTarget = where assertTargetProblem expected targetSelector = let res = - resolveTargets + resolveTargetsFromSolver selectPackageTargets selectComponentTarget elaboratedPlan @@ -1812,7 +1812,7 @@ assertTargetProblems elaboratedPlan selectPackageTargets selectComponentTarget = problem @?= expected targetSelector unexpected -> assertFailure $ - "expected resolveTargets result: (Left [problem]) " + "expected resolveTargetsFromSolver result: (Left [problem]) " ++ "but got: " ++ show unexpected diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.project b/cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.project new file mode 100644 index 00000000000..85abdb2be1e --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.project @@ -0,0 +1,4 @@ +packages: package-a + package-b + +constraints: base == 3.0.3.2 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.test.hs b/cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.test.hs new file mode 100644 index 00000000000..65b93399596 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/cabal.test.hs @@ -0,0 +1,27 @@ +import Test.Cabal.Prelude +import System.Exit(ExitCode(..)) + + +main = cabalTest $ withRepo "repo" $ do + -- Test 1: Run from project root with no target specifier + result1 <- cabal' "outdated" ["all"] + assertOutputContains "package-a" result1 + assertOutputContains "package-b" result1 + + -- Test 2: Run from project root with specific package target + result2 <- cabal' "v2-outdated" ["package-a"] + assertOutputContains "package-a" result2 + assertOutputDoesNotContain "package-b" result2 + + -- Test 3: Run from project root with --simple-output flag + result3 <- cabal' "v2-outdated" ["all", "--simple-output"] + assertOutputContains "base" result3 + assertOutputContains "template-haskell" result3 + + -- Test 4: Run from subdirectory + result4 <- cabal' "v2-outdated" ["--project-context"] + assertOutputContains "base" result4 + + -- Test 6: Test exit code behavior + result6 <- fails $ cabal' "v2-outdated" ["all", "--exit-code"] + assertExitCode (ExitFailure 1) result6 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/LICENSE b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/LICENSE new file mode 100644 index 00000000000..28dc5630888 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2023, Cabal Test Suite + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Cabal Test Suite nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/package-a.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/package-a.cabal new file mode 100644 index 00000000000..edc1301f76b --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/package-a.cabal @@ -0,0 +1,24 @@ +cabal-version: 2.4 +name: package-a +version: 0.1.0.0 +synopsis: Package A for outdated command testing +license: BSD-3-Clause +license-file: LICENSE +author: Cabal Test Suite +maintainer: cabal-dev@haskell.org +build-type: Simple + +flag foo + description: test flag + +library + exposed-modules: ModuleA + build-depends: base >=3.2 && <3.3, + binary == 0.8.6.0 + if flag(foo) + build-depends: template-haskell == 2.3.0.* + else + build-depends: template-haskell == 2.3.* + + hs-source-dirs: src + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/src/ModuleA.hs b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/src/ModuleA.hs new file mode 100644 index 00000000000..c82668f00b1 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-a/src/ModuleA.hs @@ -0,0 +1,4 @@ +module ModuleA where + +simpleFunction :: Int -> Int +simpleFunction x = x + 1 \ No newline at end of file diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/LICENSE b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/LICENSE new file mode 100644 index 00000000000..28dc5630888 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2023, Cabal Test Suite + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Cabal Test Suite nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/package-b.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/package-b.cabal new file mode 100644 index 00000000000..619bca806cb --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/package-b.cabal @@ -0,0 +1,17 @@ +cabal-version: 2.4 +name: package-b +version: 0.1.0.0 +synopsis: Package B for outdated command testing +license: BSD-3-Clause +license-file: LICENSE +author: Cabal Test Suite +maintainer: cabal-dev@haskell.org +build-type: Simple + +library + exposed-modules: ModuleB + build-depends: base >=3.0 && < 3.5, + package-a, + binary >=0.8 && <0.8.7 + hs-source-dirs: src + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/src/ModuleB.hs b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/src/ModuleB.hs new file mode 100644 index 00000000000..161f49352d0 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/package-b/src/ModuleB.hs @@ -0,0 +1,6 @@ +module ModuleB where + +import ModuleA + +anotherFunction :: Int -> Int +anotherFunction x = simpleFunction (x * 2) \ No newline at end of file diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.1/base.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.1/base.cabal new file mode 100644 index 00000000000..3c7fb7a3000 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.1/base.cabal @@ -0,0 +1,154 @@ +name: base +version: 3.0.3.1 +license: BSD3 +license-file: LICENSE +maintainer: libraries@haskell.org +synopsis: Basic libraries (backwards-compatibility version) +description: + This is a backwards-compatible version of the base package. + It depends on a later version of base, and was probably supplied + with your compiler when it was installed. + +cabal-version: >=1.2 +build-type: Simple + +Library { + build-depends: base >= 4.0 && < 4.2, + syb >= 0.1 && < 0.2 + extensions: PackageImports + + if impl(ghc < 6.9) { + buildable: False + } + + if impl(ghc) { + exposed-modules: + Data.Generics, + Data.Generics.Aliases, + Data.Generics.Basics, + Data.Generics.Instances, + Data.Generics.Schemes, + Data.Generics.Text, + Data.Generics.Twins, + Foreign.Concurrent, + GHC.Arr, + GHC.Base, + GHC.Conc, + GHC.ConsoleHandler, + GHC.Desugar, + GHC.Dotnet, + GHC.Enum, + GHC.Environment, + GHC.Err, + GHC.Exception, + GHC.Exts, + GHC.Float, + GHC.ForeignPtr, + GHC.Handle, + GHC.IO, + GHC.IOBase, + GHC.Int, + GHC.List, + GHC.Num, + GHC.PArr, + GHC.Pack, + GHC.Ptr, + GHC.Read, + GHC.Real, + GHC.ST, + GHC.STRef, + GHC.Show, + GHC.Stable, + GHC.Storable, + GHC.TopHandler, + GHC.Unicode, + GHC.Weak, + GHC.Word, + System.Timeout + } + exposed-modules: + Control.Applicative, + Control.Arrow, + Control.Category, + Control.Concurrent, + Control.Concurrent.Chan, + Control.Concurrent.MVar, + Control.Concurrent.QSem, + Control.Concurrent.QSemN, + Control.Concurrent.SampleVar, + Control.Exception, + Control.Monad, + Control.Monad.Fix, + Control.Monad.Instances, + Control.Monad.ST, + Control.Monad.ST.Lazy, + Control.Monad.ST.Strict, + Data.Bits, + Data.Bool, + Data.Char, + Data.Complex, + Data.Dynamic, + Data.Either, + Data.Eq, + Data.Fixed, + Data.Foldable + Data.Function, + Data.HashTable, + Data.IORef, + Data.Int, + Data.Ix, + Data.List, + Data.Maybe, + Data.Monoid, + Data.Ord, + Data.Ratio, + Data.STRef, + Data.STRef.Lazy, + Data.STRef.Strict, + Data.String, + Data.Traversable + Data.Tuple, + Data.Typeable, + Data.Unique, + Data.Version, + Data.Word, + Debug.Trace, + Foreign, + Foreign.C, + Foreign.C.Error, + Foreign.C.String, + Foreign.C.Types, + Foreign.ForeignPtr, + Foreign.Marshal, + Foreign.Marshal.Alloc, + Foreign.Marshal.Array, + Foreign.Marshal.Error, + Foreign.Marshal.Pool, + Foreign.Marshal.Utils, + Foreign.Ptr, + Foreign.StablePtr, + Foreign.Storable, + Numeric, + Prelude, + System.Console.GetOpt, + System.CPUTime, + System.Environment, + System.Exit, + System.IO, + System.IO.Error, + System.IO.Unsafe, + System.Info, + System.Mem, + System.Mem.StableName, + System.Mem.Weak, + System.Posix.Internals, + System.Posix.Types, + Text.ParserCombinators.ReadP, + Text.ParserCombinators.ReadPrec, + Text.Printf, + Text.Read, + Text.Read.Lex, + Text.Show, + Text.Show.Functions + Unsafe.Coerce +} diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.2/base.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.2/base.cabal new file mode 100644 index 00000000000..1edd58ea0ca --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-3.0.3.2/base.cabal @@ -0,0 +1,160 @@ +name: base +version: 3.0.3.2 +license: BSD3 +license-file: LICENSE +maintainer: libraries@haskell.org +bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries/base +synopsis: Basic libraries (backwards-compatibility version) +description: + This is a backwards-compatible version of the base package. + It depends on a later version of base, and was probably supplied + with your compiler when it was installed. + +cabal-version: >=1.6 +build-type: Simple + +source-repository head + type: darcs + location: http://darcs.haskell.org/packages/base3-compat + +Library { + build-depends: base >= 4.0 && < 4.3, + syb >= 0.1 && < 0.2 + extensions: PackageImports,CPP + ghc-options: -fno-warn-deprecations + + if impl(ghc < 6.9) { + buildable: False + } + + if impl(ghc) { + exposed-modules: + Data.Generics, + Data.Generics.Aliases, + Data.Generics.Basics, + Data.Generics.Instances, + Data.Generics.Schemes, + Data.Generics.Text, + Data.Generics.Twins, + Foreign.Concurrent, + GHC.Arr, + GHC.Base, + GHC.Conc, + GHC.ConsoleHandler, + GHC.Desugar, + GHC.Dotnet, + GHC.Enum, + GHC.Environment, + GHC.Err, + GHC.Exception, + GHC.Exts, + GHC.Float, + GHC.ForeignPtr, + GHC.Handle, + GHC.IO, + GHC.IOBase, + GHC.Int, + GHC.List, + GHC.Num, + GHC.PArr, + GHC.Pack, + GHC.Ptr, + GHC.Read, + GHC.Real, + GHC.ST, + GHC.STRef, + GHC.Show, + GHC.Stable, + GHC.Storable, + GHC.TopHandler, + GHC.Unicode, + GHC.Weak, + GHC.Word, + System.Timeout + } + exposed-modules: + Control.Applicative, + Control.Arrow, + Control.Category, + Control.Concurrent, + Control.Concurrent.Chan, + Control.Concurrent.MVar, + Control.Concurrent.QSem, + Control.Concurrent.QSemN, + Control.Concurrent.SampleVar, + Control.Exception, + Control.Monad, + Control.Monad.Fix, + Control.Monad.Instances, + Control.Monad.ST, + Control.Monad.ST.Lazy, + Control.Monad.ST.Strict, + Data.Bits, + Data.Bool, + Data.Char, + Data.Complex, + Data.Dynamic, + Data.Either, + Data.Eq, + Data.Fixed, + Data.Foldable + Data.Function, + Data.HashTable, + Data.IORef, + Data.Int, + Data.Ix, + Data.List, + Data.Maybe, + Data.Monoid, + Data.Ord, + Data.Ratio, + Data.STRef, + Data.STRef.Lazy, + Data.STRef.Strict, + Data.String, + Data.Traversable + Data.Tuple, + Data.Typeable, + Data.Unique, + Data.Version, + Data.Word, + Debug.Trace, + Foreign, + Foreign.C, + Foreign.C.Error, + Foreign.C.String, + Foreign.C.Types, + Foreign.ForeignPtr, + Foreign.Marshal, + Foreign.Marshal.Alloc, + Foreign.Marshal.Array, + Foreign.Marshal.Error, + Foreign.Marshal.Pool, + Foreign.Marshal.Utils, + Foreign.Ptr, + Foreign.StablePtr, + Foreign.Storable, + Numeric, + Prelude, + System.Console.GetOpt, + System.CPUTime, + System.Environment, + System.Exit, + System.IO, + System.IO.Error, + System.IO.Unsafe, + System.Info, + System.Mem, + System.Mem.StableName, + System.Mem.Weak, + System.Posix.Internals, + System.Posix.Types, + Text.ParserCombinators.ReadP, + Text.ParserCombinators.ReadPrec, + Text.Printf, + Text.Read, + Text.Read.Lex, + Text.Show, + Text.Show.Functions + Unsafe.Coerce +} diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-4.0.0.0/base.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-4.0.0.0/base.cabal new file mode 100644 index 00000000000..1aa83333731 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/base-4.0.0.0/base.cabal @@ -0,0 +1,174 @@ +name: base +version: 4.0.0.0 +license: BSD3 +license-file: LICENSE +maintainer: libraries@haskell.org +synopsis: Basic libraries +description: + This package contains the Prelude and its support libraries, + and a large collection of useful libraries ranging from data + structures to parsing combinators and debugging utilities. +cabal-version: >= 1.2.3 +build-type: Configure +extra-tmp-files: + config.log config.status autom4te.cache + include/HsBaseConfig.h +extra-source-files: + config.guess config.sub install-sh + aclocal.m4 configure.ac configure + include/CTypes.h + +Library { + if impl(ghc) { + build-depends: rts, ghc-prim, integer + exposed-modules: + Foreign.Concurrent, + GHC.Arr, + GHC.Base, + GHC.Classes, + GHC.Conc, + GHC.ConsoleHandler, + GHC.Desugar, + GHC.Enum, + GHC.Environment, + GHC.Err, + GHC.Exception, + GHC.Exts, + GHC.Float, + GHC.ForeignPtr, + GHC.Handle, + GHC.IO, + GHC.IOBase, + GHC.Int, + GHC.List, + GHC.Num, + GHC.PArr, + GHC.Pack, + GHC.Ptr, + GHC.Read, + GHC.Real, + GHC.ST, + GHC.STRef, + GHC.Show, + GHC.Stable, + GHC.Storable, + GHC.TopHandler, + GHC.Unicode, + GHC.Weak, + GHC.Word, + System.Timeout + extensions: MagicHash, ExistentialQuantification, Rank2Types, + ScopedTypeVariables, UnboxedTuples, + ForeignFunctionInterface, UnliftedFFITypes, + DeriveDataTypeable, GeneralizedNewtypeDeriving, + FlexibleInstances, PatternSignatures, StandaloneDeriving, + PatternGuards, EmptyDataDecls + } + exposed-modules: + Control.Applicative, + Control.Arrow, + Control.Category, + Control.Concurrent, + Control.Concurrent.Chan, + Control.Concurrent.MVar, + Control.Concurrent.QSem, + Control.Concurrent.QSemN, + Control.Concurrent.SampleVar, + Control.Exception, + Control.Exception.Base + Control.OldException, + Control.Monad, + Control.Monad.Fix, + Control.Monad.Instances, + Control.Monad.ST + Control.Monad.ST.Lazy + Control.Monad.ST.Strict + Data.Bits, + Data.Bool, + Data.Char, + Data.Complex, + Data.Dynamic, + Data.Either, + Data.Eq, + Data.Data, + Data.Fixed, + Data.Foldable + Data.Function, + Data.HashTable, + Data.IORef, + Data.Int, + Data.Ix, + Data.List, + Data.Maybe, + Data.Monoid, + Data.Ord, + Data.Ratio, + Data.STRef + Data.STRef.Lazy + Data.STRef.Strict + Data.String, + Data.Traversable + Data.Tuple, + Data.Typeable, + Data.Unique, + Data.Version, + Data.Word, + Debug.Trace, + Foreign, + Foreign.C, + Foreign.C.Error, + Foreign.C.String, + Foreign.C.Types, + Foreign.ForeignPtr, + Foreign.Marshal, + Foreign.Marshal.Alloc, + Foreign.Marshal.Array, + Foreign.Marshal.Error, + Foreign.Marshal.Pool, + Foreign.Marshal.Utils, + Foreign.Ptr, + Foreign.StablePtr, + Foreign.Storable, + Numeric, + Prelude, + System.Console.GetOpt + System.CPUTime, + System.Environment, + System.Exit, + System.IO, + System.IO.Error, + System.IO.Unsafe, + System.Info, + System.Mem, + System.Mem.StableName, + System.Mem.Weak, + System.Posix.Internals, + System.Posix.Types, + Text.ParserCombinators.ReadP, + Text.ParserCombinators.ReadPrec, + Text.Printf, + Text.Read, + Text.Read.Lex, + Text.Show, + Text.Show.Functions + Unsafe.Coerce + c-sources: + cbits/PrelIOUtils.c + cbits/WCsubst.c + cbits/Win32Utils.c + cbits/consUtils.c + cbits/dirUtils.c + cbits/inputReady.c + cbits/selectUtils.c + include-dirs: include + includes: HsBase.h + install-includes: HsBase.h HsBaseConfig.h WCsubst.h dirUtils.h consUtils.h Typeable.h + if os(windows) { + extra-libraries: wsock32, msvcrt, kernel32, user32, shell32 + } + extensions: CPP + -- We need to set the package name to base (without a version number) + -- as it's magic. + ghc-options: -package-name base + nhc98-options: -H4M -K3M +} diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.5.0/binary.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.5.0/binary.cabal new file mode 100644 index 00000000000..b0ab2b24ec7 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.5.0/binary.cabal @@ -0,0 +1,35 @@ +name: binary +version: 0.8.5.0 +license: BSD3 +license-file: LICENSE +author: Lennart Kolmodin +maintainer: Lennart Kolmodin, Don Stewart +homepage: https://github.com/kolmodin/binary +description: Efficient, pure binary serialisation using lazy ByteStrings. + Haskell values may be encoded to and from binary formats, + written to disk as binary, or sent over the network. + The format used can be automatically generated, or + you can choose to implement a custom format if needed. + Serialisation speeds of over 1 G\/sec have been observed, + so this library should be suitable for high performance + scenarios. +synopsis: Binary serialisation for Haskell values using lazy ByteStrings +category: Data, Parsing +stability: provisional +build-type: Simple +cabal-version: >= 1.8 + +library + build-depends: base >= 3 && < 5, bytestring >= 0.8.4, containers, array + hs-source-dirs: src + exposed-modules: Data.Binary, + Data.Binary.Put, + Data.Binary.Get, + Data.Binary.Get.Internal, + Data.Binary.Builder + + other-modules: Data.Binary.Class, + Data.Binary.Internal, + Data.Binary.Generic, + Data.Binary.FloatCast + ghc-options: -O2 -Wall -fliberate-case-threshold=1000 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.6.0/binary.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.6.0/binary.cabal new file mode 100644 index 00000000000..caa10cb2230 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.8.6.0/binary.cabal @@ -0,0 +1,35 @@ +name: binary +version: 0.8.6.0 +license: BSD3 +license-file: LICENSE +author: Lennart Kolmodin +maintainer: Lennart Kolmodin, Don Stewart +homepage: https://github.com/kolmodin/binary +description: Efficient, pure binary serialisation using lazy ByteStrings. + Haskell values may be encoded to and from binary formats, + written to disk as binary, or sent over the network. + The format used can be automatically generated, or + you can choose to implement a custom format if needed. + Serialisation speeds of over 1 G\/sec have been observed, + so this library should be suitable for high performance + scenarios. +synopsis: Binary serialisation for Haskell values using lazy ByteStrings +category: Data, Parsing +stability: provisional +build-type: Simple +cabal-version: >= 1.8 + +library + build-depends: base >= 3 && < 5, bytestring >= 0.8.4, containers, array + hs-source-dirs: src + exposed-modules: Data.Binary, + Data.Binary.Put, + Data.Binary.Get, + Data.Binary.Get.Internal, + Data.Binary.Builder + + other-modules: Data.Binary.Class, + Data.Binary.Internal, + Data.Binary.Generic, + Data.Binary.FloatCast + ghc-options: -O2 -Wall -fliberate-case-threshold=1000 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.9.0.0/binary.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.9.0.0/binary.cabal new file mode 100644 index 00000000000..66485f6f767 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/binary-0.9.0.0/binary.cabal @@ -0,0 +1,35 @@ +name: binary +version: 0.9.0.0 +license: BSD3 +license-file: LICENSE +author: Lennart Kolmodin +maintainer: Lennart Kolmodin, Don Stewart +homepage: https://github.com/kolmodin/binary +description: Efficient, pure binary serialisation using lazy ByteStrings. + Haskell values may be encoded to and from binary formats, + written to disk as binary, or sent over the network. + The format used can be automatically generated, or + you can choose to implement a custom format if needed. + Serialisation speeds of over 1 G\/sec have been observed, + so this library should be suitable for high performance + scenarios. +synopsis: Binary serialisation for Haskell values using lazy ByteStrings +category: Data, Parsing +stability: provisional +build-type: Simple +cabal-version: >= 1.8 + +library + build-depends: base >= 3 && < 5, bytestring >= 0.8.4, containers, array + hs-source-dirs: src + exposed-modules: Data.Binary, + Data.Binary.Put, + Data.Binary.Get, + Data.Binary.Get.Internal, + Data.Binary.Builder + + other-modules: Data.Binary.Class, + Data.Binary.Internal, + Data.Binary.Generic, + Data.Binary.FloatCast + ghc-options: -O2 -Wall -fliberate-case-threshold=1000 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/preferred-versions b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/preferred-versions new file mode 100644 index 00000000000..5be791d3d0a --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/preferred-versions @@ -0,0 +1 @@ +binary <0.9.0.0 || >0.9.0.0 diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.0/template-haskell.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.0/template-haskell.cabal new file mode 100644 index 00000000000..064cb9d51bd --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.0/template-haskell.cabal @@ -0,0 +1,22 @@ +name: template-haskell +version: 2.3.0.0 +x-revision: 1 +license: BSD3 +license-file: LICENSE +maintainer: libraries@haskell.org +description: + Facilities for manipulating Haskell source code using Template Haskell. +build-type: Simple +build-depends: base<4.3, pretty, packedstring, containers +exposed-modules: + Language.Haskell.TH.Syntax, + Language.Haskell.TH.PprLib, + Language.Haskell.TH.Ppr, + Language.Haskell.TH.Lib, + Language.Haskell.TH.Quote, + Language.Haskell.TH +extensions: MagicHash, PatternGuards, PolymorphicComponents, + DeriveDataTypeable, TypeSynonymInstances +-- We need to set the package name to template-haskell (without a +-- version number) as it's magic. +ghc-options: -package-name template-haskell diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.1/template-haskell.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.1/template-haskell.cabal new file mode 100644 index 00000000000..8fbe6b269ab --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.3.0.1/template-haskell.cabal @@ -0,0 +1,22 @@ +name: template-haskell +version: 2.3.0.1 +x-revision: 1 +license: BSD3 +license-file: LICENSE +maintainer: libraries@haskell.org +description: + Facilities for manipulating Haskell source code using Template Haskell. +build-type: Simple +build-depends: base <4.3, pretty, packedstring, containers +exposed-modules: + Language.Haskell.TH.Syntax, + Language.Haskell.TH.PprLib, + Language.Haskell.TH.Ppr, + Language.Haskell.TH.Lib, + Language.Haskell.TH.Quote, + Language.Haskell.TH +extensions: MagicHash, PatternGuards, PolymorphicComponents, + DeriveDataTypeable, TypeSynonymInstances +-- We need to set the package name to template-haskell (without a +-- version number) as it's magic. +ghc-options: -package-name template-haskell diff --git a/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.4.0.0/template-haskell.cabal b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.4.0.0/template-haskell.cabal new file mode 100644 index 00000000000..4c5ee3cf3a3 --- /dev/null +++ b/cabal-testsuite/PackageTests/Outdated/Issue8283/repo/template-haskell-2.4.0.0/template-haskell.cabal @@ -0,0 +1,32 @@ +name: template-haskell +version: 2.4.0.0 +x-revision: 1 +license: BSD3 +license-file: LICENSE +maintainer: libraries@haskell.org +bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=Template%20Haskell +description: + Facilities for manipulating Haskell source code using Template Haskell. +build-type: Simple +Cabal-Version: >= 1.6 + +Library + build-depends: base >= 3 && < 4.3, + pretty, containers + exposed-modules: + Language.Haskell.TH.Syntax.Internals + Language.Haskell.TH.Syntax + Language.Haskell.TH.PprLib + Language.Haskell.TH.Ppr + Language.Haskell.TH.Lib + Language.Haskell.TH.Quote + Language.Haskell.TH + extensions: MagicHash, PatternGuards, PolymorphicComponents, + DeriveDataTypeable, TypeSynonymInstances + -- We need to set the package name to template-haskell (without a + -- version number) as it's magic. + ghc-options: -package-name template-haskell + +source-repository head + type: darcs + location: http://darcs.haskell.org/packages/template-haskell/ diff --git a/cabal-testsuite/PackageTests/Outdated/outdated_freeze.out b/cabal-testsuite/PackageTests/Outdated/outdated_freeze.out index d417b8dfe7f..284422bc320 100644 --- a/cabal-testsuite/PackageTests/Outdated/outdated_freeze.out +++ b/cabal-testsuite/PackageTests/Outdated/outdated_freeze.out @@ -1,29 +1,46 @@ # cabal v2-update Downloading the latest package list from test-local-repo # cabal outdated +Configuration is affected by the following files: +- cabal.project +- cabal.project.freeze Outdated dependencies: -base ==3.0.3.2 (latest: 4.0.0.0) -template-haskell ==2.3.0.0 (latest: 2.4.0.0) -binary ==0.8.5.0 (latest: 0.8.6.0) +base ==3.0.3.2 (latest: 4.0.0.0, project config cabal.project.freeze) +template-haskell ==2.3.0.0 (latest: 2.4.0.0, project config cabal.project.freeze) +binary ==0.8.5.0 (latest: 0.8.6.0, project config cabal.project.freeze) # cabal outdated +Configuration is affected by the following files: +- cabal.project +- cabal.project.freeze All dependencies are up to date. # cabal outdated +Configuration is affected by the following files: +- cabal.project +- cabal.project.freeze Outdated dependencies: -template-haskell ==2.3.0.0 (latest: 2.3.0.1) -binary ==0.8.5.0 (latest: 0.8.6.0) +template-haskell ==2.3.0.0 (latest: 2.3.0.1, project config cabal.project.freeze) +binary ==0.8.5.0 (latest: 0.8.6.0, project config cabal.project.freeze) # cabal outdated +Configuration is affected by the following files: +- cabal.project +- cabal.project.freeze Outdated dependencies: -base ==3.0.3.2 (latest: 4.0.0.0) -template-haskell ==2.3.0.0 (latest: 2.4.0.0) -binary ==0.8.5.0 (latest: 0.8.6.0) +base ==3.0.3.2 (latest: 4.0.0.0, user config /cabal.config) +template-haskell ==2.3.0.0 (latest: 2.4.0.0, user config /cabal.config) +binary ==0.8.5.0 (latest: 0.8.6.0, user config /cabal.config) # cabal outdated +Configuration is affected by the following files: +- cabal.project +- cabal.project.freeze All dependencies are up to date. # cabal outdated +Configuration is affected by the following files: +- cabal.project +- cabal.project.freeze Outdated dependencies: -template-haskell ==2.3.0.0 (latest: 2.3.0.1) -binary ==0.8.5.0 (latest: 0.8.6.0) +template-haskell ==2.3.0.0 (latest: 2.3.0.1, user config /cabal.config) +binary ==0.8.5.0 (latest: 0.8.6.0, user config /cabal.config) # cabal outdated -Error: [Cabal-7104] -Couldn't find a freeze file expected at: /cabal.project.missing.freeze.freeze - -We are looking for this file because you supplied '--project-file' or '--v2-freeze-file'. When one of these flags is given, we try to read the dependencies from a freeze file. If it is undesired behaviour, you should not use these flags, otherwise please generate a freeze file via 'cabal freeze'. +Configuration is affected by the following files: +- cabal.project.missing.freeze +All dependencies are up to date. diff --git a/cabal-testsuite/PackageTests/Outdated/outdated_freeze.test.hs b/cabal-testsuite/PackageTests/Outdated/outdated_freeze.test.hs index 35756590ea8..175b74d7b3c 100644 --- a/cabal-testsuite/PackageTests/Outdated/outdated_freeze.test.hs +++ b/cabal-testsuite/PackageTests/Outdated/outdated_freeze.test.hs @@ -19,5 +19,5 @@ main = cabalTest $ withRepo "repo" $ do assertOutputContains "template-haskell" out assertOutputContains "binary" out) - fails $ cabal' "outdated" ["--project-file=cabal.project.missing.freeze", "--v2-freeze-file"] + cabal' "outdated" ["--project-file=cabal.project.missing.freeze", "--v2-freeze-file"] return ()