@@ -11,6 +11,7 @@ module Input.Cabal(
1111import Input.Settings
1212
1313import Data.List.Extra
14+ import Data.Map (Map )
1415import System.FilePath
1516import Control.DeepSeq
1617import Control.Exception.Extra
@@ -20,7 +21,6 @@ import General.Str
2021import System.Exit
2122import qualified System.Process.ByteString as BS
2223import qualified Data.ByteString.UTF8 as UTF8
23- import System.Directory
2424import Data.Maybe
2525import Data.Tuple.Extra
2626import qualified Data.Map.Strict as Map
@@ -30,12 +30,18 @@ import Control.Applicative
3030import Prelude
3131
3232import Distribution.Compat.Lens (toListOf )
33+ import qualified Distribution.InstalledPackageInfo as IPI
34+ import Distribution.Package (packageId , UnitId , pkgName )
3335import qualified Distribution.PackageDescription as PD
3436import qualified Distribution.PackageDescription.Configuration as PD
3537import qualified Distribution.PackageDescription.Parsec as PD
3638import qualified Distribution.Pretty
39+ import Distribution.Text (display )
3740import qualified Distribution.Types.BuildInfo.Lens as Lens
38- import Distribution.Types.PackageName (mkPackageName , unPackageName )
41+ import Distribution.Types.LibraryVisibility (LibraryVisibility (.. ))
42+ import Distribution.Types.PackageDescription (license' )
43+ import Distribution.Types.PackageId (pkgVersion )
44+ import Distribution.Types.PackageName (unPackageName )
3945import Distribution.Types.Version (versionNumbers )
4046import Distribution.Utils.ShortText (fromShortText )
4147import Hackage.RevDeps (lastVersionsOfPackages )
@@ -72,6 +78,7 @@ instance NFData Package where
7278
7379-- | Given a set of packages, return the popularity of each package, along with any warnings
7480-- about packages imported but not found.
81+
7582packagePopularity :: Map. Map PkgName Package -> ([String ], Map. Map PkgName Int )
7683packagePopularity cbl = mp `seq` (errs, mp)
7784 where
@@ -89,7 +96,6 @@ packagePopularity cbl = mp `seq` (errs, mp)
8996-- | Run 'ghc-pkg' and get a list of packages which are installed.
9097readGhcPkg :: Settings -> IO (Map. Map PkgName Package )
9198readGhcPkg settings = do
92- topdir <- findExecutable " ghc-pkg"
9399 (exit, stdout, stderr) <-
94100 -- From GHC 9.0.1, the `haddock-html` field in `*.conf` files for GHC boot
95101 -- libraries has used `${pkgroot}`, which can be expanded in the output.
@@ -111,14 +117,78 @@ readGhcPkg settings = do
111117 BS. readProcessWithExitCode " ghc-pkg" [" dump" , " --expand-pkgroot" ] mempty
112118 when (exit /= ExitSuccess ) $
113119 errorIO $ " Error when reading from ghc-pkg, " ++ show exit ++ " \n " ++ UTF8. toString stderr
114- let g (stripPrefix " $topdir" -> Just x) | Just t <- topdir = takeDirectory t ++ x
115- -- ^ Backwards compatibility with GHC < 9.0
116- g x = x
117- let fixer p = p{packageLibrary = True , packageDocs = g <$> packageDocs p}
118- let f ((stripPrefix " name: " -> Just x): xs) = Just (mkPackageName $ trimStart x, fixer $ readCabal settings $ bstrPack $ unlines xs)
119- f _ = Nothing
120- pure $ Map. fromList $ mapMaybe f $ splitOn [" ---" ] $ lines $ filter (/= ' \r ' ) $ UTF8. toString stdout
121120
121+ installedPackages <- parsePackages stdout
122+
123+ pure $
124+ Map. fromList
125+ [ ( pkgName $ packageId installedPackage
126+ , fromInstalledPackage settings installedPackages installedPackage
127+ )
128+ | (_unitId, installedPackage) <- Map. toList installedPackages
129+ ]
130+ where
131+ parsePackages :: UTF8. ByteString -> IO (Map UnitId IPI. InstalledPackageInfo )
132+ parsePackages input =
133+ Map. fromList . fmap ((,) <$> IPI. installedUnitId <*> id ) . catMaybes <$>
134+ traverse
135+ (\ input ->
136+ case IPI. parseInstalledPackageInfo . bstrPack $ unlines input of
137+ Left errors -> do
138+ mapM_ (\ msg -> putStrLn $ " error (parsing ghc-pkg output): " ++ msg) errors
139+ pure Nothing
140+ Right (warnings, package) -> do
141+ mapM_ (\ msg -> putStrLn $ " warning (parsing ghc-pkg output): " ++ msg) warnings
142+ pure $ Just package
143+ )
144+ (splitOn [" ---" ] . lines . filter (/= ' \r ' ) $ UTF8. toString input)
145+
146+ fromInstalledPackage ::
147+ Settings ->
148+ Map UnitId IPI. InstalledPackageInfo ->
149+ IPI. InstalledPackageInfo ->
150+ Package
151+ fromInstalledPackage Settings {.. } installedPackages ipi = Package {.. }
152+ where
153+ pkgId = packageId ipi
154+
155+ packageDepends =
156+ fmap
157+ (\ unitId ->
158+ maybe
159+ (error $ display unitId ++ " missing from installed packages" ) (pkgName . packageId)
160+ (Map. lookup unitId installedPackages)
161+ )
162+ (IPI. depends ipi)
163+ packageVersion = strPack $ intercalate " ." $ map show $ versionNumbers $ pkgVersion pkgId
164+ packageSynopsis = strPack $ fromShortText $ IPI. synopsis ipi
165+ packageLibrary = IPI. libVisibility ipi == LibraryVisibilityPublic
166+ packageDocs = listToMaybe $ IPI. haddockHTMLs ipi
167+
168+ unpackLicenseExpression (SPDX. EOr x y) = unpackLicenseExpression x ++ unpackLicenseExpression y
169+ unpackLicenseExpression x = [x]
170+
171+ packageLicenses = case license' $ IPI. license ipi of
172+ SPDX. NONE -> []
173+ SPDX. License licExpr -> map (show . Distribution.Pretty. pretty) $
174+ unpackLicenseExpression licExpr
175+ packageCategories =
176+ filter (not . null ) $ split (`elem` " ," ) $
177+ fromShortText $ IPI. category ipi
178+ packageAuthor = fromShortText $ IPI. author ipi
179+ packageMaintainer = fromShortText $ IPI. maintainer ipi
180+
181+ packageTags = map (both strPack) $ nubOrd $ concat
182+ [ map (" license" ,) packageLicenses
183+ , map (" category" ,) packageCategories
184+ , map (" author" ,) (concatMap cleanup [packageAuthor, packageMaintainer])
185+ ]
186+
187+ -- split on things like "," "&" "and", then throw away email addresses, replace spaces with "-" and rename
188+ cleanup =
189+ filter (/= " " ) .
190+ map (renameTag . intercalate " -" . filter (' @' `notElem` ) . words . takeWhile (`notElem` " <(" )) .
191+ concatMap (map unwords . split (== " and" ) . words ) . split (`elem` " ,&" )
122192
123193-- | Given a tarball of Cabal files, parse the latest version of each package.
124194parseCabalTarball :: Settings -> FilePath -> IO (Map. Map PkgName Package )
0 commit comments