{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} module Distribution.Types.Dependency ( Dependency(..) , mkDependency , depPkgName , depVerRange , depLibraries , simplifyDependency , mainLibSet ) where import Distribution.Compat.Prelude import Prelude () import Distribution.Types.VersionRange (isAnyVersionLight) import Distribution.Version (VersionRange, anyVersion, simplifyVersionRange) import Distribution.CabalSpecVersion import Distribution.Compat.CharParsing (char, spaces) import Distribution.Compat.Parsing (between, option) import Distribution.Parsec import Distribution.Pretty import Distribution.Types.LibraryName import Distribution.Types.PackageName import Distribution.Types.UnqualComponentName import qualified Distribution.Compat.NonEmptySet as NES import qualified Text.PrettyPrint as PP -- | Describes a dependency on a source package (API) -- -- /Invariant:/ package name does not appear as 'LSubLibName' in -- set of library names. -- data Dependency = Dependency PackageName VersionRange (NonEmptySet LibraryName) -- ^ The set of libraries required from the package. -- Only the selected libraries will be built. -- It does not affect the cabal-install solver yet. deriving (Generic, Read, Show, Eq, Typeable, Data) depPkgName :: Dependency -> PackageName depPkgName (Dependency pn _ _) = pn depVerRange :: Dependency -> VersionRange depVerRange (Dependency _ vr _) = vr depLibraries :: Dependency -> NonEmptySet LibraryName depLibraries (Dependency _ _ cs) = cs -- | Smart constructor of 'Dependency'. -- -- If 'PackageName' is appears as 'LSubLibName' in a set of sublibraries, -- it is automatically converted to 'LMainLibName'. -- -- @since 3.4.0.0 -- mkDependency :: PackageName -> VersionRange -> NonEmptySet LibraryName -> Dependency mkDependency pn vr lb = Dependency pn vr (NES.map conv lb) where pn' = packageNameToUnqualComponentName pn conv l@LMainLibName = l conv l@(LSubLibName ln) | ln == pn' = LMainLibName | otherwise = l instance Binary Dependency instance Structured Dependency instance NFData Dependency where rnf = genericRnf -- | -- -- >>> prettyShow $ Dependency "pkg" anyVersion mainLibSet -- "pkg" -- -- >>> prettyShow $ Dependency "pkg" anyVersion $ NES.insert (LSubLibName "sublib") mainLibSet -- "pkg:{pkg, sublib}" -- -- >>> prettyShow $ Dependency "pkg" anyVersion $ NES.singleton (LSubLibName "sublib") -- "pkg:sublib" -- -- >>> prettyShow $ Dependency "pkg" anyVersion $ NES.insert (LSubLibName "sublib-b") $ NES.singleton (LSubLibName "sublib-a") -- "pkg:{sublib-a, sublib-b}" -- instance Pretty Dependency where pretty (Dependency name ver sublibs) = withSubLibs (pretty name) <+> pver where -- TODO: change to isAnyVersion after #6736 pver | isAnyVersionLight ver = PP.empty | otherwise = pretty ver withSubLibs doc = case NES.toList sublibs of [LMainLibName] -> doc [LSubLibName uq] -> doc <<>> PP.colon <<>> pretty uq _ -> doc <<>> PP.colon <<>> PP.braces prettySublibs prettySublibs = PP.hsep $ PP.punctuate PP.comma $ prettySublib <$> NES.toList sublibs prettySublib LMainLibName = PP.text $ unPackageName name prettySublib (LSubLibName un) = PP.text $ unUnqualComponentName un -- | -- -- >>> simpleParsec "mylib:sub" :: Maybe Dependency -- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub") :| []))) -- -- >>> simpleParsec "mylib:{sub1,sub2}" :: Maybe Dependency -- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub1") :| [LSubLibName (UnqualComponentName "sub2")]))) -- -- >>> simpleParsec "mylib:{ sub1 , sub2 }" :: Maybe Dependency -- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub1") :| [LSubLibName (UnqualComponentName "sub2")]))) -- -- >>> simpleParsec "mylib:{ sub1 , sub2 } ^>= 42" :: Maybe Dependency -- Just (Dependency (PackageName "mylib") (MajorBoundVersion (mkVersion [42])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub1") :| [LSubLibName (UnqualComponentName "sub2")]))) -- -- >>> simpleParsec "mylib:{ } ^>= 42" :: Maybe Dependency -- Nothing -- -- >>> traverse_ print (map simpleParsec ["mylib:mylib", "mylib:{mylib}", "mylib:{mylib,sublib}" ] :: [Maybe Dependency]) -- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LMainLibName :| []))) -- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LMainLibName :| []))) -- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LMainLibName :| [LSubLibName (UnqualComponentName "sublib")]))) -- -- Spaces around colon are not allowed: -- -- >>> map simpleParsec ["mylib: sub", "mylib :sub", "mylib: {sub1,sub2}", "mylib :{sub1,sub2}"] :: [Maybe Dependency] -- [Nothing,Nothing,Nothing,Nothing] -- -- Sublibrary syntax is accepted since @cabal-version: 3.0@ -- -- >>> map (`simpleParsec'` "mylib:sub") [CabalSpecV2_4, CabalSpecV3_0] :: [Maybe Dependency] -- [Nothing,Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub") :| [])))] -- instance Parsec Dependency where parsec = do name <- parsec libs <- option mainLibSet $ do _ <- char ':' versionGuardMultilibs parsecWarning PWTExperimental "colon specifier is experimental feature (issue #5660)" NES.singleton <$> parseLib <|> parseMultipleLibs spaces -- https://github.com/haskell/cabal/issues/5846 ver <- parsec <|> pure anyVersion return $ mkDependency name ver libs where parseLib = LSubLibName <$> parsec parseMultipleLibs = between (char '{' *> spaces) (spaces *> char '}') (NES.fromNonEmpty <$> parsecCommaNonEmpty parseLib) versionGuardMultilibs :: CabalParsing m => m () versionGuardMultilibs = do csv <- askCabalSpecVersion when (csv < CabalSpecV3_0) $ fail $ unwords [ "Sublibrary dependency syntax used." , "To use this syntax the package needs to specify at least 'cabal-version: 3.0'." , "Alternatively, if you are depending on an internal library, you can write" , "directly the library name as it were a package." ] -- | Library set with main library. -- -- @since 3.4.0.0 mainLibSet :: NonEmptySet LibraryName mainLibSet = NES.singleton LMainLibName -- | Simplify the 'VersionRange' expression in a 'Dependency'. -- See 'simplifyVersionRange'. -- simplifyDependency :: Dependency -> Dependency simplifyDependency (Dependency name range comps) = Dependency name (simplifyVersionRange range) comps