2 {-# LANGUAGE ScopedTypeVariables #-}
3 {-# LANGUAGE DeriveGeneric #-}
4 {-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
6 -----------------------------------------------------------------------------
8 -- Module : Distribution.Client.Targets
9 -- Copyright : (c) Duncan Coutts 2011
12 -- Maintainer : duncan@community.haskell.org
14 -- Handling for user-specified targets
15 -----------------------------------------------------------------------------
16 module Distribution
.Client
.Targets
(
21 -- * Resolving user targets to package specifiers
24 -- ** Detailed interface
25 UserTargetProblem
(..),
27 reportUserTargetProblems
,
34 PackageTargetProblem
(..),
35 reportPackageTargetProblems
,
37 disambiguatePackageTargets
,
38 disambiguatePackageName
,
42 UserConstraintScope
(..),
44 userConstraintPackageName
,
46 userToPackageConstraint
,
51 import Distribution
.Client
.Compat
.Prelude
53 import Distribution
.Package
54 ( Package
(..), PackageName
, unPackageName
, mkPackageName
56 import Distribution
.Client
.Types
57 ( PackageLocation
(..), ResolvedPkgLoc
, UnresolvedSourcePackage
58 , PackageSpecifier
(..) )
60 import Distribution
.Solver
.Types
.OptionalStanza
61 import Distribution
.Solver
.Types
.PackageConstraint
62 import Distribution
.Solver
.Types
.PackagePath
63 import Distribution
.Solver
.Types
.PackageIndex
(PackageIndex
)
64 import qualified Distribution
.Solver
.Types
.PackageIndex
as PackageIndex
65 import Distribution
.Solver
.Types
.SourcePackage
67 import qualified Codec
.Archive
.Tar
as Tar
68 import qualified Codec
.Archive
.Tar
.Entry
as Tar
69 import qualified Distribution
.Client
.Tar
as Tar
70 import Distribution
.Client
.FetchUtils
71 import Distribution
.Client
.Utils
( tryFindPackageDesc
)
72 import Distribution
.Client
.GlobalFlags
74 import Distribution
.Types
.PackageVersionConstraint
75 ( PackageVersionConstraint
(..) )
77 import Distribution
.PackageDescription
78 ( GenericPackageDescription
)
79 import Distribution
.Types
.Flag
80 ( parsecFlagAssignmentNonEmpty
)
81 import Distribution
.Version
83 import Distribution
.Simple
.Utils
86 import Distribution
.PackageDescription
.Parsec
87 ( parseGenericPackageDescriptionMaybe
)
88 import Distribution
.Simple
.PackageDescription
89 ( readGenericPackageDescription
)
91 import qualified Data
.Map
as Map
92 import qualified Data
.ByteString
.Lazy
as BS
93 import qualified Distribution
.Client
.GZipUtils
as GZipUtils
94 import qualified Distribution
.Compat
.CharParsing
as P
95 import System
.FilePath
96 ( takeExtension
, dropExtension
, takeDirectory
, splitPath
)
97 import System
.Directory
98 ( doesFileExist, doesDirectoryExist )
100 ( URI
(..), URIAuth
(..), parseAbsoluteURI
)
102 -- ------------------------------------------------------------
104 -- ------------------------------------------------------------
106 -- | Various ways that a user may specify a package or package collection.
110 -- | A partially specified package, identified by name and possibly with
111 -- an exact version or a version constraint.
113 -- > cabal install foo
114 -- > cabal install foo-1.0
115 -- > cabal install 'foo < 2'
117 UserTargetNamed PackageVersionConstraint
119 -- | A specific package that is unpacked in a local directory, often the
120 -- current directory.
123 -- > cabal install ../lib/other
125 -- * Note: in future, if multiple @.cabal@ files are allowed in a single
126 -- directory then this will refer to the collection of packages.
128 | UserTargetLocalDir
FilePath
130 -- | A specific local unpacked package, identified by its @.cabal@ file.
132 -- > cabal install foo.cabal
133 -- > cabal install ../lib/other/bar.cabal
135 | UserTargetLocalCabalFile
FilePath
137 -- | A specific package that is available as a local tarball file
139 -- > cabal install dist/foo-1.0.tar.gz
140 -- > cabal install ../build/baz-1.0.tar.gz
142 | UserTargetLocalTarball
FilePath
144 -- | A specific package that is available as a remote tarball file
146 -- > cabal install http://code.haskell.org/~user/foo/foo-0.9.tar.gz
148 | UserTargetRemoteTarball URI
152 -- ------------------------------------------------------------
153 -- * Parsing and checking user targets
154 -- ------------------------------------------------------------
156 readUserTargets
:: Verbosity
-> [String] -> IO [UserTarget
]
157 readUserTargets verbosity targetStrs
= do
158 (problems
, targets
) <- liftM partitionEithers
159 (traverse readUserTarget targetStrs
)
160 reportUserTargetProblems verbosity problems
164 data UserTargetProblem
165 = UserTargetUnexpectedFile
String
166 | UserTargetNonexistantFile
String
167 | UserTargetUnexpectedUriScheme
String
168 | UserTargetUnrecognisedUri
String
169 | UserTargetUnrecognised
String
172 readUserTarget
:: String -> IO (Either UserTargetProblem UserTarget
)
173 readUserTarget targetstr
=
174 case eitherParsec targetstr
of
175 Right dep
-> return (Right
(UserTargetNamed dep
))
177 fileTarget
<- testFileTargets targetstr
179 Just target
-> return target
181 case testUriTargets targetstr
of
182 Just target
-> return target
183 Nothing
-> return (Left
(UserTargetUnrecognised targetstr
))
185 testFileTargets
:: FilePath -> IO (Maybe (Either UserTargetProblem UserTarget
))
186 testFileTargets filename
= do
187 isDir
<- doesDirectoryExist filename
188 isFile
<- doesFileExist filename
189 parentDirExists
<- case takeDirectory filename
of
191 dir
-> doesDirectoryExist dir
192 let result
:: Maybe (Either UserTargetProblem UserTarget
)
195 = Just
(Right
(UserTargetLocalDir filename
))
197 | isFile
&& extensionIsTarGz filename
198 = Just
(Right
(UserTargetLocalTarball filename
))
200 | isFile
&& takeExtension filename
== ".cabal"
201 = Just
(Right
(UserTargetLocalCabalFile filename
))
204 = Just
(Left
(UserTargetUnexpectedFile filename
))
207 = Just
(Left
(UserTargetNonexistantFile filename
))
213 testUriTargets
:: String -> Maybe (Either UserTargetProblem UserTarget
)
215 case parseAbsoluteURI str
of
218 uriAuthority
= Just URIAuth
{ uriRegName
= host
}
220 | scheme
/= "http:" && scheme
/= "https:" ->
221 Just
(Left
(UserTargetUnexpectedUriScheme targetstr
))
224 Just
(Left
(UserTargetUnrecognisedUri targetstr
))
227 Just
(Right
(UserTargetRemoteTarball uri
))
230 extensionIsTarGz
:: FilePath -> Bool
231 extensionIsTarGz f
= takeExtension f
== ".gz"
232 && takeExtension
(dropExtension f
) == ".tar"
234 reportUserTargetProblems
:: Verbosity
-> [UserTargetProblem
] -> IO ()
235 reportUserTargetProblems verbosity problems
= do
236 case [ target | UserTargetUnrecognised target
<- problems
] of
238 target
-> die
' verbosity
240 [ "Unrecognised target '" ++ name
++ "'."
242 ++ "Targets can be:\n"
243 ++ " - package names, e.g. 'pkgname', 'pkgname-1.0.1', 'pkgname < 2.0'\n"
244 ++ " - cabal files 'pkgname.cabal' or package directories 'pkgname/'\n"
245 ++ " - package tarballs 'pkgname.tar.gz' or 'http://example.com/pkgname.tar.gz'"
247 case [ target | UserTargetNonexistantFile target
<- problems
] of
249 target
-> die
' verbosity
251 [ "The file does not exist '" ++ name
++ "'."
254 case [ target | UserTargetUnexpectedFile target
<- problems
] of
256 target
-> die
' verbosity
258 [ "Unrecognised file target '" ++ name
++ "'."
260 ++ "File targets can be either package tarballs 'pkgname.tar.gz' "
261 ++ "or cabal files 'pkgname.cabal'."
263 case [ target | UserTargetUnexpectedUriScheme target
<- problems
] of
265 target
-> die
' verbosity
267 [ "URL target not supported '" ++ name
++ "'."
269 ++ "Only 'http://' and 'https://' URLs are supported."
271 case [ target | UserTargetUnrecognisedUri target
<- problems
] of
273 target
-> die
' verbosity
275 [ "Unrecognise URL target '" ++ name
++ "'."
279 -- ------------------------------------------------------------
280 -- * Resolving user targets to package specifiers
281 -- ------------------------------------------------------------
283 -- | Given a bunch of user-specified targets, try to resolve what it is they
284 -- refer to. They can either be specific packages (local dirs, tarballs etc)
285 -- or they can be named packages (with or without version info).
287 resolveUserTargets
:: Package pkg
292 -> IO [PackageSpecifier UnresolvedSourcePackage
]
293 resolveUserTargets verbosity repoCtxt available userTargets
= do
295 -- given the user targets, get a list of fully or partially resolved
296 -- package references
297 packageTargets
<- traverse
(readPackageTarget verbosity
)
298 =<< traverse
(fetchPackageTarget verbosity repoCtxt
) . concat
299 =<< traverse
(expandUserTarget verbosity
) userTargets
301 -- users are allowed to give package names case-insensitively, so we must
302 -- disambiguate named package references
303 let (problems
, packageSpecifiers
) :: ([PackageTargetProblem
], [PackageSpecifier UnresolvedSourcePackage
]) =
304 disambiguatePackageTargets available availableExtra packageTargets
306 -- use any extra specific available packages to help us disambiguate
307 availableExtra
:: [PackageName
]
308 availableExtra
= [ packageName pkg
309 | PackageTargetLocation pkg
<- packageTargets
]
311 reportPackageTargetProblems verbosity problems
313 return packageSpecifiers
316 -- ------------------------------------------------------------
318 -- ------------------------------------------------------------
320 -- | An intermediate between a 'UserTarget' and a resolved 'PackageSpecifier'.
321 -- Unlike a 'UserTarget', a 'PackageTarget' refers only to a single package.
323 data PackageTarget pkg
=
324 PackageTargetNamed PackageName
[PackageProperty
] UserTarget
326 -- | A package identified by name, but case insensitively, so it needs
327 -- to be resolved to the right case-sensitive name.
328 | PackageTargetNamedFuzzy PackageName
[PackageProperty
] UserTarget
329 | PackageTargetLocation pkg
330 deriving (Show, Functor
, Foldable
, Traversable
)
333 -- ------------------------------------------------------------
334 -- * Converting user targets to package targets
335 -- ------------------------------------------------------------
337 -- | Given a user-specified target, expand it to a bunch of package targets
338 -- (each of which refers to only one package).
340 expandUserTarget
:: Verbosity
342 -> IO [PackageTarget
(PackageLocation
())]
343 expandUserTarget verbosity userTarget
= case userTarget
of
345 UserTargetNamed
(PackageVersionConstraint name vrange
) ->
346 let props
= [ PackagePropertyVersion vrange
347 |
not (isAnyVersion vrange
) ]
348 in return [PackageTargetNamedFuzzy name props userTarget
]
350 UserTargetLocalDir dir
->
351 return [ PackageTargetLocation
(LocalUnpackedPackage dir
) ]
353 UserTargetLocalCabalFile file
-> do
354 let dir
= takeDirectory file
355 _
<- tryFindPackageDesc verbosity dir
(localPackageError dir
) -- just as a check
356 return [ PackageTargetLocation
(LocalUnpackedPackage dir
) ]
358 UserTargetLocalTarball tarballFile
->
359 return [ PackageTargetLocation
(LocalTarballPackage tarballFile
) ]
361 UserTargetRemoteTarball tarballURL
->
362 return [ PackageTargetLocation
(RemoteTarballPackage tarballURL
()) ]
364 localPackageError
:: FilePath -> String
365 localPackageError dir
=
366 "Error reading local package.\nCouldn't find .cabal file in: " ++ dir
368 -- ------------------------------------------------------------
369 -- * Fetching and reading package targets
370 -- ------------------------------------------------------------
373 -- | Fetch any remote targets so that they can be read.
375 fetchPackageTarget
:: Verbosity
377 -> PackageTarget
(PackageLocation
())
378 -> IO (PackageTarget ResolvedPkgLoc
)
379 fetchPackageTarget verbosity repoCtxt
= traverse
$
380 fetchPackage verbosity repoCtxt
. fmap (const Nothing
)
383 -- | Given a package target that has been fetched, read the .cabal file.
385 -- This only affects targets given by location, named targets are unaffected.
387 readPackageTarget
:: Verbosity
388 -> PackageTarget ResolvedPkgLoc
389 -> IO (PackageTarget UnresolvedSourcePackage
)
390 readPackageTarget verbosity
= traverse modifyLocation
392 modifyLocation
:: ResolvedPkgLoc
-> IO UnresolvedSourcePackage
393 modifyLocation location
= case location
of
395 LocalUnpackedPackage dir
-> do
396 pkg
<- tryFindPackageDesc verbosity dir
(localPackageError dir
) >>=
397 readGenericPackageDescription verbosity
399 { srcpkgPackageId
= packageId pkg
400 , srcpkgDescription
= pkg
401 , srcpkgSource
= fmap Just location
402 , srcpkgDescrOverride
= Nothing
405 LocalTarballPackage tarballFile
->
406 readTarballPackageTarget location tarballFile tarballFile
408 RemoteTarballPackage tarballURL tarballFile
->
409 readTarballPackageTarget location tarballFile
(show tarballURL
)
411 RepoTarballPackage _repo _pkgid _
->
412 error "TODO: readPackageTarget RepoTarballPackage"
413 -- For repo tarballs this info should be obtained from the index.
415 RemoteSourceRepoPackage _srcRepo _
->
416 error "TODO: readPackageTarget RemoteSourceRepoPackage"
417 -- This can't happen, because it would have errored out already
418 -- in fetchPackage, via fetchPackageTarget before it gets to this
421 -- When that is corrected, this will also need to be fixed.
423 readTarballPackageTarget
:: ResolvedPkgLoc
-> FilePath -> FilePath -> IO UnresolvedSourcePackage
424 readTarballPackageTarget location tarballFile tarballOriginalLoc
= do
425 (filename
, content
) <- extractTarballPackageCabalFile
426 tarballFile tarballOriginalLoc
427 case parsePackageDescription
' content
of
428 Nothing
-> die
' verbosity
$ "Could not parse the cabal file "
429 ++ filename
++ " in " ++ tarballFile
432 { srcpkgPackageId
= packageId pkg
433 , srcpkgDescription
= pkg
434 , srcpkgSource
= fmap Just location
435 , srcpkgDescrOverride
= Nothing
438 extractTarballPackageCabalFile
:: FilePath -> String
439 -> IO (FilePath, BS
.ByteString
)
440 extractTarballPackageCabalFile tarballFile tarballOriginalLoc
=
441 either (die
' verbosity
. formatErr
) return
444 . Tar
.filterEntries isCabalFile
446 . GZipUtils
.maybeDecompress
447 =<< BS
.readFile tarballFile
449 formatErr msg
= "Error reading " ++ tarballOriginalLoc
++ ": " ++ msg
451 accumEntryMap
:: Tar
.Entries Tar
.FormatError
452 -> Either (Tar
.FormatError
, Map Tar
.TarPath Tar
.Entry
) (Map Tar
.TarPath Tar
.Entry
)
453 accumEntryMap
= Tar
.foldlEntries
454 (\m e
-> Map
.insert (Tar
.entryTarPath e
) e m
)
457 check
(Left e
) = Left
(show e
)
458 check
(Right m
) = case Map
.elems m
of
459 [] -> Left noCabalFile
460 [file
] -> case Tar
.entryContent file
of
461 Tar
.NormalFile content _
-> Right
(Tar
.entryPath file
, content
)
462 _
-> Left noCabalFile
463 _files
-> Left multipleCabalFiles
465 noCabalFile
= "No cabal file found"
466 multipleCabalFiles
= "Multiple cabal files found"
468 isCabalFile
:: Tar
.Entry
-> Bool
469 isCabalFile e
= case splitPath
(Tar
.entryPath e
) of
470 [ _dir
, file
] -> takeExtension file
== ".cabal"
471 [".", _dir
, file
] -> takeExtension file
== ".cabal"
474 parsePackageDescription
' :: BS
.ByteString
-> Maybe GenericPackageDescription
475 parsePackageDescription
' bs
=
476 parseGenericPackageDescriptionMaybe
(BS
.toStrict bs
)
478 -- ------------------------------------------------------------
479 -- * Checking package targets
480 -- ------------------------------------------------------------
482 data PackageTargetProblem
483 = PackageNameUnknown PackageName UserTarget
484 | PackageNameAmbiguous PackageName
[PackageName
] UserTarget
488 -- | Users are allowed to give package names case-insensitively, so we must
489 -- disambiguate named package references.
491 disambiguatePackageTargets
:: Package pkg
'
494 -> [PackageTarget pkg
]
495 -> ( [PackageTargetProblem
]
496 , [PackageSpecifier pkg
] )
497 disambiguatePackageTargets availablePkgIndex availableExtra targets
=
498 partitionEithers
(map disambiguatePackageTarget targets
)
500 disambiguatePackageTarget packageTarget
= case packageTarget
of
501 PackageTargetLocation pkg
-> Right
(SpecificSourcePackage pkg
)
503 PackageTargetNamed pkgname props userTarget
504 |
null (PackageIndex
.lookupPackageName availablePkgIndex pkgname
)
505 -> Left
(PackageNameUnknown pkgname userTarget
)
506 |
otherwise -> Right
(NamedPackage pkgname props
)
508 PackageTargetNamedFuzzy pkgname props userTarget
->
509 case disambiguatePackageName packageNameEnv pkgname
of
510 None
-> Left
(PackageNameUnknown
512 Ambiguous pkgnames
-> Left
(PackageNameAmbiguous
513 pkgname pkgnames userTarget
)
514 Unambiguous pkgname
' -> Right
(NamedPackage pkgname
' props
)
516 -- use any extra specific available packages to help us disambiguate
517 packageNameEnv
:: PackageNameEnv
518 packageNameEnv
= mappend
(indexPackageNameEnv availablePkgIndex
)
519 (extraPackageNameEnv availableExtra
)
522 -- | Report problems to the user. That is, if there are any problems
523 -- then raise an exception.
524 reportPackageTargetProblems
:: Verbosity
525 -> [PackageTargetProblem
] -> IO ()
526 reportPackageTargetProblems verbosity problems
= do
527 case [ pkg | PackageNameUnknown pkg _
<- problems
] of
529 pkgs
-> die
' verbosity
$ unlines
530 [ "There is no package named '" ++ prettyShow name
++ "'. "
532 ++ "You may need to run 'cabal update' to get the latest "
533 ++ "list of available packages."
535 case [ (pkg
, matches
) | PackageNameAmbiguous pkg matches _
<- problems
] of
537 ambiguities
-> die
' verbosity
$ unlines
538 [ "There is no package named '" ++ prettyShow name
++ "'. "
539 ++ (if length matches
> 1
540 then "However, the following package names exist: "
541 else "However, the following package name exists: ")
542 ++ intercalate
", " [ "'" ++ prettyShow m
++ "'" | m
<- matches
]
544 |
(name
, matches
) <- ambiguities
]
547 -- ------------------------------------------------------------
548 -- * Disambiguating package names
549 -- ------------------------------------------------------------
551 data MaybeAmbiguous a
= None | Unambiguous a | Ambiguous
[a
]
553 -- | Given a package name and a list of matching names, figure out
554 -- which one it might be referring to. If there is an exact
555 -- case-sensitive match then that's ok (i.e. returned via
556 -- 'Unambiguous'). If it matches just one package case-insensitively
557 -- or if it matches multiple packages case-insensitively, in that case
558 -- the result is 'Ambiguous'.
560 -- Note: Before cabal 2.2, when only a single package matched
561 -- case-insensitively it would be considered 'Unambiguous'.
563 disambiguatePackageName
:: PackageNameEnv
565 -> MaybeAmbiguous PackageName
566 disambiguatePackageName
(PackageNameEnv pkgNameLookup
) name
=
567 case nub (pkgNameLookup name
) of
569 names
-> case find (name
==) names
of
570 Just name
' -> Unambiguous name
'
571 Nothing
-> Ambiguous names
574 newtype PackageNameEnv
= PackageNameEnv
(PackageName
-> [PackageName
])
576 instance Monoid PackageNameEnv
where
577 mempty
= PackageNameEnv
(const [])
580 instance Semigroup PackageNameEnv
where
581 PackageNameEnv lookupA
<> PackageNameEnv lookupB
=
582 PackageNameEnv
(\name
-> lookupA name
++ lookupB name
)
584 indexPackageNameEnv
:: PackageIndex pkg
-> PackageNameEnv
585 indexPackageNameEnv pkgIndex
= PackageNameEnv pkgNameLookup
587 pkgNameLookup pname
=
588 map fst (PackageIndex
.searchByName pkgIndex
$ unPackageName pname
)
590 extraPackageNameEnv
:: [PackageName
] -> PackageNameEnv
591 extraPackageNameEnv names
= PackageNameEnv pkgNameLookup
593 pkgNameLookup pname
=
595 |
let lname
= lowercase
(unPackageName pname
)
597 , lowercase
(unPackageName pname
') == lname
]
600 -- ------------------------------------------------------------
601 -- * Package constraints
602 -- ------------------------------------------------------------
604 -- | Version of 'Qualifier' that a user may specify on the
607 -- | Top-level dependency.
610 -- | Setup dependency.
611 | UserQualSetup PackageName
613 -- | Executable dependency.
614 | UserQualExe PackageName PackageName
615 deriving (Eq
, Show, Generic
)
617 instance Binary UserQualifier
618 instance Structured UserQualifier
620 -- | Version of 'ConstraintScope' that a user may specify on the
622 data UserConstraintScope
=
623 -- | Scope that applies to the package when it has the specified qualifier.
624 UserQualified UserQualifier PackageName
626 -- | Scope that applies to the package when it has a setup qualifier.
627 | UserAnySetupQualifier PackageName
629 -- | Scope that applies to the package when it has any qualifier.
630 | UserAnyQualifier PackageName
631 deriving (Eq
, Show, Generic
)
633 instance Binary UserConstraintScope
634 instance Structured UserConstraintScope
636 fromUserQualifier
:: UserQualifier
-> Qualifier
637 fromUserQualifier UserQualToplevel
= QualToplevel
638 fromUserQualifier
(UserQualSetup name
) = QualSetup name
639 fromUserQualifier
(UserQualExe name1 name2
) = QualExe name1 name2
641 fromUserConstraintScope
:: UserConstraintScope
-> ConstraintScope
642 fromUserConstraintScope
(UserQualified q pn
) =
643 ScopeQualified
(fromUserQualifier q
) pn
644 fromUserConstraintScope
(UserAnySetupQualifier pn
) = ScopeAnySetupQualifier pn
645 fromUserConstraintScope
(UserAnyQualifier pn
) = ScopeAnyQualifier pn
647 -- | Version of 'PackageConstraint' that the user can specify on
649 data UserConstraint
=
650 UserConstraint UserConstraintScope PackageProperty
651 deriving (Eq
, Show, Generic
)
653 instance Binary UserConstraint
654 instance Structured UserConstraint
656 userConstraintPackageName
:: UserConstraint
-> PackageName
657 userConstraintPackageName
(UserConstraint scope _
) = scopePN scope
659 scopePN
(UserQualified _ pn
) = pn
660 scopePN
(UserAnyQualifier pn
) = pn
661 scopePN
(UserAnySetupQualifier pn
) = pn
663 userToPackageConstraint
:: UserConstraint
-> PackageConstraint
664 userToPackageConstraint
(UserConstraint scope prop
) =
665 PackageConstraint
(fromUserConstraintScope scope
) prop
667 readUserConstraint
:: String -> Either String UserConstraint
668 readUserConstraint str
=
669 case explicitEitherParsec parsec str
of
670 Left err
-> Left
$ msgCannotParse
++ err
674 "expected a (possibly qualified) package name followed by a " ++
675 "constraint, which is either a version range, 'installed', " ++
676 "'source', 'test', 'bench', or flags. "
678 instance Pretty UserConstraint
where
679 pretty
(UserConstraint scope prop
) =
680 dispPackageConstraint
$ PackageConstraint
(fromUserConstraintScope scope
) prop
682 instance Parsec UserConstraint
where
684 scope
<- parseConstraintScope
687 [ PackagePropertyFlags
<$> parsecFlagAssignmentNonEmpty
-- headed by "+-"
688 , PackagePropertyVersion
<$> parsec
-- headed by "<=>" (will be)
689 , PackagePropertyInstalled
<$ P
.string "installed"
690 , PackagePropertySource
<$ P
.string "source"
691 , PackagePropertyStanzas
[TestStanzas
] <$ P
.string "test"
692 , PackagePropertyStanzas
[BenchStanzas
] <$ P
.string "bench"
694 return (UserConstraint scope prop
)
697 parseConstraintScope
:: forall m
. CabalParsing m
=> m UserConstraintScope
698 parseConstraintScope
= do
701 [ P
.char
'.' *> withDot pn
702 , P
.char
':' *> withColon pn
703 , return (UserQualified UserQualToplevel pn
)
706 withDot
:: PackageName
-> m UserConstraintScope
708 | pn
== mkPackageName
"any" = UserAnyQualifier
<$> parsec
709 | pn
== mkPackageName
"setup" = UserAnySetupQualifier
<$> parsec
710 |
otherwise = P
.unexpected
$ "constraint scope: " ++ unPackageName pn
712 withColon
:: PackageName
-> m UserConstraintScope
713 withColon pn
= UserQualified
(UserQualSetup pn
)