Skip to content

Commit 2596f85

Browse files
authored
Merge pull request #7276 from fendor/polish/replace-list-with-non-empty
Replace List with NonEmpty in TargetsMap
2 parents 78fbe09 + 8212e90 commit 2596f85

File tree

9 files changed

+133
-22
lines changed

9 files changed

+133
-22
lines changed

cabal-install/cabal-install.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ Test-Suite unit-tests
278278
UnitTests.Distribution.Client.TreeDiffInstances
279279
UnitTests.Distribution.Client.UserConfig
280280
UnitTests.Distribution.Client.ProjectConfig
281+
UnitTests.Distribution.Client.ProjectPlanning
281282
UnitTests.Distribution.Client.JobControl
282283
UnitTests.Distribution.Client.IndexUtils
283284
UnitTests.Distribution.Client.IndexUtils.Timestamp

cabal-install/src/Distribution/Client/CmdInstall.hs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ import qualified Data.ByteString.Lazy.Char8 as BS
130130
import Data.Ord
131131
( Down(..) )
132132
import qualified Data.Map as Map
133+
import qualified Data.List.NonEmpty as NE
133134
import Distribution.Utils.NubList
134135
( fromNubList )
135136
import Network.URI (URI)
@@ -483,7 +484,7 @@ partitionToKnownTargetsAndHackagePackages
483484
-> SourcePackageDb
484485
-> ElaboratedInstallPlan
485486
-> [TargetSelector]
486-
-> IO (Map UnitId [(ComponentTarget,[TargetSelector])], [PackageName])
487+
-> IO (TargetsMap, [PackageName])
487488
partitionToKnownTargetsAndHackagePackages verbosity pkgDb elaboratedPlan targetSelectors = do
488489
let mTargets = resolveTargets
489490
selectPackageTargets
@@ -693,7 +694,7 @@ warnIfNoExes verbosity buildCtx =
693694
where
694695
targets = concat $ Map.elems $ targetsMap buildCtx
695696
components = fst <$> targets
696-
selectors = concatMap snd targets
697+
selectors = concatMap (NE.toList . snd) targets
697698
noExes = null $ catMaybes $ exeMaybe <$> components
698699

699700
exeMaybe (ComponentTarget (CExeName exe) _) = Just exe
@@ -757,7 +758,7 @@ installUnitExes
757758
-> FilePath
758759
-> InstallMethod
759760
-> ( UnitId
760-
, [(ComponentTarget, [TargetSelector])] )
761+
, [(ComponentTarget, NonEmpty TargetSelector)] )
761762
-> IO ()
762763
installUnitExes verbosity overwritePolicy
763764
mkSourceBinDir mkExeName mkFinalExeName
@@ -831,12 +832,12 @@ installBuiltExe verbosity overwritePolicy
831832
entriesForLibraryComponents :: TargetsMap -> [GhcEnvironmentFileEntry]
832833
entriesForLibraryComponents = Map.foldrWithKey' (\k v -> mappend (go k v)) []
833834
where
834-
hasLib :: (ComponentTarget, [TargetSelector]) -> Bool
835+
hasLib :: (ComponentTarget, NonEmpty TargetSelector) -> Bool
835836
hasLib (ComponentTarget (CLibName _) _, _) = True
836837
hasLib _ = False
837838

838839
go :: UnitId
839-
-> [(ComponentTarget, [TargetSelector])]
840+
-> [(ComponentTarget, NonEmpty TargetSelector)]
840841
-> [GhcEnvironmentFileEntry]
841842
go unitId targets
842843
| any hasLib targets = [GhcEnvFilePackageId unitId]

