Skip to content

Commit 393d7f1

Browse files
committed
Use InstalledPackageInfo in readGhcPkg
See <github.com//issues/471>
1 parent 1feb9c4 commit 393d7f1

1 file changed

Lines changed: 80 additions & 10 deletions

File tree

src/Input/Cabal.hs

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module Input.Cabal(
1111
import Input.Settings
1212

1313
import Data.List.Extra
14+
import Data.Map (Map)
1415
import System.FilePath
1516
import Control.DeepSeq
1617
import Control.Exception.Extra
@@ -20,7 +21,6 @@ import General.Str
2021
import System.Exit
2122
import qualified System.Process.ByteString as BS
2223
import qualified Data.ByteString.UTF8 as UTF8
23-
import System.Directory
2424
import Data.Maybe
2525
import Data.Tuple.Extra
2626
import qualified Data.Map.Strict as Map
@@ -30,12 +30,18 @@ import Control.Applicative
3030
import Prelude
3131

3232
import Distribution.Compat.Lens (toListOf)
33+
import qualified Distribution.InstalledPackageInfo as IPI
34+
import Distribution.Package (packageId, UnitId, pkgName)
3335
import qualified Distribution.PackageDescription as PD
3436
import qualified Distribution.PackageDescription.Configuration as PD
3537
import qualified Distribution.PackageDescription.Parsec as PD
3638
import qualified Distribution.Pretty
39+
import Distribution.Text (display)
3740
import 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)
3945
import Distribution.Types.Version (versionNumbers)
4046
import Distribution.Utils.ShortText (fromShortText)
4147
import 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+
7582
packagePopularity :: Map.Map PkgName Package -> ([String], Map.Map PkgName Int)
7683
packagePopularity 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.
9097
readGhcPkg :: Settings -> IO (Map.Map PkgName Package)
9198
readGhcPkg 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.
124194
parseCabalTarball :: Settings -> FilePath -> IO (Map.Map PkgName Package)

0 commit comments

Comments
 (0)