From 24b37c7c35f4ead8436e0997ecf474d5d3bdf1f9 Mon Sep 17 00:00:00 2001 From: Musab Guma'a Date: Sun, 19 Jun 2022 19:38:19 +0000 Subject: [PATCH] pretty-print run targets on failure --- .../src/Distribution/Types/ComponentName.hs | 43 ++++++++++++++----- .../Distribution/Client/CmdErrorMessages.hs | 7 +++ .../src/Distribution/Client/CmdRun.hs | 23 +++++----- .../NewBuild/CmdRun/MultipleExes/cabal.out | 6 ++- .../CmdRun/MultiplePackages/cabal.out | 3 +- changelog.d/issue-8189 | 11 +++++ 6 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 changelog.d/issue-8189 diff --git a/Cabal-syntax/src/Distribution/Types/ComponentName.hs b/Cabal-syntax/src/Distribution/Types/ComponentName.hs index 9b5373b4ddb..0dc0b3137c2 100644 --- a/Cabal-syntax/src/Distribution/Types/ComponentName.hs +++ b/Cabal-syntax/src/Distribution/Types/ComponentName.hs @@ -1,9 +1,11 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE PatternSynonyms #-} module Distribution.Types.ComponentName ( - ComponentName(..), + ComponentName(.., CFLibName, CExeName, CTestName, CBenchName), showComponentName, + componentNameRaw, componentNameStanza, componentNameString, ) where @@ -21,12 +23,32 @@ import qualified Distribution.Compat.CharParsing as P -- Libraries live in a separate namespace, so must distinguish data ComponentName = CLibName LibraryName - | CFLibName UnqualComponentName - | CExeName UnqualComponentName - | CTestName UnqualComponentName - | CBenchName UnqualComponentName + | CNotLibName NotLibComponentName deriving (Eq, Generic, Ord, Read, Show, Typeable) +data NotLibComponentName + = CNLFLibName { toCompName :: UnqualComponentName } + | CNLExeName { toCompName :: UnqualComponentName } + | CNLTestName { toCompName :: UnqualComponentName } + | CNLBenchName { toCompName :: UnqualComponentName } + deriving (Eq, Generic, Ord, Read, Show, Typeable) + +pattern CFLibName :: UnqualComponentName -> ComponentName +pattern CFLibName n = CNotLibName (CNLFLibName n) + +pattern CExeName :: UnqualComponentName -> ComponentName +pattern CExeName n = CNotLibName (CNLExeName n) + +pattern CTestName :: UnqualComponentName -> ComponentName +pattern CTestName n = CNotLibName (CNLTestName n) + +pattern CBenchName :: UnqualComponentName -> ComponentName +pattern CBenchName n = CNotLibName (CNLBenchName n) +{-# COMPLETE CLibName, CFLibName, CExeName, CTestName, CBenchName #-} + +instance Binary NotLibComponentName +instance Structured NotLibComponentName + instance Binary ComponentName instance Structured ComponentName @@ -59,6 +81,10 @@ showComponentName (CExeName name) = "executable '" ++ prettyShow name ++ "'" showComponentName (CTestName name) = "test suite '" ++ prettyShow name ++ "'" showComponentName (CBenchName name) = "benchmark '" ++ prettyShow name ++ "'" +componentNameRaw :: ComponentName -> String +componentNameRaw l@(CLibName _) = showComponentName l +componentNameRaw (CNotLibName x) = prettyShow $ toCompName x + componentNameStanza :: ComponentName -> String componentNameStanza (CLibName lib) = libraryNameStanza lib componentNameStanza (CFLibName name) = "foreign-library " ++ prettyShow name @@ -71,8 +97,5 @@ componentNameStanza (CBenchName name) = "benchmark " ++ prettyShow name -- @Nothing@ if the 'ComponentName' was for the public -- library. componentNameString :: ComponentName -> Maybe UnqualComponentName -componentNameString (CLibName lib) = libraryNameString lib -componentNameString (CFLibName n) = Just n -componentNameString (CExeName n) = Just n -componentNameString (CTestName n) = Just n -componentNameString (CBenchName n) = Just n +componentNameString (CLibName lib) = libraryNameString lib +componentNameString (CNotLibName x) = Just $ toCompName x diff --git a/cabal-install/src/Distribution/Client/CmdErrorMessages.hs b/cabal-install/src/Distribution/Client/CmdErrorMessages.hs index ed80c474e8c..cf41fc4ebac 100644 --- a/cabal-install/src/Distribution/Client/CmdErrorMessages.hs +++ b/cabal-install/src/Distribution/Client/CmdErrorMessages.hs @@ -72,6 +72,13 @@ renderListCommaAnd [x] = x renderListCommaAnd [x,x'] = x ++ " and " ++ x' renderListCommaAnd (x:xs) = x ++ ", " ++ renderListCommaAnd xs +renderListTabular :: [String] -> String +renderListTabular = ("\n"++) . unlines . map ("| * "++) + +renderListPretty :: [String] -> String +renderListPretty xs = if length xs > 5 then renderListTabular xs + else renderListCommaAnd xs + -- | Render a list of things in the style @blah blah; this that; and the other@ renderListSemiAnd :: [String] -> String renderListSemiAnd [] = "" diff --git a/cabal-install/src/Distribution/Client/CmdRun.hs b/cabal-install/src/Distribution/Client/CmdRun.hs index c8e79821179..9bb34b2bfd2 100644 --- a/cabal-install/src/Distribution/Client/CmdRun.hs +++ b/cabal-install/src/Distribution/Client/CmdRun.hs @@ -27,7 +27,8 @@ import Distribution.Client.CmdErrorMessages ( renderTargetSelector, showTargetSelector, renderTargetProblem, renderTargetProblemNoTargets, plural, targetSelectorPluralPkgs, - targetSelectorFilter, renderListCommaAnd ) + targetSelectorFilter, renderListCommaAnd, + renderListPretty ) import Distribution.Client.TargetProblem ( TargetProblem (..) ) @@ -42,11 +43,11 @@ import Distribution.Simple.Flag import Distribution.Simple.Command ( CommandUI(..), usageAlternatives ) import Distribution.Types.ComponentName - ( showComponentName ) + ( componentNameRaw ) import Distribution.Verbosity ( normal, silent ) import Distribution.Simple.Utils - ( wrapText, die', info, notice ) + ( wrapText, die', info, notice, safeHead ) import Distribution.Client.ProjectPlanning ( ElaboratedConfiguredPackage(..) , ElaboratedInstallPlan, binDirectoryFor ) @@ -64,6 +65,7 @@ import Distribution.Types.UnitId import Distribution.Client.ScriptUtils ( AcceptNoTargets(..), withContextAndSelectors, updateContextAndWriteProjectFile, TargetContext(..) ) +import Data.List (group) import qualified Data.Set as Set import System.Directory ( doesFileExist ) @@ -424,14 +426,13 @@ renderRunProblem :: RunProblem -> String renderRunProblem (TargetProblemMatchesMultiple targetSelector targets) = "The run command is for running a single executable at once. The target '" ++ showTargetSelector targetSelector ++ "' refers to " - ++ renderTargetSelector targetSelector ++ " which includes " - ++ renderListCommaAnd ( ("the "++) <$> - showComponentName <$> - availableTargetComponentName <$> - foldMap - (\kind -> filterTargetsKind kind targets) - [ExeKind, TestKind, BenchKind] ) - ++ "." + ++ renderTargetSelector targetSelector ++ " which includes \n" + ++ unlines ((\(label, xs) -> "- " ++ label ++ ": " ++ renderListPretty xs) + <$> (zip ["executables", "test-suites", "benchmarks"] + $ filter (not . null) . map removeDuplicates + $ map (componentNameRaw . availableTargetComponentName) + <$> (flip filterTargetsKind $ targets) <$> [ExeKind, TestKind, BenchKind] )) + where removeDuplicates = catMaybes . map safeHead . group . sort renderRunProblem (TargetProblemMultipleTargets selectorMap) = "The run command is for running a single executable at once. The targets " diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultipleExes/cabal.out b/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultipleExes/cabal.out index 31358efe8d1..1c5a2e37052 100644 --- a/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultipleExes/cabal.out +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultipleExes/cabal.out @@ -16,6 +16,8 @@ Building executable 'bar' for MultipleExes-1.0.. # cabal v2-run Up to date # cabal v2-run -Error: cabal: The run command is for running a single executable at once. The target '' refers to the package MultipleExes-1.0 which includes the executable 'foo' and the executable 'bar'. +Error: cabal: The run command is for running a single executable at once. The target '' refers to the package MultipleExes-1.0 which includes +- executables: bar and foo # cabal v2-run -Error: cabal: The run command is for running a single executable at once. The target 'MultipleExes' refers to the package MultipleExes-1.0 which includes the executable 'foo' and the executable 'bar'. +Error: cabal: The run command is for running a single executable at once. The target 'MultipleExes' refers to the package MultipleExes-1.0 which includes +- executables: bar and foo diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultiplePackages/cabal.out b/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultiplePackages/cabal.out index 2d71bf5e2bb..66606e20870 100644 --- a/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultiplePackages/cabal.out +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/MultiplePackages/cabal.out @@ -25,7 +25,8 @@ Building executable 'foo-exe' for bar-1.0.. # cabal v2-run Error: cabal: No targets given and there is no package in the current directory. Specify packages or components by name or location. See 'cabal build --help' for more details on target options. # cabal v2-run -Error: cabal: The run command is for running a single executable at once. The target 'bar' refers to the package bar-1.0 which includes the executable 'foo-exe' and the executable 'bar-exe'. +Error: cabal: The run command is for running a single executable at once. The target 'bar' refers to the package bar-1.0 which includes +- executables: bar-exe and foo-exe # cabal v2-run Error: cabal: Ambiguous target 'foo-exe'. It could be: bar:foo-exe (component) diff --git a/changelog.d/issue-8189 b/changelog.d/issue-8189 new file mode 100644 index 00000000000..0d7372e2db7 --- /dev/null +++ b/changelog.d/issue-8189 @@ -0,0 +1,11 @@ +synopsis: Pretty-print run targets on failure +packages: cabal-install +prs: #8234 +issues: #8189 + +description: { + +- Targets of the `run` command are pretty-printed when failing due to multiple targets. +- Duplicate targets are removed in the output. + +} \ No newline at end of file