cabal-install/src/Distribution/Client/CmdListBin.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import Distribution.Client.TargetProblem (TargetProblem (..))
2727
import Distribution.Simple.BuildPaths (dllExtension, exeExtension)
2828
import Distribution.Simple.Command (CommandUI (..))
2929
import Distribution.Simple.Setup (configVerbosity, fromFlagOrDefault)
30-
import Distribution.Simple.Utils (die', ordNub, wrapText)
30+
import Distribution.Simple.Utils (die', wrapText)
3131
import Distribution.System (Platform)
3232
import Distribution.Types.ComponentName (showComponentName)
3333
import Distribution.Types.UnitId (UnitId)
@@ -341,7 +341,7 @@ renderListBinProblem (TargetProblemMatchesMultiple targetSelector targets) =
341341
renderListBinProblem (TargetProblemMultipleTargets selectorMap) =
342342
"The list-bin command is for finding a single binary at once. The targets "
343343
++ renderListCommaAnd [ "'" ++ showTargetSelector ts ++ "'"
344-
| ts <- ordNub (concatMap snd (concat (Map.elems selectorMap))) ]
344+
| ts <- uniqueTargetSelectors selectorMap ]
345345
++ " refer to different executables."
346346

347347
renderListBinProblem (TargetProblemComponentNotRightKind pkgid cname) =

cabal-install/src/Distribution/Client/CmdRepl.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ import Distribution.Utils.Generic
9696
import Distribution.Verbosity
9797
( normal, lessVerbose )
9898
import Distribution.Simple.Utils
99-
( wrapText, die', debugNoWrap, ordNub, createTempDirectory, handleDoesNotExist )
99+
( wrapText, die', debugNoWrap, createTempDirectory, handleDoesNotExist )
100100
import Language.Haskell.Extension
101101
( Language(..) )
102102
import Distribution.CabalSpecVersion
@@ -571,7 +571,7 @@ renderReplProblem (TargetProblemMultipleTargets selectorMap) =
571571
"Cannot open a repl for multiple components at once. The targets "
572572
++ renderListCommaAnd
573573
[ "'" ++ showTargetSelector ts ++ "'"
574-
| ts <- ordNub (concatMap snd (concat (Map.elems selectorMap))) ]
574+
| ts <- uniqueTargetSelectors selectorMap ]
575575
++ " refer to different components."
576576
++ ".\n\n" ++ explanationSingleComponentLimitation
577577

cabal-install/src/Distribution/Client/CmdRun.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import Distribution.CabalSpecVersion (CabalSpecVersion (..), cabalSpecLatest)
4646
import Distribution.Verbosity
4747
( normal )
4848
import Distribution.Simple.Utils
49-
( wrapText, warn, die', ordNub, info
49+
( wrapText, warn, die', info
5050
, createTempDirectory, handleDoesNotExist )
5151
import Distribution.Client.ProjectConfig
5252
( ProjectConfig(..), ProjectConfigShared(..)
@@ -101,7 +101,6 @@ import Language.Haskell.Extension
101101
( Language(..) )
102102

103103
import qualified Data.ByteString.Char8 as BS
104-
import qualified Data.Map as Map
105104
import qualified Data.Set as Set
106105
import qualified Text.Parsec as P
107106
import System.Directory
@@ -605,7 +604,7 @@ renderRunProblem (TargetProblemMatchesMultiple targetSelector targets) =
605604
renderRunProblem (TargetProblemMultipleTargets selectorMap) =
606605
"The run command is for running a single executable at once. The targets "
607606
++ renderListCommaAnd [ "'" ++ showTargetSelector ts ++ "'"
608-
| ts <- ordNub (concatMap snd (concat (Map.elems selectorMap))) ]
607+
| ts <- uniqueTargetSelectors selectorMap ]
609608
++ " refer to different executables."
610609

611610
renderRunProblem (TargetProblemComponentNotExe pkgid cname) =

cabal-install/src/Distribution/Client/ProjectOrchestration.hs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ module Distribution.Client.ProjectOrchestration (
5858
reportTargetSelectorProblems,
5959
resolveTargets,
6060
TargetsMap,
61+
allTargetSelectors,
62+
uniqueTargetSelectors,
6163
TargetSelector(..),
6264
TargetImplicitCwd(..),
6365
PackageId,
@@ -151,7 +153,7 @@ import Distribution.Simple.Command (commandShowOptions)
151153
import Distribution.Simple.Configure (computeEffectiveProfiling)
152154

153155
import Distribution.Simple.Utils
154-
( die', warn, notice, noticeNoWrap, debugNoWrap, createDirectoryIfMissingVerbose )
156+
( die', warn, notice, noticeNoWrap, debugNoWrap, createDirectoryIfMissingVerbose, ordNub )
155157
import Distribution.Verbosity
156158
import Distribution.Version
157159
( mkVersion )
@@ -473,7 +475,15 @@ runProjectPostBuildPhase verbosity
473475
-- possible to for different selectors to match the same target. This extra
474476
-- information is primarily to help make helpful error messages.
475477
--
476-
type TargetsMap = Map UnitId [(ComponentTarget, [TargetSelector])]
478+
type TargetsMap = Map UnitId [(ComponentTarget, NonEmpty TargetSelector)]
479+
480+
-- | Get all target selectors.
481+
allTargetSelectors :: TargetsMap -> [TargetSelector]
482+
allTargetSelectors = concatMap (NE.toList . snd) . concat . Map.elems
483+
484+
-- | Get all unique target selectors.
485+
uniqueTargetSelectors :: TargetsMap -> [TargetSelector]
486+
uniqueTargetSelectors = ordNub . allTargetSelectors
477487

478488
-- | Given a set of 'TargetSelector's, resolve which 'UnitId's and
479489
-- 'ComponentTarget's they ought to refer to.
@@ -529,7 +539,7 @@ resolveTargets selectPackageTargets selectComponentTarget
529539
-> TargetsMap
530540
mkTargetsMap targets =
531541
Map.map nubComponentTargets
532-
$ Map.fromListWith (++)
542+
$ Map.fromListWith (<>)
533543
[ (uid, [(ct, ts)])
534544
| (ts, cts) <- targets
535545
, (uid, ct) <- cts ]

cabal-install/src/Distribution/Client/ProjectPlanning.hs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ import qualified Data.Map as Map
163163
import qualified Data.Set as Set
164164
import Control.Monad.State as State
165165
import Control.Exception (assert)
166-
import Data.List (groupBy)
166+
import Data.List (groupBy, deleteBy)
167167
import qualified Data.List.NonEmpty as NE
168168
import System.FilePath
169169

@@ -1758,7 +1758,7 @@ elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB
17581758
-- package needs to be rebuilt. (It needs to be done here,
17591759
-- because the ElaboratedConfiguredPackage is where we test
17601760
-- whether or not there have been changes.)
1761-
TestStanzas -> listToMaybe [ v | v <- maybeToList tests, _ <- PD.testSuites elabPkgDescription ]
1761+
TestStanzas -> listToMaybe [ v | v <- maybeToList tests, _ <- PD.testSuites elabPkgDescription ]
17621762
BenchStanzas -> listToMaybe [ v | v <- maybeToList benchmarks, _ <- PD.benchmarks elabPkgDescription ]
17631763
where
17641764
tests, benchmarks :: Maybe Bool
@@ -2503,7 +2503,7 @@ availableSourceTargets elab =
25032503
-- We also allow for information associated with each component target, and
25042504
-- whenever we targets subsume each other we aggregate their associated info.
25052505
--
2506-
nubComponentTargets :: [(ComponentTarget, a)] -> [(ComponentTarget, [a])]
2506+
nubComponentTargets :: [(ComponentTarget, a)] -> [(ComponentTarget, NonEmpty a)]
25072507
nubComponentTargets =
25082508
concatMap (wholeComponentOverrides . map snd)
25092509
. groupBy ((==) `on` fst)
@@ -2514,11 +2514,17 @@ nubComponentTargets =
25142514
-- If we're building the whole component then that the only target all we
25152515
-- need, otherwise we can have several targets within the component.
25162516
wholeComponentOverrides :: [(ComponentTarget, a )]
2517-
-> [(ComponentTarget, [a])]
2517+
-> [(ComponentTarget, NonEmpty a)]
25182518
wholeComponentOverrides ts =
2519-
case [ t | (t@(ComponentTarget _ WholeComponent), _) <- ts ] of
2520-
(t:_) -> [ (t, map snd ts) ]
2521-
[] -> [ (t,[x]) | (t,x) <- ts ]
2519+
case [ ta | ta@(ComponentTarget _ WholeComponent, _) <- ts ] of
2520+
((t, x):_) ->
2521+
let
2522+
-- Delete tuple (t, x) from original list to avoid duplicates.
2523+
-- Use 'deleteBy', to avoid additional Class constraint on 'nubComponentTargets'.
2524+
ts' = deleteBy (\(t1, _) (t2, _) -> t1 == t2) (t, x) ts
2525+
in
2526+
[ (t, x :| map snd ts') ]
2527+
[] -> [ (t, x :| []) | (t,x) <- ts ]
25222528

25232529
-- Not all Cabal Setup.hs versions support sub-component targets, so switch
25242530
-- them over to the whole component

cabal-install/tests/UnitTests.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import qualified UnitTests.Distribution.Client.Tar
1616
import qualified UnitTests.Distribution.Client.Targets
1717
import qualified UnitTests.Distribution.Client.UserConfig
1818
import qualified UnitTests.Distribution.Client.ProjectConfig
19+
import qualified UnitTests.Distribution.Client.ProjectPlanning
1920
import qualified UnitTests.Distribution.Client.JobControl
2021
import qualified UnitTests.Distribution.Client.IndexUtils
2122
import qualified UnitTests.Distribution.Client.IndexUtils.Timestamp
@@ -51,6 +52,8 @@ main =
5152
UnitTests.Distribution.Client.UserConfig.tests
5253
, testGroup "UnitTests.Distribution.Client.ProjectConfig"
5354
UnitTests.Distribution.Client.ProjectConfig.tests
55+
, testGroup "UnitTests.Distribution.Client.ProjectPlanning"
56+
UnitTests.Distribution.Client.ProjectPlanning.tests
5457
, testGroup "UnitTests.Distribution.Client.JobControl"
5558
UnitTests.Distribution.Client.JobControl.tests
5659
, testGroup "UnitTests.Distribution.Client.IndexUtils"
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module UnitTests.Distribution.Client.ProjectPlanning (tests) where
4+
5+
import Data.List.NonEmpty
6+
import Distribution.Client.ProjectPlanning (ComponentTarget (..), SubComponentTarget (..), nubComponentTargets)
7+
import Distribution.Types.ComponentName
8+
import Distribution.Types.LibraryName
9+
import Test.Tasty
10+
import Test.Tasty.HUnit
11+
12+
tests :: [TestTree]
13+
tests =
14+
[ testGroup "Build Target Tests" buildTargetTests
15+
]
16+
17+
-- ----------------------------------------------------------------------------
18+
-- Build Target Tests
19+
-- ----------------------------------------------------------------------------
20+
21+
buildTargetTests :: [TestTree]
22+
buildTargetTests =
23+
[ testGroup "nubComponentTargets" nubComponentTargetsTests
24+
]
25+
26+
nubComponentTargetsTests :: [TestTree]
27+
nubComponentTargetsTests =
28+
[ testCase "Works on empty list" $
29+
nubComponentTargets [] @?= ([] :: [(ComponentTarget, NonEmpty Int)])
30+
, testCase "Merges targets to same component" $
31+
nubComponentTargets
32+
[ (mainLibModuleTarget, 1 :: Int)
33+
, (mainLibFileTarget, 2)
34+
]
35+
@?= [(mainLibWholeCompTarget, 1 :| [2])]
36+
, testCase "Merges whole component targets" $
37+
nubComponentTargets [(mainLibFileTarget, 2), (mainLibWholeCompTarget, 1 :: Int)]
38+
@?= [(mainLibWholeCompTarget, 2 :| [1])],
39+
testCase "Don't merge unrelated targets" $
40+
nubComponentTargets
41+
[ (mainLibWholeCompTarget, 1 :: Int)
42+
, (exeWholeCompTarget, 2)
43+
]
44+
@?= [(mainLibWholeCompTarget, pure 1), (exeWholeCompTarget, pure 2)]
45+
, testCase "Merge multiple related targets" $
46+
nubComponentTargets
47+
[ (mainLibWholeCompTarget, 1 :: Int)
48+
, (mainLibModuleTarget, 4)
49+
, (exeWholeCompTarget, 2)
50+
, (exeFileTarget, 3)
51+
]
52+
@?= [(mainLibWholeCompTarget, 1 :| [4]), (exeWholeCompTarget, 2 :| [3])]
53+
, testCase "Merge related targets, don't merge unrelated ones" $
54+
nubComponentTargets
55+
[ (mainLibFileTarget, 1 :: Int)
56+
, (mainLibModuleTarget, 4)
57+
, (exeWholeCompTarget, 2)
58+
, (exeFileTarget, 3)
59+
, (exe2FileTarget, 5)
60+
]
61+
@?=
62+
[ (mainLibWholeCompTarget, 1 :| [4])
63+
, (exeWholeCompTarget, 2 :| [3])
64+
, (exe2WholeCompTarget, 5 :| [])
65+
]
66+
]
67+
68+
-- ----------------------------------------------------------------------------
69+
-- Utils
70+
-- ----------------------------------------------------------------------------
71+
72+
mainLibWholeCompTarget :: ComponentTarget
73+
mainLibWholeCompTarget = ComponentTarget (CLibName LMainLibName) WholeComponent
74+
75+
mainLibModuleTarget :: ComponentTarget
76+
mainLibModuleTarget = ComponentTarget (CLibName LMainLibName) (ModuleTarget "Lib")
77+
78+
mainLibFileTarget :: ComponentTarget
79+
mainLibFileTarget = ComponentTarget (CLibName LMainLibName) (FileTarget "./Lib.hs")
80+
81+
exeWholeCompTarget :: ComponentTarget
82+
exeWholeCompTarget = ComponentTarget (CExeName "exe") WholeComponent
83+
84+
exeFileTarget :: ComponentTarget
85+
exeFileTarget = ComponentTarget (CExeName "exe") (FileTarget "./Main.hs")
86+
87+
exe2WholeCompTarget :: ComponentTarget
88+
exe2WholeCompTarget = ComponentTarget (CExeName "exe2") WholeComponent
89+
90+
exe2FileTarget :: ComponentTarget
91+
exe2FileTarget = ComponentTarget (CExeName "exe2") (FileTarget "./Main2.hs")

0 commit comments

Comments
 (0)