2 {-# LANGUAGE DeriveFunctor #-}
3 {-# LANGUAGE DeriveGeneric #-}
4 {-# LANGUAGE NamedFieldPuns #-}
5 {-# LANGUAGE RecordWildCards #-}
6 {-# LANGUAGE ScopedTypeVariables #-}
8 {-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}
10 -----------------------------------------------------------------------------
12 -----------------------------------------------------------------------------
15 -- Module : Distribution.Client.TargetSelector
16 -- Copyright : (c) Duncan Coutts 2012, 2015, 2016
19 -- Maintainer : duncan@community.haskell.org
21 -- Handling for user-specified target selectors.
22 module Distribution
.Client
.TargetSelector
23 ( -- * Target selectors
25 , TargetImplicitCwd
(..)
28 , SubComponentTarget
(..)
32 -- * Reading target selectors
34 , TargetSelectorProblem
(..)
35 , reportTargetSelectorProblems
42 , readTargetSelectorsWith
47 import Distribution
.Client
.Compat
.Prelude
50 import Distribution
.Client
.Types
51 ( PackageLocation
(..)
52 , PackageSpecifier
(..)
54 import Distribution
.Package
60 import Distribution
.Types
.UnqualComponentName
62 , mkUnqualComponentName
63 , packageNameToUnqualComponentName
64 , unUnqualComponentName
67 import Distribution
.ModuleName
71 import Distribution
.PackageDescription
73 , BenchmarkInterface
(..)
78 , TestSuiteInterface
(..)
84 import Distribution
.PackageDescription
.Configuration
85 ( flattenPackageDescription
87 import Distribution
.Simple
.LocalBuildInfo
95 import Distribution
.Solver
.Types
.SourcePackage
98 import Distribution
.Types
.ForeignLib
100 import Control
.Arrow
((&&&))
101 import Control
.Monad
hiding
107 import qualified Data
.List
.NonEmpty
as NE
108 import qualified Data
.Map
.Lazy
as Map
.Lazy
109 import qualified Data
.Map
.Strict
as Map
110 import qualified Data
.Set
as Set
111 import Distribution
.Client
.Errors
112 import Distribution
.Client
.Utils
113 ( makeRelativeCanonical
115 import Distribution
.Deprecated
.ParseUtils
118 import Distribution
.Deprecated
.ReadP
122 import qualified Distribution
.Deprecated
.ReadP
as Parse
123 import Distribution
.Simple
.Utils
128 import Distribution
.Utils
.Path
129 import qualified System
.Directory
as IO
133 , getCurrentDirectory
135 import System
.FilePath
136 ( dropTrailingPathSeparator
142 import System
.FilePath as FilePath
149 import Text
.EditDistance
151 , restrictedDamerauLevenshteinDistance
153 import qualified Prelude
(foldr1)
155 -- ------------------------------------------------------------
157 -- * Target selector terms
159 -- ------------------------------------------------------------
161 -- | A target selector is expression selecting a set of components (as targets
162 -- for a actions like @build@, @run@, @test@ etc). A target selector
163 -- corresponds to the user syntax for referring to targets on the command line.
165 -- From the users point of view a target can be many things: packages, dirs,
166 -- component names, files etc. Internally we consider a target to be a specific
167 -- component (or module\/file within a component), and all the users' notions
168 -- of targets are just different ways of referring to these component targets.
170 -- So target selectors are expressions in the sense that they are interpreted
171 -- to refer to one or more components. For example a 'TargetPackage' gets
172 -- interpreted differently by different commands to refer to all or a subset
173 -- of components within the package.
175 -- The syntax has lots of optional parts:
177 -- > [ package name | package dir | package .cabal file ]
178 -- > [ [lib:|exe:] component name ]
179 -- > [ module name | source file ]
181 = -- | One (or more) packages as a whole, or all the components of a
182 -- particular kind within the package(s).
184 -- These are always packages that are local to the project. In the case
185 -- that there is more than one, they all share the same directory location.
186 TargetPackage TargetImplicitCwd
[PackageId
] (Maybe ComponentKindFilter
)
187 |
-- | A package specified by name. This may refer to @extra-packages@ from
188 -- the @cabal.project@ file, or a dependency of a known project package or
189 -- could refer to a package from a hackage archive. It needs further
190 -- context to resolve to a specific package.
191 TargetPackageNamed PackageName
(Maybe ComponentKindFilter
)
192 |
-- | All packages, or all components of a particular kind in all packages.
193 TargetAllPackages
(Maybe ComponentKindFilter
)
194 |
-- | A specific component in a package within the project.
195 TargetComponent PackageId ComponentName SubComponentTarget
196 |
-- | A component in a package, but where it cannot be verified that the
197 -- package has such a component, or because the package is itself not
199 TargetComponentUnknown
201 (Either UnqualComponentName ComponentName
)
203 deriving (Eq
, Ord
, Show, Generic
)
205 -- | Does this 'TargetPackage' selector arise from syntax referring to a
206 -- package in the current directory (e.g. @tests@ or no giving no explicit
207 -- target at all) or does it come from syntax referring to a package name
209 data TargetImplicitCwd
= TargetImplicitCwd | TargetExplicitNamed
210 deriving (Eq
, Ord
, Show, Generic
)
212 data ComponentKind
= LibKind | FLibKind | ExeKind | TestKind | BenchKind
213 deriving (Eq
, Ord
, Enum
, Show)
215 type ComponentKindFilter
= ComponentKind
217 -- | Either the component as a whole or detail about a file or module target
218 -- within a component.
219 data SubComponentTarget
220 = -- | The component as a whole
222 |
-- | A specific module within a component.
223 ModuleTarget ModuleName
224 |
-- | A specific file within a component. Note that this does not carry the
227 deriving (Eq
, Ord
, Show, Generic
)
229 instance Binary SubComponentTarget
230 instance Structured SubComponentTarget
232 -- ------------------------------------------------------------
234 -- * Top level, do everything
236 -- ------------------------------------------------------------
238 -- | Parse a bunch of command line args as 'TargetSelector's, failing with an
239 -- error if any are unrecognised. The possible target selectors are based on
240 -- the available packages (and their locations).
242 :: [PackageSpecifier
(SourcePackage
(PackageLocation a
))]
243 -> Maybe ComponentKindFilter
244 -- ^ This parameter is used when there are ambiguous selectors.
245 -- If it is 'Just', then we attempt to resolve ambiguity
246 -- by applying it, since otherwise there is no way to allow
247 -- contextually valid yet syntactically ambiguous selectors.
250 -> IO (Either [TargetSelectorProblem
] [TargetSelector
])
251 readTargetSelectors
= readTargetSelectorsWith defaultDirActions
253 readTargetSelectorsWith
254 :: (Applicative m
, Monad m
)
256 -> [PackageSpecifier
(SourcePackage
(PackageLocation a
))]
257 -> Maybe ComponentKindFilter
259 -> m
(Either [TargetSelectorProblem
] [TargetSelector
])
260 readTargetSelectorsWith dirActions
@DirActions
{} pkgs mfilter targetStrs
=
261 case parseTargetStrings targetStrs
of
262 ([], usertargets
) -> do
263 usertargets
' <- traverse
(getTargetStringFileStatus dirActions
) usertargets
264 knowntargets
<- getKnownTargets dirActions pkgs
265 case resolveTargetSelectors knowntargets usertargets
' mfilter
of
266 ([], btargets
) -> return (Right btargets
)
267 (problems
, _
) -> return (Left problems
)
268 (strs
, _
) -> return (Left
(map TargetSelectorUnrecognised strs
))
270 data DirActions m
= DirActions
271 { doesFileExist :: FilePath -> m
Bool
272 , doesDirectoryExist :: FilePath -> m
Bool
273 , canonicalizePath
:: FilePath -> m
FilePath
274 , getCurrentDirectory :: m
FilePath
277 defaultDirActions
:: DirActions
IO
280 { doesFileExist = IO.doesFileExist
281 , doesDirectoryExist = IO.doesDirectoryExist
282 , -- Workaround for <https://github.com/haskell/directory/issues/63>
283 canonicalizePath
= IO.canonicalizePath
. dropTrailingPathSeparator
284 , getCurrentDirectory = IO.getCurrentDirectory
287 makeRelativeToCwd
:: Applicative m
=> DirActions m
-> FilePath -> m
FilePath
288 makeRelativeToCwd DirActions
{..} path
=
289 makeRelativeCanonical
<$> canonicalizePath path
<*> getCurrentDirectory
291 -- ------------------------------------------------------------
293 -- * Parsing target strings
295 -- ------------------------------------------------------------
297 -- | The outline parse of a target selector. It takes one of the forms:
302 -- > str1:str2:str3:str4
304 = TargetString1
String
305 | TargetString2
String String
306 | TargetString3
String String String
307 | TargetString4
String String String String
308 | TargetString5
String String String String String
309 | TargetString7
String String String String String String String
312 -- | Parse a bunch of 'TargetString's (purely without throwing exceptions).
313 parseTargetStrings
:: [String] -> ([String], [TargetString
])
316 . map (\str
-> maybe (Left str
) Right
(parseTargetString str
))
318 parseTargetString
:: String -> Maybe TargetString
320 readPToMaybe parseTargetApprox
322 parseTargetApprox
:: Parse
.ReadP r TargetString
326 return (TargetString1 a
)
332 return (TargetString2 a b
)
340 return (TargetString3 a b c
)
350 return (TargetString4 a b c d
)
362 return (TargetString5 a b c d e
)
378 return (TargetString7 a b c d e f g
)
381 token
= Parse
.munch1
(\x
-> not (isSpace x
) && x
/= ':')
382 tokenQ
= parseHaskellString
<++ token
383 token0
= Parse
.munch
(\x
-> not (isSpace x
) && x
/= ':')
384 tokenQ0
= parseHaskellString
<++ token0
385 parseHaskellString
:: Parse
.ReadP r
String
386 parseHaskellString
= Parse
.readS_to_P
reads
388 -- | Render a 'TargetString' back as the external syntax. This is mainly for
390 showTargetString
:: TargetString
-> String
391 showTargetString
= intercalate
":" . components
393 components
(TargetString1 s1
) = [s1
]
394 components
(TargetString2 s1 s2
) = [s1
, s2
]
395 components
(TargetString3 s1 s2 s3
) = [s1
, s2
, s3
]
396 components
(TargetString4 s1 s2 s3 s4
) = [s1
, s2
, s3
, s4
]
397 components
(TargetString5 s1 s2 s3 s4 s5
) = [s1
, s2
, s3
, s4
, s5
]
398 components
(TargetString7 s1 s2 s3 s4 s5 s6 s7
) = [s1
, s2
, s3
, s4
, s5
, s6
, s7
]
400 showTargetSelector
:: TargetSelector
-> String
401 showTargetSelector ts
=
402 case [ t | ql
<- [QL1
.. QLFull
], t
<- renderTargetSelector ql ts
404 (t
' : _
) -> showTargetString
(forgetFileStatus t
')
407 showTargetSelectorKind
:: TargetSelector
-> String
408 showTargetSelectorKind bt
= case bt
of
409 TargetPackage TargetExplicitNamed _ Nothing
-> "package"
410 TargetPackage TargetExplicitNamed _
(Just _
) -> "package:filter"
411 TargetPackage TargetImplicitCwd _ Nothing
-> "cwd-package"
412 TargetPackage TargetImplicitCwd _
(Just _
) -> "cwd-package:filter"
413 TargetPackageNamed _ Nothing
-> "named-package"
414 TargetPackageNamed _
(Just _
) -> "named-package:filter"
415 TargetAllPackages Nothing
-> "package *"
416 TargetAllPackages
(Just _
) -> "package *:filter"
417 TargetComponent _ _ WholeComponent
-> "component"
418 TargetComponent _ _ ModuleTarget
{} -> "module"
419 TargetComponent _ _ FileTarget
{} -> "file"
420 TargetComponentUnknown _ _ WholeComponent
-> "unknown-component"
421 TargetComponentUnknown _ _ ModuleTarget
{} -> "unknown-module"
422 TargetComponentUnknown _ _ FileTarget
{} -> "unknown-file"
424 -- ------------------------------------------------------------
426 -- * Checking if targets exist as files
428 -- ------------------------------------------------------------
430 data TargetStringFileStatus
431 = TargetStringFileStatus1
String FileStatus
432 | TargetStringFileStatus2
String FileStatus
String
433 | TargetStringFileStatus3
String FileStatus
String String
434 | TargetStringFileStatus4
String String String String
435 | TargetStringFileStatus5
String String String String String
436 | TargetStringFileStatus7
String String String String String String String
437 deriving (Eq
, Ord
, Show)
440 = FileStatusExistsFile
FilePath -- the canonicalised filepath
441 | FileStatusExistsDir
FilePath -- the canonicalised filepath
442 | FileStatusNotExists
Bool -- does the parent dir exist even?
443 deriving (Eq
, Ord
, Show)
445 noFileStatus
:: FileStatus
446 noFileStatus
= FileStatusNotExists
False
448 getTargetStringFileStatus
449 :: (Applicative m
, Monad m
)
452 -> m TargetStringFileStatus
453 getTargetStringFileStatus DirActions
{..} t
=
456 (\f1 -> TargetStringFileStatus1 s1 f1
) <$> fileStatus s1
457 TargetString2 s1 s2
->
458 (\f1 -> TargetStringFileStatus2 s1 f1 s2
) <$> fileStatus s1
459 TargetString3 s1 s2 s3
->
460 (\f1 -> TargetStringFileStatus3 s1 f1 s2 s3
) <$> fileStatus s1
461 TargetString4 s1 s2 s3 s4
->
462 return (TargetStringFileStatus4 s1 s2 s3 s4
)
463 TargetString5 s1 s2 s3 s4 s5
->
464 return (TargetStringFileStatus5 s1 s2 s3 s4 s5
)
465 TargetString7 s1 s2 s3 s4 s5 s6 s7
->
466 return (TargetStringFileStatus7 s1 s2 s3 s4 s5 s6 s7
)
469 fexists
<- doesFileExist f
470 dexists
<- doesDirectoryExist f
473 | fexists
-> FileStatusExistsFile
<$> canonicalizePath f
474 | dexists
-> FileStatusExistsDir
<$> canonicalizePath f
475 (d
: _
) -> FileStatusNotExists
<$> doesDirectoryExist d
476 _
-> pure
(FileStatusNotExists
False)
478 forgetFileStatus
:: TargetStringFileStatus
-> TargetString
479 forgetFileStatus t
= case t
of
480 TargetStringFileStatus1 s1 _
-> TargetString1 s1
481 TargetStringFileStatus2 s1 _ s2
-> TargetString2 s1 s2
482 TargetStringFileStatus3 s1 _ s2 s3
-> TargetString3 s1 s2 s3
483 TargetStringFileStatus4 s1 s2 s3 s4
-> TargetString4 s1 s2 s3 s4
484 TargetStringFileStatus5
489 s5
-> TargetString5 s1 s2 s3 s4 s5
490 TargetStringFileStatus7
497 s7
-> TargetString7 s1 s2 s3 s4 s5 s6 s7
499 getFileStatus
:: TargetStringFileStatus
-> Maybe FileStatus
500 getFileStatus
(TargetStringFileStatus1 _ f
) = Just f
501 getFileStatus
(TargetStringFileStatus2 _ f _
) = Just f
502 getFileStatus
(TargetStringFileStatus3 _ f _ _
) = Just f
503 getFileStatus _
= Nothing
505 setFileStatus
:: FileStatus
-> TargetStringFileStatus
-> TargetStringFileStatus
506 setFileStatus f
(TargetStringFileStatus1 s1 _
) = TargetStringFileStatus1 s1 f
507 setFileStatus f
(TargetStringFileStatus2 s1 _ s2
) = TargetStringFileStatus2 s1 f s2
508 setFileStatus f
(TargetStringFileStatus3 s1 _ s2 s3
) = TargetStringFileStatus3 s1 f s2 s3
509 setFileStatus _ t
= t
511 copyFileStatus
:: TargetStringFileStatus
-> TargetStringFileStatus
-> TargetStringFileStatus
512 copyFileStatus src dst
=
513 case getFileStatus src
of
514 Just f
-> setFileStatus f dst
517 -- ------------------------------------------------------------
519 -- * Resolving target strings to target selectors
521 -- ------------------------------------------------------------
523 -- | Given a bunch of user-specified targets, try to resolve what it is they
525 resolveTargetSelectors
527 -> [TargetStringFileStatus
]
528 -> Maybe ComponentKindFilter
529 -> ( [TargetSelectorProblem
]
532 -- default local dir target if there's no given target:
533 resolveTargetSelectors
(KnownTargets
{knownPackagesAll
= []}) [] _
=
534 ([TargetSelectorNoTargetsInProject
], [])
535 -- if the component kind filter is just exes, we don't want to suggest "all" as a target.
536 resolveTargetSelectors
(KnownTargets
{knownPackagesPrimary
= []}) [] ckf
=
537 ([TargetSelectorNoTargetsInCwd
(ckf
/= Just ExeKind
)], [])
538 resolveTargetSelectors
(KnownTargets
{knownPackagesPrimary
}) [] _
=
539 ([], [TargetPackage TargetImplicitCwd pkgids Nothing
])
541 pkgids
= [pinfoId | KnownPackage
{pinfoId
} <- knownPackagesPrimary
]
542 resolveTargetSelectors knowntargets targetStrs mfilter
=
544 . map (resolveTargetSelector knowntargets mfilter
)
547 resolveTargetSelector
549 -> Maybe ComponentKindFilter
550 -> TargetStringFileStatus
551 -> Either TargetSelectorProblem TargetSelector
552 resolveTargetSelector knowntargets
@KnownTargets
{..} mfilter targetStrStatus
=
553 case findMatch
(matcher targetStrStatus
) of
555 | projectIsEmpty
-> Left TargetSelectorNoTargetsInProject
556 Unambiguous
(TargetPackage TargetImplicitCwd
[] _
) ->
557 Left
(TargetSelectorNoCurrentPackage targetStr
)
558 Unambiguous target
-> Right target
560 | projectIsEmpty
-> Left TargetSelectorNoTargetsInProject
561 |
otherwise -> Left
(classifyMatchErrors errs
)
563 | Just kfilter
<- mfilter
564 , [target
] <- applyKindFilter kfilter targets
->
566 Ambiguous exactMatch targets
->
567 case disambiguateTargetSelectors
572 Right targets
' -> Left
(TargetSelectorAmbiguous targetStr targets
')
573 Left
((m
, ms
) : _
) -> Left
(MatchingInternalError targetStr m ms
)
574 Left
[] -> internalError
"resolveTargetSelector"
576 matcher
= matchTargetSelector knowntargets
578 targetStr
= forgetFileStatus targetStrStatus
580 projectIsEmpty
= null knownPackagesAll
582 classifyMatchErrors errs
583 | Just expectedNE
<- NE
.nonEmpty expected
=
584 let (things
, got
:| _
) = NE
.unzip expectedNE
585 in TargetSelectorExpected targetStr
(NE
.toList things
) got
586 |
not (null nosuch
) =
587 TargetSelectorNoSuch targetStr nosuch
589 internalError
$ "classifyMatchErrors: " ++ show errs
593 |
(_
, MatchErrorExpected thing got
) <-
594 map (innerErr Nothing
) errs
596 -- Trim the list of alternatives by dropping duplicates and
597 -- retaining only at most three most similar (by edit distance) ones.
599 Map
.foldrWithKey genResults
[] $
600 Map
.fromListWith Set
.union $
601 [ ((inside
, thing
, got
), Set
.fromList alts
)
602 |
(inside
, MatchErrorNoSuch thing got alts
) <-
603 map (innerErr Nothing
) errs
606 genResults
(inside
, thing
, got
) alts acc
=
612 takeWhile distanceLow
$
613 sortBy (comparing
snd) $
621 &&& restrictedDamerauLevenshteinDistance
625 distanceLow
(_
, dist
) = dist
< length got `
div`
2
629 innerErr _
(MatchErrorIn kind thing m
) =
630 innerErr
(Just
(kind
, thing
)) m
631 innerErr c m
= (c
, m
)
633 applyKindFilter
:: ComponentKindFilter
-> [TargetSelector
] -> [TargetSelector
]
634 applyKindFilter kfilter
= filter go
636 go
(TargetPackage _ _
(Just
filter')) = kfilter
== filter'
637 go
(TargetPackageNamed _
(Just
filter')) = kfilter
== filter'
638 go
(TargetAllPackages
(Just
filter')) = kfilter
== filter'
639 go
(TargetComponent _ cname _
)
640 | CLibName _
<- cname
= kfilter
== LibKind
641 | CFLibName _
<- cname
= kfilter
== FLibKind
642 | CExeName _
<- cname
= kfilter
== ExeKind
643 | CTestName _
<- cname
= kfilter
== TestKind
644 | CBenchName _
<- cname
= kfilter
== BenchKind
647 -- | The various ways that trying to resolve a 'TargetString' to a
648 -- 'TargetSelector' can fail.
649 data TargetSelectorProblem
650 = -- | [expected thing] (actually got)
651 TargetSelectorExpected TargetString
[String] String
652 |
-- | [([in thing], no such thing, actually got, alternatives)]
655 [(Maybe (String, String), String, String, [String])]
656 | TargetSelectorAmbiguous
658 [(TargetString
, TargetSelector
)]
659 | MatchingInternalError
662 [(TargetString
, [TargetSelector
])]
663 |
-- | Syntax error when trying to parse a target string.
664 TargetSelectorUnrecognised
String
665 | TargetSelectorNoCurrentPackage TargetString
666 |
-- | bool that flags when it is acceptable to suggest "all" as a target
667 TargetSelectorNoTargetsInCwd
Bool
668 | TargetSelectorNoTargetsInProject
669 | TargetSelectorNoScript TargetString
672 -- | Qualification levels.
673 -- Given the filepath src/F, executable component A, and package foo:
677 |
-- | @foo:src/F | A:src/F@
679 |
-- | @foo:A:src/F | exe:A:src/F@
681 |
-- | @pkg:foo:exe:A:file:src/F@
683 deriving (Eq
, Enum
, Show)
685 disambiguateTargetSelectors
686 :: (TargetStringFileStatus
-> Match TargetSelector
)
687 -> TargetStringFileStatus
691 [(TargetSelector
, [(TargetString
, [TargetSelector
])])]
692 [(TargetString
, TargetSelector
)]
693 disambiguateTargetSelectors matcher matchInput exactMatch matchResults
=
694 case partitionEithers results
of
695 (errs
@(_
: _
), _
) -> Left errs
698 -- So, here's the strategy. We take the original match results, and make a
699 -- table of all their renderings at all qualification levels.
700 -- Note there can be multiple renderings at each qualification level.
702 -- Note that renderTargetSelector won't immediately work on any file syntax
703 -- When rendering syntax, the FileStatus is always FileStatusNotExists,
704 -- which will never match on syntaxForm1File!
705 -- Because matchPackageDirectoryPrefix expects a FileStatusExistsFile.
706 -- So we need to copy over the file status from the input
707 -- TargetStringFileStatus, onto the new rendered TargetStringFileStatus
708 matchResultsRenderings
:: [(TargetSelector
, [TargetStringFileStatus
])]
709 matchResultsRenderings
=
710 [ (matchResult
, matchRenderings
)
711 | matchResult
<- matchResults
712 , let matchRenderings
=
713 [ copyFileStatus matchInput rendering
714 | ql
<- [QL1
.. QLFull
]
715 , rendering
<- renderTargetSelector ql matchResult
719 -- Of course the point is that we're looking for renderings that are
720 -- unambiguous matches. So we build another memo table of all the matches
721 -- for all of those renderings. So by looking up in this table we can see
722 -- if we've got an unambiguous match.
724 memoisedMatches
:: Map TargetStringFileStatus
(Match TargetSelector
)
726 -- avoid recomputing the main one if it was an exact match
727 ( if exactMatch
== Exact
728 then Map
.insert matchInput
(Match Exact
0 matchResults
)
732 -- (matcher rendering) should *always* be a Match! Otherwise we will hit
733 -- the internal error later on.
734 [ (rendering
, matcher rendering
)
735 | rendering
<- concatMap snd matchResultsRenderings
738 -- Finally, for each of the match results, we go through all their
739 -- possible renderings (in order of qualification level, though remember
740 -- there can be multiple renderings per level), and find the first one
741 -- that has an unambiguous match.
744 (TargetSelector
, [(TargetString
, [TargetSelector
])])
745 (TargetString
, TargetSelector
)
748 [ case findUnambiguous originalMatch matchRenderings
of
749 Just unambiguousRendering
->
751 ( forgetFileStatus unambiguousRendering
754 -- This case is an internal error, but we bubble it up and report it
758 , [ (forgetFileStatus rendering
, matches
)
759 | rendering
<- matchRenderings
760 , let Match m _ matches
=
761 memoisedMatches Map
.! rendering
765 |
(originalMatch
, matchRenderings
) <- matchResultsRenderings
770 -> [TargetStringFileStatus
]
771 -> Maybe TargetStringFileStatus
772 findUnambiguous _
[] = Nothing
773 findUnambiguous t
(r
: rs
) =
774 case memoisedMatches Map
.! r
of
778 Match Exact _ _
-> findUnambiguous t rs
779 Match Unknown _ _
-> findUnambiguous t rs
780 Match Inexact _ _
-> internalError
"Match Inexact"
781 NoMatch _ _
-> internalError
"NoMatch"
783 internalError
:: String -> a
785 error $ "TargetSelector: internal error: " ++ msg
787 -- | Throw an exception with a formatted message if there are any problems.
788 reportTargetSelectorProblems
:: Verbosity
-> [TargetSelectorProblem
] -> IO a
789 reportTargetSelectorProblems verbosity problems
= do
790 case [str | TargetSelectorUnrecognised str
<- problems
] of
792 targets
-> dieWithException verbosity
$ ReportTargetSelectorProblems targets
794 case [(t
, m
, ms
) | MatchingInternalError t m ms
<- problems
] of
796 ((target
, originalMatch
, renderingsAndMatches
) : _
) ->
797 dieWithException verbosity
798 $ MatchingInternalErrorErr
799 (showTargetString target
)
800 (showTargetSelector originalMatch
)
801 (showTargetSelectorKind originalMatch
)
803 ( \(rendering
, matches
) ->
804 ( showTargetString rendering
805 , (map (\match
-> showTargetSelector match
++ " (" ++ showTargetSelectorKind match
++ ")") matches
)
810 case [(t
, e
, g
) | TargetSelectorExpected t e g
<- problems
] of
813 dieWithException verbosity
$
815 map (\(target
, expected
, got
) -> (showTargetString target
, expected
, got
)) targets
817 case [(t
, e
) | TargetSelectorNoSuch t e
<- problems
] of
820 dieWithException verbosity
$
821 NoSuchTargetSelectorErr
$
822 map (\(target
, nosuch
) -> (showTargetString target
, nosuch
)) targets
824 case [(t
, ts
) | TargetSelectorAmbiguous t ts
<- problems
] of
827 dieWithException verbosity
$
828 TargetSelectorAmbiguousErr
$
831 ( showTargetString target
832 , (map (\(ut
, bt
) -> (showTargetString ut
, showTargetSelectorKind bt
)) amb
)
837 case [t | TargetSelectorNoCurrentPackage t
<- problems
] of
840 dieWithException verbosity
$ TargetSelectorNoCurrentPackageErr
(showTargetString target
)
842 -- TODO: report a different error if there is a .cabal file but it's
843 -- not a member of the project
845 case [() | TargetSelectorNoTargetsInCwd
True <- problems
] of
848 dieWithException verbosity TargetSelectorNoTargetsInCwdTrue
850 case [() | TargetSelectorNoTargetsInCwd
False <- problems
] of
853 dieWithException verbosity TargetSelectorNoTargetsInCwdFalse
855 case [() | TargetSelectorNoTargetsInProject
<- problems
] of
858 dieWithException verbosity TargetSelectorNoTargetsInProjectErr
860 case [t | TargetSelectorNoScript t
<- problems
] of
863 dieWithException verbosity
$ TargetSelectorNoScriptErr
(showTargetString target
)
865 fail "reportTargetSelectorProblems: internal error"
867 ----------------------------------
871 -- | Syntax for the 'TargetSelector': the matcher and renderer
873 = Syntax QualLevel Matcher Renderer
874 | AmbiguousAlternatives Syntax Syntax
875 | ShadowingAlternatives Syntax Syntax
877 type Matcher
= TargetStringFileStatus
-> Match TargetSelector
878 type Renderer
= TargetSelector
-> [TargetStringFileStatus
]
883 -> (QualLevel
-> Matcher
-> Renderer
-> a
)
885 foldSyntax ambiguous unambiguous syntax
= go
887 go
(Syntax ql match render
) = syntax ql match render
888 go
(AmbiguousAlternatives a b
) = ambiguous
(go a
) (go b
)
889 go
(ShadowingAlternatives a b
) = unambiguous
(go a
) (go b
)
891 ----------------------------------
892 -- Top level renderer and matcher
898 -> [TargetStringFileStatus
]
899 renderTargetSelector ql ts
=
903 (\ql
' _ render
-> guard (ql
== ql
') >> render ts
)
906 syntax
= syntaxForms emptyKnownTargets
908 -- don't need known targets for rendering
912 -> TargetStringFileStatus
913 -> Match TargetSelector
914 matchTargetSelector knowntargets
= \usertarget
->
916 let ql
= targetQualLevel usertarget
920 (\ql
' match _
-> guard (ql
== ql
') >> match usertarget
)
923 syntax
= syntaxForms knowntargets
925 targetQualLevel TargetStringFileStatus1
{} = QL1
926 targetQualLevel TargetStringFileStatus2
{} = QL2
927 targetQualLevel TargetStringFileStatus3
{} = QL3
928 targetQualLevel TargetStringFileStatus4
{} = QLFull
929 targetQualLevel TargetStringFileStatus5
{} = QLFull
930 targetQualLevel TargetStringFileStatus7
{} = QLFull
932 ----------------------------------
936 -- | All the forms of syntax for 'TargetSelector'.
937 syntaxForms
:: KnownTargets
-> Syntax
940 { knownPackagesAll
= pinfo
941 , knownPackagesPrimary
= ppinfo
942 , knownComponentsAll
= cinfo
943 , knownComponentsPrimary
= pcinfo
944 , knownComponentsOther
= ocinfo
946 -- The various forms of syntax here are ambiguous in many cases.
947 -- Our policy is by default we expose that ambiguity and report
948 -- ambiguous matches. In certain cases we override the ambiguity
949 -- by having some forms shadow others.
951 -- We make modules shadow files because module name "Q" clashes
952 -- with file "Q" with no extension but these refer to the same
953 -- thing anyway so it's not a useful ambiguity. Other cases are
954 -- not ambiguous like "Q" vs "Q.hs" or "Data.Q" vs "Data/Q".
956 ambiguousAlternatives
957 -- convenient single-component forms
958 [ shadowingAlternatives
959 [ ambiguousAlternatives
961 , syntaxForm1Filter ppinfo
962 , shadowingAlternatives
963 [ syntaxForm1Component pcinfo
964 , syntaxForm1Package pinfo
967 , syntaxForm1Component ocinfo
968 , syntaxForm1Module cinfo
969 , syntaxForm1File pinfo
971 , -- two-component partially qualified forms
972 -- fully qualified form for 'all'
974 , syntaxForm2AllFilter
975 , syntaxForm2NamespacePackage pinfo
976 , syntaxForm2PackageComponent pinfo
977 , syntaxForm2PackageFilter pinfo
978 , syntaxForm2KindComponent cinfo
979 , shadowingAlternatives
980 [ syntaxForm2PackageModule pinfo
981 , syntaxForm2PackageFile pinfo
983 , shadowingAlternatives
984 [ syntaxForm2ComponentModule cinfo
985 , syntaxForm2ComponentFile cinfo
987 , -- rarely used partially qualified forms
988 syntaxForm3PackageKindComponent pinfo
989 , shadowingAlternatives
990 [ syntaxForm3PackageComponentModule pinfo
991 , syntaxForm3PackageComponentFile pinfo
993 , shadowingAlternatives
994 [ syntaxForm3KindComponentModule cinfo
995 , syntaxForm3KindComponentFile cinfo
997 , syntaxForm3NamespacePackageFilter pinfo
998 , -- fully-qualified forms for all and cwd with filter
999 syntaxForm3MetaAllFilter
1000 , syntaxForm3MetaCwdFilter ppinfo
1001 , -- fully-qualified form for package and package with filter
1002 syntaxForm3MetaNamespacePackage pinfo
1003 , syntaxForm4MetaNamespacePackageFilter pinfo
1004 , -- fully-qualified forms for component, module and file
1005 syntaxForm5MetaNamespacePackageKindComponent pinfo
1006 , syntaxForm7MetaNamespacePackageKindComponentNamespaceModule pinfo
1007 , syntaxForm7MetaNamespacePackageKindComponentNamespaceFile pinfo
1010 ambiguousAlternatives
= Prelude
.foldr1 AmbiguousAlternatives
1011 shadowingAlternatives
= Prelude
.foldr1 ShadowingAlternatives
1013 -- | Syntax: "all" to select all packages in the project
1015 -- > cabal build all
1016 syntaxForm1All
:: Syntax
1018 syntaxForm1 render
$ \str1 _fstatus1
-> do
1020 return (TargetAllPackages Nothing
)
1022 render
(TargetAllPackages Nothing
) =
1023 [TargetStringFileStatus1
"all" noFileStatus
]
1028 -- > cabal build tests
1029 syntaxForm1Filter
:: [KnownPackage
] -> Syntax
1030 syntaxForm1Filter ps
=
1031 syntaxForm1 render
$ \str1 _fstatus1
-> do
1032 kfilter
<- matchComponentKindFilter str1
1033 return (TargetPackage TargetImplicitCwd pids
(Just kfilter
))
1035 pids
= [pinfoId | KnownPackage
{pinfoId
} <- ps
]
1036 render
(TargetPackage TargetImplicitCwd _
(Just kfilter
)) =
1037 [TargetStringFileStatus1
(dispF kfilter
) noFileStatus
]
1040 -- | Syntax: package (name, dir or file)
1042 -- > cabal build foo
1043 -- > cabal build ../bar ../bar/bar.cabal
1044 syntaxForm1Package
:: [KnownPackage
] -> Syntax
1045 syntaxForm1Package pinfo
=
1046 syntaxForm1 render
$ \str1 fstatus1
-> do
1047 guardPackage str1 fstatus1
1048 p
<- matchPackage pinfo str1 fstatus1
1050 KnownPackage
{pinfoId
} ->
1051 return (TargetPackage TargetExplicitNamed
[pinfoId
] Nothing
)
1052 KnownPackageName pn
->
1053 return (TargetPackageNamed pn Nothing
)
1055 render
(TargetPackage TargetExplicitNamed
[p
] Nothing
) =
1056 [TargetStringFileStatus1
(dispP p
) noFileStatus
]
1057 render
(TargetPackageNamed pn Nothing
) =
1058 [TargetStringFileStatus1
(dispPN pn
) noFileStatus
]
1061 -- | Syntax: component
1063 -- > cabal build foo
1064 syntaxForm1Component
:: [KnownComponent
] -> Syntax
1065 syntaxForm1Component cs
=
1066 syntaxForm1 render
$ \str1 _fstatus1
-> do
1067 guardComponentName str1
1068 c
<- matchComponentName cs str1
1069 return (TargetComponent
(cinfoPackageId c
) (cinfoName c
) WholeComponent
)
1071 render
(TargetComponent p c WholeComponent
) =
1072 [TargetStringFileStatus1
(dispC p c
) noFileStatus
]
1077 -- > cabal build Data.Foo
1078 syntaxForm1Module
:: [KnownComponent
] -> Syntax
1079 syntaxForm1Module cs
=
1080 syntaxForm1 render
$ \str1 _fstatus1
-> do
1081 guardModuleName str1
1082 let ms
= [(m
, c
) | c
<- cs
, m
<- cinfoModules c
]
1083 (m
, c
) <- matchModuleNameAnd ms str1
1084 return (TargetComponent
(cinfoPackageId c
) (cinfoName c
) (ModuleTarget m
))
1086 render
(TargetComponent _p _c
(ModuleTarget m
)) =
1087 [TargetStringFileStatus1
(dispM m
) noFileStatus
]
1090 -- | Syntax: file name
1092 -- > cabal build Data/Foo.hs bar/Main.hsc
1093 syntaxForm1File
:: [KnownPackage
] -> Syntax
1094 syntaxForm1File ps
=
1095 -- Note there's a bit of an inconsistency here vs the other syntax forms
1096 -- for files. For the single-part syntax the target has to point to a file
1097 -- that exists (due to our use of matchPackageDirectoryPrefix), whereas for
1098 -- all the other forms we don't require that.
1099 syntaxForm1 render
$ \str1 fstatus1
->
1100 expecting
"file" str1
$ do
1101 (pkgfile
, ~KnownPackage
{pinfoId
, pinfoComponents
}) <-
1102 -- always returns the KnownPackage case
1103 matchPackageDirectoryPrefix ps fstatus1
1104 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1105 (filepath
, c
) <- matchComponentFile pinfoComponents pkgfile
1106 return (TargetComponent pinfoId
(cinfoName c
) (FileTarget filepath
))
1108 render
(TargetComponent _p _c
(FileTarget f
)) =
1109 [TargetStringFileStatus1 f noFileStatus
]
1116 -- > cabal build :all
1117 syntaxForm2MetaAll
:: Syntax
1118 syntaxForm2MetaAll
=
1119 syntaxForm2 render
$ \str1 _fstatus1 str2
-> do
1120 guardNamespaceMeta str1
1122 return (TargetAllPackages Nothing
)
1124 render
(TargetAllPackages Nothing
) =
1125 [TargetStringFileStatus2
"" noFileStatus
"all"]
1128 -- | Syntax: all : filer
1130 -- > cabal build all:tests
1131 syntaxForm2AllFilter
:: Syntax
1132 syntaxForm2AllFilter
=
1133 syntaxForm2 render
$ \str1 _fstatus1 str2
-> do
1135 kfilter
<- matchComponentKindFilter str2
1136 return (TargetAllPackages
(Just kfilter
))
1138 render
(TargetAllPackages
(Just kfilter
)) =
1139 [TargetStringFileStatus2
"all" noFileStatus
(dispF kfilter
)]
1142 -- | Syntax: package : filer
1144 -- > cabal build foo:tests
1145 syntaxForm2PackageFilter
:: [KnownPackage
] -> Syntax
1146 syntaxForm2PackageFilter ps
=
1147 syntaxForm2 render
$ \str1 fstatus1 str2
-> do
1148 guardPackage str1 fstatus1
1149 p
<- matchPackage ps str1 fstatus1
1150 kfilter
<- matchComponentKindFilter str2
1152 KnownPackage
{pinfoId
} ->
1153 return (TargetPackage TargetExplicitNamed
[pinfoId
] (Just kfilter
))
1154 KnownPackageName pn
->
1155 return (TargetPackageNamed pn
(Just kfilter
))
1157 render
(TargetPackage TargetExplicitNamed
[p
] (Just kfilter
)) =
1158 [TargetStringFileStatus2
(dispP p
) noFileStatus
(dispF kfilter
)]
1159 render
(TargetPackageNamed pn
(Just kfilter
)) =
1160 [TargetStringFileStatus2
(dispPN pn
) noFileStatus
(dispF kfilter
)]
1163 -- | Syntax: pkg : package name
1165 -- > cabal build pkg:foo
1166 syntaxForm2NamespacePackage
:: [KnownPackage
] -> Syntax
1167 syntaxForm2NamespacePackage pinfo
=
1168 syntaxForm2 render
$ \str1 _fstatus1 str2
-> do
1169 guardNamespacePackage str1
1170 guardPackageName str2
1171 p
<- matchPackage pinfo str2 noFileStatus
1173 KnownPackage
{pinfoId
} ->
1174 return (TargetPackage TargetExplicitNamed
[pinfoId
] Nothing
)
1175 KnownPackageName pn
->
1176 return (TargetPackageNamed pn Nothing
)
1178 render
(TargetPackage TargetExplicitNamed
[p
] Nothing
) =
1179 [TargetStringFileStatus2
"pkg" noFileStatus
(dispP p
)]
1180 render
(TargetPackageNamed pn Nothing
) =
1181 [TargetStringFileStatus2
"pkg" noFileStatus
(dispPN pn
)]
1184 -- | Syntax: package : component
1186 -- > cabal build foo:foo
1187 -- > cabal build ./foo:foo
1188 -- > cabal build ./foo.cabal:foo
1189 syntaxForm2PackageComponent
:: [KnownPackage
] -> Syntax
1190 syntaxForm2PackageComponent ps
=
1191 syntaxForm2 render
$ \str1 fstatus1 str2
-> do
1192 guardPackage str1 fstatus1
1193 guardComponentName str2
1194 p
<- matchPackage ps str1 fstatus1
1196 KnownPackage
{pinfoId
, pinfoComponents
} ->
1197 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1198 c
<- matchComponentName pinfoComponents str2
1199 return (TargetComponent pinfoId
(cinfoName c
) WholeComponent
)
1200 -- TODO: the error here ought to say there's no component by that name in
1201 -- this package, and name the package
1202 KnownPackageName pn
->
1203 let cn
= mkUnqualComponentName str2
1204 in return (TargetComponentUnknown pn
(Left cn
) WholeComponent
)
1206 render
(TargetComponent p c WholeComponent
) =
1207 [TargetStringFileStatus2
(dispP p
) noFileStatus
(dispC p c
)]
1208 render
(TargetComponentUnknown pn
(Left cn
) WholeComponent
) =
1209 [TargetStringFileStatus2
(dispPN pn
) noFileStatus
(prettyShow cn
)]
1212 -- | Syntax: namespace : component
1214 -- > cabal build lib:foo exe:foo
1215 syntaxForm2KindComponent
:: [KnownComponent
] -> Syntax
1216 syntaxForm2KindComponent cs
=
1217 syntaxForm2 render
$ \str1 _fstatus1 str2
-> do
1218 ckind
<- matchComponentKind str1
1219 guardComponentName str2
1220 c
<- matchComponentKindAndName cs ckind str2
1221 return (TargetComponent
(cinfoPackageId c
) (cinfoName c
) WholeComponent
)
1223 render
(TargetComponent p c WholeComponent
) =
1224 [TargetStringFileStatus2
(dispCK c
) noFileStatus
(dispC p c
)]
1227 -- | Syntax: package : module
1229 -- > cabal build foo:Data.Foo
1230 -- > cabal build ./foo:Data.Foo
1231 -- > cabal build ./foo.cabal:Data.Foo
1232 syntaxForm2PackageModule
:: [KnownPackage
] -> Syntax
1233 syntaxForm2PackageModule ps
=
1234 syntaxForm2 render
$ \str1 fstatus1 str2
-> do
1235 guardPackage str1 fstatus1
1236 guardModuleName str2
1237 p
<- matchPackage ps str1 fstatus1
1239 KnownPackage
{pinfoId
, pinfoComponents
} ->
1240 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1241 let ms
= [(m
, c
) | c
<- pinfoComponents
, m
<- cinfoModules c
]
1242 (m
, c
) <- matchModuleNameAnd ms str2
1243 return (TargetComponent pinfoId
(cinfoName c
) (ModuleTarget m
))
1244 KnownPackageName pn
-> do
1245 m
<- matchModuleNameUnknown str2
1246 -- We assume the primary library component of the package:
1247 return (TargetComponentUnknown pn
(Right
$ CLibName LMainLibName
) (ModuleTarget m
))
1249 render
(TargetComponent p _c
(ModuleTarget m
)) =
1250 [TargetStringFileStatus2
(dispP p
) noFileStatus
(dispM m
)]
1253 -- | Syntax: component : module
1255 -- > cabal build foo:Data.Foo
1256 syntaxForm2ComponentModule
:: [KnownComponent
] -> Syntax
1257 syntaxForm2ComponentModule cs
=
1258 syntaxForm2 render
$ \str1 _fstatus1 str2
-> do
1259 guardComponentName str1
1260 guardModuleName str2
1261 c
<- matchComponentName cs str1
1262 orNoThingIn
"component" (cinfoStrName c
) $ do
1263 let ms
= cinfoModules c
1264 m
<- matchModuleName ms str2
1272 render
(TargetComponent p c
(ModuleTarget m
)) =
1273 [TargetStringFileStatus2
(dispC p c
) noFileStatus
(dispM m
)]
1276 -- | Syntax: package : filename
1278 -- > cabal build foo:Data/Foo.hs
1279 -- > cabal build ./foo:Data/Foo.hs
1280 -- > cabal build ./foo.cabal:Data/Foo.hs
1281 syntaxForm2PackageFile
:: [KnownPackage
] -> Syntax
1282 syntaxForm2PackageFile ps
=
1283 syntaxForm2 render
$ \str1 fstatus1 str2
-> do
1284 guardPackage str1 fstatus1
1285 p
<- matchPackage ps str1 fstatus1
1287 KnownPackage
{pinfoId
, pinfoComponents
} ->
1288 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1289 (filepath
, c
) <- matchComponentFile pinfoComponents str2
1290 return (TargetComponent pinfoId
(cinfoName c
) (FileTarget filepath
))
1291 KnownPackageName pn
->
1293 in -- We assume the primary library component of the package:
1294 return (TargetComponentUnknown pn
(Right
$ CLibName LMainLibName
) (FileTarget filepath
))
1296 render
(TargetComponent p _c
(FileTarget f
)) =
1297 [TargetStringFileStatus2
(dispP p
) noFileStatus f
]
1300 -- | Syntax: component : filename
1302 -- > cabal build foo:Data/Foo.hs
1303 syntaxForm2ComponentFile
:: [KnownComponent
] -> Syntax
1304 syntaxForm2ComponentFile cs
=
1305 syntaxForm2 render
$ \str1 _fstatus1 str2
-> do
1306 guardComponentName str1
1307 c
<- matchComponentName cs str1
1308 orNoThingIn
"component" (cinfoStrName c
) $ do
1309 (filepath
, _
) <- matchComponentFile
[c
] str2
1314 (FileTarget filepath
)
1317 render
(TargetComponent p c
(FileTarget f
)) =
1318 [TargetStringFileStatus2
(dispC p c
) noFileStatus f
]
1323 -- | Syntax: :all : filter
1325 -- > cabal build :all:tests
1326 syntaxForm3MetaAllFilter
:: Syntax
1327 syntaxForm3MetaAllFilter
=
1328 syntaxForm3 render
$ \str1 _fstatus1 str2 str3
-> do
1329 guardNamespaceMeta str1
1331 kfilter
<- matchComponentKindFilter str3
1332 return (TargetAllPackages
(Just kfilter
))
1334 render
(TargetAllPackages
(Just kfilter
)) =
1335 [TargetStringFileStatus3
"" noFileStatus
"all" (dispF kfilter
)]
1338 syntaxForm3MetaCwdFilter
:: [KnownPackage
] -> Syntax
1339 syntaxForm3MetaCwdFilter ps
=
1340 syntaxForm3 render
$ \str1 _fstatus1 str2 str3
-> do
1341 guardNamespaceMeta str1
1342 guardNamespaceCwd str2
1343 kfilter
<- matchComponentKindFilter str3
1344 return (TargetPackage TargetImplicitCwd pids
(Just kfilter
))
1346 pids
= [pinfoId | KnownPackage
{pinfoId
} <- ps
]
1347 render
(TargetPackage TargetImplicitCwd _
(Just kfilter
)) =
1348 [TargetStringFileStatus3
"" noFileStatus
"cwd" (dispF kfilter
)]
1351 -- | Syntax: :pkg : package name
1353 -- > cabal build :pkg:foo
1354 syntaxForm3MetaNamespacePackage
:: [KnownPackage
] -> Syntax
1355 syntaxForm3MetaNamespacePackage pinfo
=
1356 syntaxForm3 render
$ \str1 _fstatus1 str2 str3
-> do
1357 guardNamespaceMeta str1
1358 guardNamespacePackage str2
1359 guardPackageName str3
1360 p
<- matchPackage pinfo str3 noFileStatus
1362 KnownPackage
{pinfoId
} ->
1363 return (TargetPackage TargetExplicitNamed
[pinfoId
] Nothing
)
1364 KnownPackageName pn
->
1365 return (TargetPackageNamed pn Nothing
)
1367 render
(TargetPackage TargetExplicitNamed
[p
] Nothing
) =
1368 [TargetStringFileStatus3
"" noFileStatus
"pkg" (dispP p
)]
1369 render
(TargetPackageNamed pn Nothing
) =
1370 [TargetStringFileStatus3
"" noFileStatus
"pkg" (dispPN pn
)]
1373 -- | Syntax: package : namespace : component
1375 -- > cabal build foo:lib:foo
1376 -- > cabal build foo/:lib:foo
1377 -- > cabal build foo.cabal:lib:foo
1378 syntaxForm3PackageKindComponent
:: [KnownPackage
] -> Syntax
1379 syntaxForm3PackageKindComponent ps
=
1380 syntaxForm3 render
$ \str1 fstatus1 str2 str3
-> do
1381 guardPackage str1 fstatus1
1382 ckind
<- matchComponentKind str2
1383 guardComponentName str3
1384 p
<- matchPackage ps str1 fstatus1
1386 KnownPackage
{pinfoId
, pinfoComponents
} ->
1387 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1388 c
<- matchComponentKindAndName pinfoComponents ckind str3
1389 return (TargetComponent pinfoId
(cinfoName c
) WholeComponent
)
1390 KnownPackageName pn
->
1391 let cn
= mkComponentName pn ckind
(mkUnqualComponentName str3
)
1392 in return (TargetComponentUnknown pn
(Right cn
) WholeComponent
)
1394 render
(TargetComponent p c WholeComponent
) =
1395 [TargetStringFileStatus3
(dispP p
) noFileStatus
(dispCK c
) (dispC p c
)]
1396 render
(TargetComponentUnknown pn
(Right c
) WholeComponent
) =
1397 [TargetStringFileStatus3
(dispPN pn
) noFileStatus
(dispCK c
) (dispC
' pn c
)]
1400 -- | Syntax: package : component : module
1402 -- > cabal build foo:foo:Data.Foo
1403 -- > cabal build foo/:foo:Data.Foo
1404 -- > cabal build foo.cabal:foo:Data.Foo
1405 syntaxForm3PackageComponentModule
:: [KnownPackage
] -> Syntax
1406 syntaxForm3PackageComponentModule ps
=
1407 syntaxForm3 render
$ \str1 fstatus1 str2 str3
-> do
1408 guardPackage str1 fstatus1
1409 guardComponentName str2
1410 guardModuleName str3
1411 p
<- matchPackage ps str1 fstatus1
1413 KnownPackage
{pinfoId
, pinfoComponents
} ->
1414 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1415 c
<- matchComponentName pinfoComponents str2
1416 orNoThingIn
"component" (cinfoStrName c
) $ do
1417 let ms
= cinfoModules c
1418 m
<- matchModuleName ms str3
1419 return (TargetComponent pinfoId
(cinfoName c
) (ModuleTarget m
))
1420 KnownPackageName pn
-> do
1421 let cn
= mkUnqualComponentName str2
1422 m
<- matchModuleNameUnknown str3
1423 return (TargetComponentUnknown pn
(Left cn
) (ModuleTarget m
))
1425 render
(TargetComponent p c
(ModuleTarget m
)) =
1426 [TargetStringFileStatus3
(dispP p
) noFileStatus
(dispC p c
) (dispM m
)]
1427 render
(TargetComponentUnknown pn
(Left c
) (ModuleTarget m
)) =
1428 [TargetStringFileStatus3
(dispPN pn
) noFileStatus
(dispCN c
) (dispM m
)]
1431 -- | Syntax: namespace : component : module
1433 -- > cabal build lib:foo:Data.Foo
1434 syntaxForm3KindComponentModule
:: [KnownComponent
] -> Syntax
1435 syntaxForm3KindComponentModule cs
=
1436 syntaxForm3 render
$ \str1 _fstatus1 str2 str3
-> do
1437 ckind
<- matchComponentKind str1
1438 guardComponentName str2
1439 guardModuleName str3
1440 c
<- matchComponentKindAndName cs ckind str2
1441 orNoThingIn
"component" (cinfoStrName c
) $ do
1442 let ms
= cinfoModules c
1443 m
<- matchModuleName ms str3
1451 render
(TargetComponent p c
(ModuleTarget m
)) =
1452 [TargetStringFileStatus3
(dispCK c
) noFileStatus
(dispC p c
) (dispM m
)]
1455 -- | Syntax: package : component : filename
1457 -- > cabal build foo:foo:Data/Foo.hs
1458 -- > cabal build foo/:foo:Data/Foo.hs
1459 -- > cabal build foo.cabal:foo:Data/Foo.hs
1460 syntaxForm3PackageComponentFile
:: [KnownPackage
] -> Syntax
1461 syntaxForm3PackageComponentFile ps
=
1462 syntaxForm3 render
$ \str1 fstatus1 str2 str3
-> do
1463 guardPackage str1 fstatus1
1464 guardComponentName str2
1465 p
<- matchPackage ps str1 fstatus1
1467 KnownPackage
{pinfoId
, pinfoComponents
} ->
1468 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1469 c
<- matchComponentName pinfoComponents str2
1470 orNoThingIn
"component" (cinfoStrName c
) $ do
1471 (filepath
, _
) <- matchComponentFile
[c
] str3
1472 return (TargetComponent pinfoId
(cinfoName c
) (FileTarget filepath
))
1473 KnownPackageName pn
->
1474 let cn
= mkUnqualComponentName str2
1476 in return (TargetComponentUnknown pn
(Left cn
) (FileTarget filepath
))
1478 render
(TargetComponent p c
(FileTarget f
)) =
1479 [TargetStringFileStatus3
(dispP p
) noFileStatus
(dispC p c
) f
]
1480 render
(TargetComponentUnknown pn
(Left c
) (FileTarget f
)) =
1481 [TargetStringFileStatus3
(dispPN pn
) noFileStatus
(dispCN c
) f
]
1484 -- | Syntax: namespace : component : filename
1486 -- > cabal build lib:foo:Data/Foo.hs
1487 syntaxForm3KindComponentFile
:: [KnownComponent
] -> Syntax
1488 syntaxForm3KindComponentFile cs
=
1489 syntaxForm3 render
$ \str1 _fstatus1 str2 str3
-> do
1490 ckind
<- matchComponentKind str1
1491 guardComponentName str2
1492 c
<- matchComponentKindAndName cs ckind str2
1493 orNoThingIn
"component" (cinfoStrName c
) $ do
1494 (filepath
, _
) <- matchComponentFile
[c
] str3
1499 (FileTarget filepath
)
1502 render
(TargetComponent p c
(FileTarget f
)) =
1503 [TargetStringFileStatus3
(dispCK c
) noFileStatus
(dispC p c
) f
]
1506 syntaxForm3NamespacePackageFilter
:: [KnownPackage
] -> Syntax
1507 syntaxForm3NamespacePackageFilter ps
=
1508 syntaxForm3 render
$ \str1 _fstatus1 str2 str3
-> do
1509 guardNamespacePackage str1
1510 guardPackageName str2
1511 p
<- matchPackage ps str2 noFileStatus
1512 kfilter
<- matchComponentKindFilter str3
1514 KnownPackage
{pinfoId
} ->
1515 return (TargetPackage TargetExplicitNamed
[pinfoId
] (Just kfilter
))
1516 KnownPackageName pn
->
1517 return (TargetPackageNamed pn
(Just kfilter
))
1519 render
(TargetPackage TargetExplicitNamed
[p
] (Just kfilter
)) =
1520 [TargetStringFileStatus3
"pkg" noFileStatus
(dispP p
) (dispF kfilter
)]
1521 render
(TargetPackageNamed pn
(Just kfilter
)) =
1522 [TargetStringFileStatus3
"pkg" noFileStatus
(dispPN pn
) (dispF kfilter
)]
1527 syntaxForm4MetaNamespacePackageFilter
:: [KnownPackage
] -> Syntax
1528 syntaxForm4MetaNamespacePackageFilter ps
=
1529 syntaxForm4 render
$ \str1 str2 str3 str4
-> do
1530 guardNamespaceMeta str1
1531 guardNamespacePackage str2
1532 guardPackageName str3
1533 p
<- matchPackage ps str3 noFileStatus
1534 kfilter
<- matchComponentKindFilter str4
1536 KnownPackage
{pinfoId
} ->
1537 return (TargetPackage TargetExplicitNamed
[pinfoId
] (Just kfilter
))
1538 KnownPackageName pn
->
1539 return (TargetPackageNamed pn
(Just kfilter
))
1541 render
(TargetPackage TargetExplicitNamed
[p
] (Just kfilter
)) =
1542 [TargetStringFileStatus4
"" "pkg" (dispP p
) (dispF kfilter
)]
1543 render
(TargetPackageNamed pn
(Just kfilter
)) =
1544 [TargetStringFileStatus4
"" "pkg" (dispPN pn
) (dispF kfilter
)]
1547 -- | Syntax: :pkg : package : namespace : component
1549 -- > cabal build :pkg:foo:lib:foo
1550 syntaxForm5MetaNamespacePackageKindComponent
:: [KnownPackage
] -> Syntax
1551 syntaxForm5MetaNamespacePackageKindComponent ps
=
1552 syntaxForm5 render
$ \str1 str2 str3 str4 str5
-> do
1553 guardNamespaceMeta str1
1554 guardNamespacePackage str2
1555 guardPackageName str3
1556 ckind
<- matchComponentKind str4
1557 guardComponentName str5
1558 p
<- matchPackage ps str3 noFileStatus
1560 KnownPackage
{pinfoId
, pinfoComponents
} ->
1561 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1562 c
<- matchComponentKindAndName pinfoComponents ckind str5
1563 return (TargetComponent pinfoId
(cinfoName c
) WholeComponent
)
1564 KnownPackageName pn
->
1565 let cn
= mkComponentName pn ckind
(mkUnqualComponentName str5
)
1566 in return (TargetComponentUnknown pn
(Right cn
) WholeComponent
)
1568 render
(TargetComponent p c WholeComponent
) =
1569 [TargetStringFileStatus5
"" "pkg" (dispP p
) (dispCK c
) (dispC p c
)]
1570 render
(TargetComponentUnknown pn
(Right c
) WholeComponent
) =
1571 [TargetStringFileStatus5
"" "pkg" (dispPN pn
) (dispCK c
) (dispC
' pn c
)]
1574 -- | Syntax: :pkg : package : namespace : component : module : module
1576 -- > cabal build :pkg:foo:lib:foo:module:Data.Foo
1577 syntaxForm7MetaNamespacePackageKindComponentNamespaceModule
1578 :: [KnownPackage
] -> Syntax
1579 syntaxForm7MetaNamespacePackageKindComponentNamespaceModule ps
=
1580 syntaxForm7 render
$ \str1 str2 str3 str4 str5 str6 str7
-> do
1581 guardNamespaceMeta str1
1582 guardNamespacePackage str2
1583 guardPackageName str3
1584 ckind
<- matchComponentKind str4
1585 guardComponentName str5
1586 guardNamespaceModule str6
1587 p
<- matchPackage ps str3 noFileStatus
1589 KnownPackage
{pinfoId
, pinfoComponents
} ->
1590 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1591 c
<- matchComponentKindAndName pinfoComponents ckind str5
1592 orNoThingIn
"component" (cinfoStrName c
) $ do
1593 let ms
= cinfoModules c
1594 m
<- matchModuleName ms str7
1595 return (TargetComponent pinfoId
(cinfoName c
) (ModuleTarget m
))
1596 KnownPackageName pn
-> do
1597 let cn
= mkComponentName pn ckind
(mkUnqualComponentName str2
)
1598 m
<- matchModuleNameUnknown str7
1599 return (TargetComponentUnknown pn
(Right cn
) (ModuleTarget m
))
1601 render
(TargetComponent p c
(ModuleTarget m
)) =
1602 [ TargetStringFileStatus7
1611 render
(TargetComponentUnknown pn
(Right c
) (ModuleTarget m
)) =
1612 [ TargetStringFileStatus7
1623 -- | Syntax: :pkg : package : namespace : component : file : filename
1625 -- > cabal build :pkg:foo:lib:foo:file:Data/Foo.hs
1626 syntaxForm7MetaNamespacePackageKindComponentNamespaceFile
1627 :: [KnownPackage
] -> Syntax
1628 syntaxForm7MetaNamespacePackageKindComponentNamespaceFile ps
=
1629 syntaxForm7 render
$ \str1 str2 str3 str4 str5 str6 str7
-> do
1630 guardNamespaceMeta str1
1631 guardNamespacePackage str2
1632 guardPackageName str3
1633 ckind
<- matchComponentKind str4
1634 guardComponentName str5
1635 guardNamespaceFile str6
1636 p
<- matchPackage ps str3 noFileStatus
1638 KnownPackage
{pinfoId
, pinfoComponents
} ->
1639 orNoThingIn
"package" (prettyShow
(packageName pinfoId
)) $ do
1640 c
<- matchComponentKindAndName pinfoComponents ckind str5
1641 orNoThingIn
"component" (cinfoStrName c
) $ do
1642 (filepath
, _
) <- matchComponentFile
[c
] str7
1643 return (TargetComponent pinfoId
(cinfoName c
) (FileTarget filepath
))
1644 KnownPackageName pn
->
1645 let cn
= mkComponentName pn ckind
(mkUnqualComponentName str5
)
1647 in return (TargetComponentUnknown pn
(Right cn
) (FileTarget filepath
))
1649 render
(TargetComponent p c
(FileTarget f
)) =
1650 [ TargetStringFileStatus7
1659 render
(TargetComponentUnknown pn
(Right c
) (FileTarget f
)) =
1660 [ TargetStringFileStatus7
1671 ---------------------------------------
1675 type Match1
= String -> FileStatus
-> Match TargetSelector
1680 -> Match TargetSelector
1686 -> Match TargetSelector
1692 -> Match TargetSelector
1699 -> Match TargetSelector
1708 -> Match TargetSelector
1710 syntaxForm1
:: Renderer
-> Match1
-> Syntax
1711 syntaxForm2
:: Renderer
-> Match2
-> Syntax
1712 syntaxForm3
:: Renderer
-> Match3
-> Syntax
1713 syntaxForm4
:: Renderer
-> Match4
-> Syntax
1714 syntaxForm5
:: Renderer
-> Match5
-> Syntax
1715 syntaxForm7
:: Renderer
-> Match7
-> Syntax
1716 syntaxForm1 render f
=
1717 Syntax QL1 match render
1719 match
= \(TargetStringFileStatus1 str1 fstatus1
) ->
1722 syntaxForm2 render f
=
1723 Syntax QL2 match render
1725 match
= \(TargetStringFileStatus2 str1 fstatus1 str2
) ->
1726 f str1 fstatus1 str2
1728 syntaxForm3 render f
=
1729 Syntax QL3 match render
1731 match
= \(TargetStringFileStatus3 str1 fstatus1 str2 str3
) ->
1732 f str1 fstatus1 str2 str3
1734 syntaxForm4 render f
=
1735 Syntax QLFull match render
1737 match
(TargetStringFileStatus4 str1 str2 str3 str4
) =
1738 f str1 str2 str3 str4
1741 syntaxForm5 render f
=
1742 Syntax QLFull match render
1744 match
(TargetStringFileStatus5 str1 str2 str3 str4 str5
) =
1745 f str1 str2 str3 str4 str5
1748 syntaxForm7 render f
=
1749 Syntax QLFull match render
1751 match
(TargetStringFileStatus7 str1 str2 str3 str4 str5 str6 str7
) =
1752 f str1 str2 str3 str4 str5 str6 str7
1755 dispP
:: Package p
=> p
-> String
1756 dispP
= prettyShow
. packageName
1758 dispPN
:: PackageName
-> String
1761 dispC
:: PackageId
-> ComponentName
-> String
1762 dispC
= componentStringName
. packageName
1764 dispC
' :: PackageName
-> ComponentName
-> String
1765 dispC
' = componentStringName
1767 dispCN
:: UnqualComponentName
-> String
1770 dispK
:: ComponentKind
-> String
1771 dispK
= showComponentKindShort
1773 dispCK
:: ComponentName
-> String
1774 dispCK
= dispK
. componentKind
1776 dispF
:: ComponentKind
-> String
1777 dispF
= showComponentKindFilterShort
1779 dispM
:: ModuleName
-> String
1782 -------------------------------
1783 -- Package and component info
1786 data KnownTargets
= KnownTargets
1787 { knownPackagesAll
:: [KnownPackage
]
1788 , knownPackagesPrimary
:: [KnownPackage
]
1789 , knownPackagesOther
:: [KnownPackage
]
1790 , knownComponentsAll
:: [KnownComponent
]
1791 , knownComponentsPrimary
:: [KnownComponent
]
1792 , knownComponentsOther
:: [KnownComponent
]
1798 { pinfoId
:: PackageId
1799 , pinfoDirectory
:: Maybe (FilePath, FilePath)
1800 , pinfoPackageFile
:: Maybe (FilePath, FilePath)
1801 , pinfoComponents
:: [KnownComponent
]
1804 { pinfoName
:: PackageName
1808 data KnownComponent
= KnownComponent
1809 { cinfoName
:: ComponentName
1810 , cinfoStrName
:: ComponentStringName
1811 , cinfoPackageId
:: PackageId
1812 , cinfoSrcDirs
:: [FilePath]
1813 , cinfoModules
:: [ModuleName
]
1814 , cinfoHsFiles
:: [FilePath] -- other hs files (like main.hs)
1815 , cinfoCFiles
:: [FilePath]
1816 , cinfoJsFiles
:: [FilePath]
1820 type ComponentStringName
= String
1822 knownPackageName
:: KnownPackage
-> PackageName
1823 knownPackageName KnownPackage
{pinfoId
} = packageName pinfoId
1824 knownPackageName KnownPackageName
{pinfoName
} = pinfoName
1826 emptyKnownTargets
:: KnownTargets
1827 emptyKnownTargets
= KnownTargets
[] [] [] [] [] []
1831 . (Applicative m
, Monad m
)
1833 -> [PackageSpecifier
(SourcePackage
(PackageLocation a
))]
1835 getKnownTargets dirActions
@DirActions
{..} pkgs
= do
1836 pinfo
<- traverse
(collectKnownPackageInfo dirActions
) pkgs
1837 cwd
<- getCurrentDirectory
1838 (ppinfo
, opinfo
) <- selectPrimaryPackage cwd pinfo
1841 { knownPackagesAll
= pinfo
1842 , knownPackagesPrimary
= ppinfo
1843 , knownPackagesOther
= opinfo
1844 , knownComponentsAll
= allComponentsIn pinfo
1845 , knownComponentsPrimary
= allComponentsIn ppinfo
1846 , knownComponentsOther
= allComponentsIn opinfo
1849 mPkgDir
:: KnownPackage
-> Maybe FilePath
1850 mPkgDir KnownPackage
{pinfoDirectory
= Just
(dir
, _
)} = Just dir
1853 selectPrimaryPackage
1856 -> m
([KnownPackage
], [KnownPackage
])
1857 selectPrimaryPackage _
[] = return ([], [])
1858 selectPrimaryPackage cwd
(pkg
: packages
) = do
1859 (ppinfo
, opinfo
) <- selectPrimaryPackage cwd packages
1860 isPkgDirCwd
<- maybe (pure
False) (compareFilePath dirActions cwd
) (mPkgDir pkg
)
1861 return (if isPkgDirCwd
then (pkg
: ppinfo
, opinfo
) else (ppinfo
, pkg
: opinfo
))
1863 allComponentsIn ps
=
1864 [c | KnownPackage
{pinfoComponents
} <- ps
, c
<- pinfoComponents
]
1866 collectKnownPackageInfo
1867 :: (Applicative m
, Monad m
)
1869 -> PackageSpecifier
(SourcePackage
(PackageLocation a
))
1871 collectKnownPackageInfo _
(NamedPackage pkgname _props
) =
1872 return (KnownPackageName pkgname
)
1873 collectKnownPackageInfo
1874 dirActions
@DirActions
{..}
1875 ( SpecificSourcePackage
1877 { srcpkgDescription
= pkg
1878 , srcpkgSource
= loc
1881 (pkgdir
, pkgfile
) <-
1883 -- TODO: local tarballs, remote tarballs etc
1884 LocalUnpackedPackage dir
-> do
1885 dirabs
<- canonicalizePath dir
1886 dirrel
<- makeRelativeToCwd dirActions dirabs
1887 -- TODO: ought to get this earlier in project reading
1888 let fileabs
= dirabs
</> prettyShow
(packageName pkg
) <.> "cabal"
1889 filerel
= dirrel
</> prettyShow
(packageName pkg
) <.> "cabal"
1890 exists
<- doesFileExist fileabs
1892 ( Just
(dirabs
, dirrel
)
1893 , if exists
then Just
(fileabs
, filerel
) else Nothing
1895 _
-> return (Nothing
, Nothing
)
1898 { pinfoId
= packageId pkg
1899 , pinfoDirectory
= pkgdir
1900 , pinfoPackageFile
= pkgfile
1902 collectKnownComponentInfo
1903 (flattenPackageDescription pkg
)
1907 collectKnownComponentInfo
:: PackageDescription
-> [KnownComponent
]
1908 collectKnownComponentInfo pkg
=
1910 { cinfoName
= componentName c
1911 , cinfoStrName
= componentStringName
(packageName pkg
) (componentName c
)
1912 , cinfoPackageId
= packageId pkg
1913 , cinfoSrcDirs
= ordNub
(map getSymbolicPath
(hsSourceDirs bi
))
1914 , cinfoModules
= ordNub
(componentModules c
)
1915 , cinfoHsFiles
= ordNub
(componentHsFiles c
)
1916 , cinfoCFiles
= ordNub
(cSources bi
)
1917 , cinfoJsFiles
= ordNub
(jsSources bi
)
1919 | c
<- pkgComponents pkg
1920 , let bi
= componentBuildInfo c
1923 componentStringName
:: PackageName
-> ComponentName
-> ComponentStringName
1924 componentStringName pkgname
(CLibName LMainLibName
) = prettyShow pkgname
1925 componentStringName _
(CLibName
(LSubLibName name
)) = unUnqualComponentName name
1926 componentStringName _
(CFLibName name
) = unUnqualComponentName name
1927 componentStringName _
(CExeName name
) = unUnqualComponentName name
1928 componentStringName _
(CTestName name
) = unUnqualComponentName name
1929 componentStringName _
(CBenchName name
) = unUnqualComponentName name
1931 componentModules
:: Component
-> [ModuleName
]
1932 -- I think it's unlikely users will ask to build a requirement
1933 -- which is not mentioned locally.
1934 componentModules
(CLib lib
) = explicitLibModules lib
1935 componentModules
(CFLib flib
) = foreignLibModules flib
1936 componentModules
(CExe exe
) = exeModules exe
1937 componentModules
(CTest test
) = testModules test
1938 componentModules
(CBench bench
) = benchmarkModules bench
1940 componentHsFiles
:: Component
-> [FilePath]
1941 componentHsFiles
(CExe exe
) = [modulePath exe
]
1945 { testInterface
= TestSuiteExeV10 _ mainfile
1951 { benchmarkInterface
= BenchmarkExeV10 _ mainfile
1954 componentHsFiles _
= []
1956 ------------------------------
1957 -- Matching meta targets
1960 guardNamespaceMeta
:: String -> Match
()
1961 guardNamespaceMeta
= guardToken
[""] "meta namespace"
1963 guardMetaAll
:: String -> Match
()
1964 guardMetaAll
= guardToken
["all"] "meta-target 'all'"
1966 guardNamespacePackage
:: String -> Match
()
1967 guardNamespacePackage
= guardToken
["pkg", "package"] "'pkg' namespace"
1969 guardNamespaceCwd
:: String -> Match
()
1970 guardNamespaceCwd
= guardToken
["cwd"] "'cwd' namespace"
1972 guardNamespaceModule
:: String -> Match
()
1973 guardNamespaceModule
= guardToken
["mod", "module"] "'module' namespace"
1975 guardNamespaceFile
:: String -> Match
()
1976 guardNamespaceFile
= guardToken
["file"] "'file' namespace"
1978 guardToken
:: [String] -> String -> String -> Match
()
1979 guardToken tokens msg s
1980 | caseFold s `
elem` tokens
= increaseConfidence
1981 |
otherwise = matchErrorExpected msg s
1983 ------------------------------
1984 -- Matching component kinds
1987 componentKind
:: ComponentName
-> ComponentKind
1988 componentKind
(CLibName _
) = LibKind
1989 componentKind
(CFLibName _
) = FLibKind
1990 componentKind
(CExeName _
) = ExeKind
1991 componentKind
(CTestName _
) = TestKind
1992 componentKind
(CBenchName _
) = BenchKind
1994 cinfoKind
:: KnownComponent
-> ComponentKind
1995 cinfoKind
= componentKind
. cinfoName
1997 matchComponentKind
:: String -> Match ComponentKind
1998 matchComponentKind s
1999 | s
' `
elem` liblabels
= increaseConfidence
>> return LibKind
2000 | s
' `
elem` fliblabels
= increaseConfidence
>> return FLibKind
2001 | s
' `
elem` exelabels
= increaseConfidence
>> return ExeKind
2002 | s
' `
elem` testlabels
= increaseConfidence
>> return TestKind
2003 | s
' `
elem` benchlabels
= increaseConfidence
>> return BenchKind
2004 |
otherwise = matchErrorExpected
"component kind" s
2007 liblabels
= ["lib", "library"]
2008 fliblabels
= ["flib", "foreign-library"]
2009 exelabels
= ["exe", "executable"]
2010 testlabels
= ["tst", "test", "test-suite"]
2011 benchlabels
= ["bench", "benchmark"]
2013 matchComponentKindFilter
:: String -> Match ComponentKind
2014 matchComponentKindFilter s
2015 | s
' `
elem` liblabels
= increaseConfidence
>> return LibKind
2016 | s
' `
elem` fliblabels
= increaseConfidence
>> return FLibKind
2017 | s
' `
elem` exelabels
= increaseConfidence
>> return ExeKind
2018 | s
' `
elem` testlabels
= increaseConfidence
>> return TestKind
2019 | s
' `
elem` benchlabels
= increaseConfidence
>> return BenchKind
2020 |
otherwise = matchErrorExpected
"component kind filter" s
2023 liblabels
= ["libs", "libraries"]
2024 fliblabels
= ["flibs", "foreign-libraries"]
2025 exelabels
= ["exes", "executables"]
2026 testlabels
= ["tests", "test-suites"]
2027 benchlabels
= ["benches", "benchmarks"]
2029 showComponentKind
:: ComponentKind
-> String
2030 showComponentKind LibKind
= "library"
2031 showComponentKind FLibKind
= "foreign library"
2032 showComponentKind ExeKind
= "executable"
2033 showComponentKind TestKind
= "test-suite"
2034 showComponentKind BenchKind
= "benchmark"
2036 showComponentKindShort
:: ComponentKind
-> String
2037 showComponentKindShort LibKind
= "lib"
2038 showComponentKindShort FLibKind
= "flib"
2039 showComponentKindShort ExeKind
= "exe"
2040 showComponentKindShort TestKind
= "test"
2041 showComponentKindShort BenchKind
= "bench"
2043 showComponentKindFilterShort
:: ComponentKind
-> String
2044 showComponentKindFilterShort LibKind
= "libs"
2045 showComponentKindFilterShort FLibKind
= "flibs"
2046 showComponentKindFilterShort ExeKind
= "exes"
2047 showComponentKindFilterShort TestKind
= "tests"
2048 showComponentKindFilterShort BenchKind
= "benchmarks"
2050 ------------------------------
2051 -- Matching package targets
2054 guardPackage
:: String -> FileStatus
-> Match
()
2055 guardPackage str fstatus
=
2056 guardPackageName str
2057 <|
> guardPackageDir str fstatus
2058 <|
> guardPackageFile str fstatus
2060 guardPackageName
:: String -> Match
()
2062 | validPackageName s
= increaseConfidence
2063 |
otherwise = matchErrorExpected
"package name" s
2065 validPackageName
:: String -> Bool
2066 validPackageName s
=
2067 all validPackageNameChar s
2070 validPackageNameChar c
= isAlphaNum c || c
== '-'
2072 guardPackageDir
:: String -> FileStatus
-> Match
()
2073 guardPackageDir _
(FileStatusExistsDir _
) = increaseConfidence
2074 guardPackageDir str _
= matchErrorExpected
"package directory" str
2076 guardPackageFile
:: String -> FileStatus
-> Match
()
2077 guardPackageFile _
(FileStatusExistsFile file
)
2078 | takeExtension file
== ".cabal" =
2080 guardPackageFile str _
= matchErrorExpected
"package .cabal file" str
2082 matchPackage
:: [KnownPackage
] -> String -> FileStatus
-> Match KnownPackage
2083 matchPackage pinfo
= \str fstatus
->
2084 orNoThingIn
"project" "" $
2085 matchPackageName pinfo str
2086 <//> ( matchPackageNameUnknown str
2087 <|
> matchPackageDir pinfo str fstatus
2088 <|
> matchPackageFile pinfo str fstatus
2091 matchPackageName
:: [KnownPackage
] -> String -> Match KnownPackage
2092 matchPackageName ps
= \str
-> do
2093 guard (validPackageName str
)
2097 (map (prettyShow
. knownPackageName
) ps
)
2098 $ increaseConfidenceFor
2099 $ matchInexactly caseFold
(prettyShow
. knownPackageName
) ps str
2101 matchPackageNameUnknown
:: String -> Match KnownPackage
2102 matchPackageNameUnknown str
= do
2103 pn
<- matchParse str
2104 unknownMatch
(KnownPackageName pn
)
2110 -> Match KnownPackage
2111 matchPackageDir ps
= \str fstatus
->
2113 FileStatusExistsDir canondir
->
2114 orNoSuchThing
"package directory" str
(map (snd . fst) dirs
) $
2115 increaseConfidenceFor
$
2117 matchExactly
(fst . fst) dirs canondir
2122 | p
@KnownPackage
{pinfoDirectory
= Just
(dabs
, drel
)} <- ps
2125 matchPackageFile
:: [KnownPackage
] -> String -> FileStatus
-> Match KnownPackage
2126 matchPackageFile ps
= \str fstatus
-> do
2128 FileStatusExistsFile canonfile
->
2129 orNoSuchThing
"package .cabal file" str
(map (snd . fst) files
) $
2130 increaseConfidenceFor
$
2132 matchExactly
(fst . fst) files canonfile
2137 | p
@KnownPackage
{pinfoPackageFile
= Just
(fabs
, frel
)} <- ps
2140 -- TODO: test outcome when dir exists but doesn't match any known one
2142 -- TODO: perhaps need another distinction, vs no such thing, point is the
2143 -- thing is not known, within the project, but could be outside project
2145 ------------------------------
2146 -- Matching component targets
2149 guardComponentName
:: String -> Match
()
2150 guardComponentName s
2151 |
all validComponentChar s
2154 |
otherwise = matchErrorExpected
"component name" s
2156 validComponentChar c
=
2163 matchComponentName
:: [KnownComponent
] -> String -> Match KnownComponent
2164 matchComponentName cs str
=
2165 orNoSuchThing
"component" str
(map cinfoStrName cs
) $
2166 increaseConfidenceFor
$
2167 matchInexactly caseFold cinfoStrName cs str
2169 matchComponentKindAndName
2173 -> Match KnownComponent
2174 matchComponentKindAndName cs ckind str
=
2176 (showComponentKind ckind
++ " component")
2179 $ increaseConfidenceFor
2181 (\(ck
, cn
) -> (ck
, caseFold cn
))
2182 (\c
-> (cinfoKind c
, cinfoStrName c
))
2186 render c
= showComponentKindShort
(cinfoKind c
) ++ ":" ++ cinfoStrName c
2188 ------------------------------
2189 -- Matching module targets
2192 guardModuleName
:: String -> Match
()
2194 case simpleParsec s
:: Maybe ModuleName
of
2195 Just _
-> increaseConfidence
2197 |
all validModuleChar s
2200 |
otherwise -> matchErrorExpected
"module name" s
2202 validModuleChar c
= isAlphaNum c || c
== '.' || c
== '_
' || c
== '\''
2204 matchModuleName
:: [ModuleName
] -> String -> Match ModuleName
2205 matchModuleName ms str
=
2206 orNoSuchThing
"module" str
(map prettyShow ms
) $
2207 increaseConfidenceFor
$
2208 matchInexactly caseFold prettyShow ms str
2210 matchModuleNameAnd
:: [(ModuleName
, a
)] -> String -> Match
(ModuleName
, a
)
2211 matchModuleNameAnd ms str
=
2212 orNoSuchThing
"module" str
(map (prettyShow
. fst) ms
) $
2213 increaseConfidenceFor
$
2214 matchInexactly caseFold
(prettyShow
. fst) ms str
2216 matchModuleNameUnknown
:: String -> Match ModuleName
2217 matchModuleNameUnknown str
=
2218 expecting
"module" str
$
2219 increaseConfidenceFor
$
2222 ------------------------------
2223 -- Matching file targets
2226 matchPackageDirectoryPrefix
2229 -> Match
(FilePath, KnownPackage
)
2230 matchPackageDirectoryPrefix ps
(FileStatusExistsFile filepath
) =
2231 increaseConfidenceFor
$
2232 matchDirectoryPrefix pkgdirs filepath
2236 | p
@KnownPackage
{pinfoDirectory
= Just
(dir
, _
)} <- ps
2238 matchPackageDirectoryPrefix _ _
= mzero
2243 -> Match
(FilePath, KnownComponent
)
2244 matchComponentFile cs str
=
2245 orNoSuchThing
"file" str
[] $
2246 matchComponentModuleFile cs str
2247 <|
> matchComponentOtherFile cs str
2249 matchComponentOtherFile
2252 -> Match
(FilePath, KnownComponent
)
2253 matchComponentOtherFile cs
=
2255 [ (normalise
(srcdir
</> file
), c
)
2257 , srcdir
<- cinfoSrcDirs c
2265 matchComponentModuleFile
2268 -> Match
(FilePath, KnownComponent
)
2269 matchComponentModuleFile cs str
= do
2271 [ (normalise
(d
</> toFilePath m
), c
)
2273 , d
<- cinfoSrcDirs c
2274 , m
<- cinfoModules c
2276 (dropExtension
(normalise str
)) -- Drop the extension because FileTarget
2277 -- is stored without the extension
2281 -- | Compare two filepaths for equality using DirActions' canonicalizePath
2282 -- to normalize AND canonicalize filepaths before comparison.
2284 :: (Applicative m
, Monad m
)
2289 compareFilePath DirActions
{..} fp1 fp2
2290 | equalFilePath fp1 fp2
= pure
True -- avoid unnecessary IO if we can match earlier
2292 c1
<- canonicalizePath fp1
2293 c2
<- canonicalizePath fp2
2294 pure
$ equalFilePath c1 c2
2296 matchFile
:: [(FilePath, a
)] -> FilePath -> Match
(FilePath, a
)
2298 increaseConfidenceFor
2299 . matchInexactly caseFold
fst fs
2301 matchDirectoryPrefix
:: [(FilePath, a
)] -> FilePath -> Match
(FilePath, a
)
2302 matchDirectoryPrefix dirs filepath
=
2306 , file
<- maybeToList (stripDirectory dir
)
2309 stripDirectory
:: FilePath -> Maybe FilePath
2310 stripDirectory dir
=
2311 joinPath `
fmap` stripPrefix
(splitDirectories dir
) filepathsplit
2313 filepathsplit
= splitDirectories filepath
2315 ------------------------------
2319 -- | A matcher embodies a way to match some input as being some recognised
2320 -- value. In particular it deals with multiple and ambiguous matches.
2322 -- There are various matcher primitives ('matchExactly', 'matchInexactly'),
2323 -- ways to combine matchers ('matchPlus', 'matchPlusShadowing') and finally we
2324 -- can run a matcher against an input using 'findMatch'.
2326 = NoMatch
!Confidence
[MatchError
]
2327 | Match
!MatchClass
!Confidence
[a
]
2330 -- | The kind of match, inexact or exact. We keep track of this so we can
2331 -- prefer exact over inexact matches. The 'Ord' here is important: we try
2332 -- to maximise this, so 'Exact' is the top value and 'Inexact' the bottom.
2334 = -- | Matches an unknown thing e.g. parses as a package
2335 -- name without it being a specific known package
2337 |
-- | Matches a known thing inexactly
2338 -- e.g. matches a known package case insensitively
2340 |
-- | Exactly matches a known thing,
2341 -- e.g. matches a known package case sensitively
2343 deriving (Show, Eq
, Ord
)
2345 type Confidence
= Int
2348 = MatchErrorExpected
String String -- thing got
2349 | MatchErrorNoSuch
String String [String] -- thing got alts
2350 | MatchErrorIn
String String MatchError
-- kind thing
2353 instance Functor Match
where
2354 fmap _
(NoMatch d ms
) = NoMatch d ms
2355 fmap f
(Match m d xs
) = Match m d
(fmap f xs
)
2357 instance Applicative Match
where
2358 pure a
= Match Exact
0 [a
]
2361 instance Alternative Match
where
2362 empty = NoMatch
0 []
2365 instance Monad Match
where
2367 NoMatch d ms
>>= _
= NoMatch d ms
2368 Match m d xs
>>= f
=
2369 -- To understand this, it needs to be read in context with the
2370 -- implementation of 'matchPlus' below
2371 case msum (map f xs
) of
2372 Match m
' d
' xs
' -> Match
(min m m
') (d
+ d
') xs
'
2373 -- The minimum match class is the one we keep. The match depth is
2374 -- tracked but not used in the Match case.
2376 NoMatch d
' ms
-> NoMatch
(d
+ d
') ms
2378 -- Here is where we transfer the depth we were keeping track of in
2379 -- the Match case over to the NoMatch case where it finally gets used.
2381 instance MonadPlus Match
where
2385 (<//>) :: Match a
-> Match a
-> Match a
2386 (<//>) = matchPlusShadowing
2390 -- | Combine two matchers. Exact matches are used over inexact matches
2391 -- but if we have multiple exact, or inexact then the we collect all the
2392 -- ambiguous matches.
2394 -- This operator is associative, has unit 'mzero' and is also commutative.
2395 matchPlus
:: Match a
-> Match a
-> Match a
2396 matchPlus a
@(Match _ _ _
) (NoMatch _ _
) = a
2397 matchPlus
(NoMatch _ _
) b
@(Match _ _ _
) = b
2398 matchPlus a
@(NoMatch d_a ms_a
) b
@(NoMatch d_b ms_b
)
2399 | d_a
> d_b
= a
-- We only really make use of the depth in the NoMatch case.
2401 |
otherwise = NoMatch d_a
(ms_a
++ ms_b
)
2402 matchPlus a
@(Match m_a d_a xs_a
) b
@(Match m_b d_b xs_b
)
2403 | m_a
> m_b
= a
-- exact over inexact
2404 | m_a
< m_b
= b
-- exact over inexact
2405 |
otherwise = Match m_a
(max d_a d_b
) (xs_a
++ xs_b
)
2407 -- | Combine two matchers. This is similar to 'matchPlus' with the
2408 -- difference that an exact match from the left matcher shadows any exact
2409 -- match on the right. Inexact matches are still collected however.
2411 -- This operator is associative, has unit 'mzero' and is not commutative.
2412 matchPlusShadowing
:: Match a
-> Match a
-> Match a
2413 matchPlusShadowing a
@(Match Exact _ _
) _
= a
2414 matchPlusShadowing a b
= matchPlus a b
2416 ------------------------------
2417 -- Various match primitives
2420 matchErrorExpected
:: String -> String -> Match a
2421 matchErrorExpected thing got
= NoMatch
0 [MatchErrorExpected thing got
]
2423 matchErrorNoSuch
:: String -> String -> [String] -> Match a
2424 matchErrorNoSuch thing got alts
= NoMatch
0 [MatchErrorNoSuch thing got alts
]
2426 expecting
:: String -> String -> Match a
-> Match a
2427 expecting thing got
(NoMatch
0 _
) = matchErrorExpected thing got
2430 orNoSuchThing
:: String -> String -> [String] -> Match a
-> Match a
2431 orNoSuchThing thing got alts
(NoMatch
0 _
) = matchErrorNoSuch thing got alts
2432 orNoSuchThing _ _ _ m
= m
2434 orNoThingIn
:: String -> String -> Match a
-> Match a
2435 orNoThingIn kind name
(NoMatch n ms
) =
2436 NoMatch n
[MatchErrorIn kind name m | m
<- ms
]
2437 orNoThingIn _ _ m
= m
2439 increaseConfidence
:: Match
()
2440 increaseConfidence
= Match Exact
1 [()]
2442 increaseConfidenceFor
:: Match a
-> Match a
2443 increaseConfidenceFor m
= m
>>= \r -> increaseConfidence
>> return r
2445 nubMatchesBy
:: (a
-> a
-> Bool) -> Match a
-> Match a
2446 nubMatchesBy _
(NoMatch d msgs
) = NoMatch d msgs
2447 nubMatchesBy eq
(Match m d xs
) = Match m d
(nubBy eq xs
)
2449 -- | Lift a list of matches to an exact match.
2450 exactMatches
, inexactMatches
:: [a
] -> Match a
2451 exactMatches
[] = mzero
2452 exactMatches xs
= Match Exact
0 xs
2453 inexactMatches
[] = mzero
2454 inexactMatches xs
= Match Inexact
0 xs
2456 unknownMatch
:: a
-> Match a
2457 unknownMatch x
= Match Unknown
0 [x
]
2459 tryEach
:: [a
] -> Match a
2460 tryEach
= exactMatches
2462 ------------------------------
2463 -- Top level match runner
2466 -- | Given a matcher and a key to look up, use the matcher to find all the
2467 -- possible matches. There may be 'None', a single 'Unambiguous' match or
2468 -- you may have an 'Ambiguous' match with several possibilities.
2469 findMatch
:: Match a
-> MaybeAmbiguous a
2470 findMatch match
= case match
of
2471 NoMatch _ msgs
-> None msgs
2472 Match _ _
[x
] -> Unambiguous x
2473 Match m d
[] -> error $ "findMatch: impossible: " ++ show match
'
2475 match
' = Match m d
[] :: Match
()
2476 -- TODO: Maybe use Data.List.NonEmpty inside
2477 -- Match so that this case would be correct
2479 Match m _ xs
-> Ambiguous m xs
2481 data MaybeAmbiguous a
2484 | Ambiguous MatchClass
[a
]
2487 ------------------------------
2491 -- | A primitive matcher that looks up a value in a finite 'Map'. The
2492 -- value must match exactly.
2493 matchExactly
:: Ord k
=> (a
-> k
) -> [a
] -> (k
-> Match a
)
2494 matchExactly key xs
=
2495 \k
-> case Map
.lookup k m
of
2497 Just ys
-> exactMatches ys
2499 m
= Map
.fromListWith
(++) [(key x
, [x
]) | x
<- xs
]
2501 -- | A primitive matcher that looks up a value in a finite 'Map'. It checks
2502 -- for an exact or inexact match. We get an inexact match if the match
2503 -- is not exact, but the canonical forms match. It takes a canonicalisation
2504 -- function for this purpose.
2506 -- So for example if we used string case fold as the canonicalisation
2507 -- function, then we would get case insensitive matching (but it will still
2508 -- report an exact match when the case matches too).
2515 matchInexactly cannonicalise key xs
=
2516 \k
-> case Map
.lookup k m
of
2517 Just ys
-> exactMatches ys
2518 Nothing
-> case Map
.lookup (cannonicalise k
) m
' of
2519 Just ys
-> inexactMatches ys
2522 m
= Map
.fromListWith
(++) [(key x
, [x
]) | x
<- xs
]
2524 -- the map of canonicalised keys to groups of inexact matches
2525 m
' = Map
.mapKeysWith
(++) cannonicalise m
2527 matchParse
:: Parsec a
=> String -> Match a
2528 matchParse
= maybe mzero
return . simpleParsec
2530 ------------------------------
2534 caseFold
:: String -> String
2535 caseFold
= lowercase
2537 -- | Make a 'ComponentName' given an 'UnqualComponentName' and knowing the
2538 -- 'ComponentKind'. We also need the 'PackageName' to distinguish the package's
2539 -- primary library from named private libraries.
2543 -> UnqualComponentName
2545 mkComponentName pkgname ckind ucname
=
2548 | packageNameToUnqualComponentName pkgname
== ucname
->
2549 CLibName LMainLibName
2550 |
otherwise -> CLibName
$ LSubLibName ucname
2551 FLibKind
-> CFLibName ucname
2552 ExeKind
-> CExeName ucname
2553 TestKind
-> CTestName ucname
2554 BenchKind
-> CBenchName ucname
2556 ------------------------------
2561 ex1pinfo :: [KnownPackage]
2563 [ addComponent (CExeName (mkUnqualComponentName "foo-exe")) [] ["Data.Foo"] $
2565 pinfoId = PackageIdentifier (mkPackageName "foo") (mkVersion [1]),
2566 pinfoDirectory = Just ("/the/foo", "foo"),
2567 pinfoPackageFile = Just ("/the/foo/foo.cabal", "foo/foo.cabal"),
2568 pinfoComponents = []
2571 pinfoId = PackageIdentifier (mkPackageName "bar") (mkVersion [1]),
2572 pinfoDirectory = Just ("/the/bar", "bar"),
2573 pinfoPackageFile = Just ("/the/bar/bar.cabal", "bar/bar.cabal"),
2574 pinfoComponents = []
2578 addComponent n ds ms p =
2581 KnownComponent n (componentStringName (pinfoId p) n)
2587 mkMn :: String -> ModuleName
2588 mkMn = ModuleName.fromString
2592 [ TargetComponent (CExeName "foo") WholeComponent
2593 , TargetComponent (CExeName "foo") (ModuleTarget (mkMn "Foo"))
2594 , TargetComponent (CExeName "tst") (ModuleTarget (mkMn "Foo"))
2597 mkMn :: String -> ModuleName
2598 mkMn = fromJust . simpleParse
2600 ex_pkgid :: PackageIdentifier
2601 Just ex_pkgid = simpleParse "thelib"
2605 ex_cs :: [KnownComponent]
2607 [ (mkC (CExeName "foo") ["src1", "src1/src2"] ["Foo", "Src2.Bar", "Bar"])
2608 , (mkC (CExeName "tst") ["src1", "test"] ["Foo"])
2611 mkC n ds ms = KnownComponent n (componentStringName n) ds (map mkMn ms)
2612 mkMn :: String -> ModuleName
2613 mkMn = fromJust . simpleParse
2614 pkgid :: PackageIdentifier
2615 Just pkgid = simpleParse "thelib"