{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}

module Hoogle.Cabal (main) where

import Control.Exception (catch, throw)
import Control.Monad (unless)
import Data.Bifunctor (Bifunctor (second))
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.List.NonEmpty.Extra as NonEmpty
import qualified Data.Map.Strict as Map
import Data.Maybe (catMaybes)
import Data.String.Interpolate (i)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Data.Traversable (forM)
import Distribution.InstalledPackageInfo (InstalledPackageInfo (haddockHTMLs, installedUnitId))
import Distribution.Simple.Configure (getPersistBuildConfig)
import Distribution.Simple.PackageIndex (allPackagesByName)
import Distribution.Types.LocalBuildInfo (LocalBuildInfo, installedPkgs, localPkgDescr)
import Distribution.Types.PackageDescription (PackageDescription (package))
import Distribution.Types.PackageId (pkgName)
import Distribution.Types.PackageName (PackageName, unPackageName)
import Hoogle.Cabal.CmdOptions
import System.Directory (createDirectoryIfMissing, createDirectoryLink, listDirectory, makeAbsolute, removeDirectoryLink, removeDirectoryRecursive, withCurrentDirectory)
import System.Exit (exitFailure)
import System.FilePath
import System.IO (stderr)
import System.IO.Error (isDoesNotExistError)
import System.Process.Typed
import Text.Regex.TDFA ((=~))
import Prelude hiding (log)

main :: IO ()
main :: IO ()
main = do
  CmdOptions {FilePath
Maybe Text
Command
cmdOptions_command :: CmdOptions -> Command
cmdOptions_builddir :: CmdOptions -> FilePath
cmdOptions_platform :: CmdOptions -> Maybe Text
cmdOptions_compiler :: CmdOptions -> Maybe Text
cmdOptions_command :: Command
cmdOptions_builddir :: FilePath
cmdOptions_platform :: Maybe Text
cmdOptions_compiler :: Maybe Text
..} <- IO CmdOptions
readCmdOptions
  FilePath
localPackagesDir <- Maybe Text -> Maybe Text -> FilePath -> IO FilePath
findLocalPackagesBuildDir Maybe Text
cmdOptions_compiler Maybe Text
cmdOptions_platform FilePath
cmdOptions_builddir
  let hoogleDir :: FilePath
hoogleDir = FilePath
localPackagesDir FilePath -> FilePath -> FilePath
</> FilePath
".hoogle"
      hoogleLocalPackagesDir :: FilePath
hoogleLocalPackagesDir = FilePath
hoogleDir FilePath -> FilePath -> FilePath
</> FilePath
"local"
      hoogleDependenciesDir :: FilePath
hoogleDependenciesDir = FilePath
hoogleDir FilePath -> FilePath -> FilePath
</> FilePath
"dependencies"

  case Command
cmdOptions_command of
    Command
CommandGenerate -> do
      IO () -> (IOError -> IO ()) -> IO ()
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
catch (FilePath -> IO ()
removeDirectoryRecursive FilePath
hoogleDir) ((IOError -> IO ()) -> IO ()) -> (IOError -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(IOError
err :: IOError) ->
        if IOError -> Bool
isDoesNotExistError IOError
err then () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure () else IOError -> IO ()
forall a e. Exception e => e -> a
throw IOError
err
      Bool -> FilePath -> IO ()
createDirectoryIfMissing Bool
True FilePath
hoogleLocalPackagesDir
      Bool -> FilePath -> IO ()
createDirectoryIfMissing Bool
True FilePath
hoogleDependenciesDir
      [LocalBuildInfo]
localPackages <- FilePath -> FilePath -> IO [LocalBuildInfo]
symlinkLocalPackages FilePath
localPackagesDir FilePath
hoogleLocalPackagesDir
      let localPkgsName :: [PackageName]
localPkgsName = (LocalBuildInfo -> PackageName)
-> [LocalBuildInfo] -> [PackageName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (PackageIdentifier -> PackageName
pkgName (PackageIdentifier -> PackageName)
-> (LocalBuildInfo -> PackageIdentifier)
-> LocalBuildInfo
-> PackageName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageDescription -> PackageIdentifier
package (PackageDescription -> PackageIdentifier)
-> (LocalBuildInfo -> PackageDescription)
-> LocalBuildInfo
-> PackageIdentifier
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LocalBuildInfo -> PackageDescription
localPkgDescr) [LocalBuildInfo]
localPackages
      [PackageName]
dependenciesName <- [LocalBuildInfo] -> FilePath -> IO [PackageName]
symlinkDependencies [LocalBuildInfo]
localPackages FilePath
hoogleDependenciesDir
      let nameStrs :: [FilePath]
nameStrs = (PackageName -> FilePath) -> [PackageName] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap PackageName -> FilePath
unPackageName ([PackageName]
localPkgsName [PackageName] -> [PackageName] -> [PackageName]
forall a. Semigroup a => a -> a -> a
<> [PackageName]
dependenciesName)
      FilePath -> IO () -> IO ()
forall a. FilePath -> IO a -> IO a
withCurrentDirectory FilePath
hoogleDir (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        ProcessConfig () () () -> IO ()
forall (m :: * -> *) stdin stdout stderr.
MonadIO m =>
ProcessConfig stdin stdout stderr -> m ()
runProcess_ (ProcessConfig () () () -> IO ())
-> ([FilePath] -> ProcessConfig () () ()) -> [FilePath] -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath] -> ProcessConfig () () ()
proc FilePath
"hoogle" ([FilePath] -> IO ()) -> [FilePath] -> IO ()
forall a b. (a -> b) -> a -> b
$
          [FilePath
"generate", FilePath
databaseArg, FilePath
"--local=local", FilePath
"--local=dependencies"] [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
nameStrs
    CommandRun [FilePath]
hoogleArgs -> do
      let hoogleArgs' :: [FilePath]
hoogleArgs' = case [FilePath]
hoogleArgs of
            (FilePath
x : [FilePath]
xs) -> FilePath
x FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: FilePath
databaseArg FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: [FilePath]
xs
            [] -> [FilePath
databaseArg]
      FilePath -> IO () -> IO ()
forall a. FilePath -> IO a -> IO a
withCurrentDirectory FilePath
hoogleDir (IO () -> IO ()) -> ([FilePath] -> IO ()) -> [FilePath] -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ProcessConfig () () () -> IO ()
forall (m :: * -> *) stdin stdout stderr.
MonadIO m =>
ProcessConfig stdin stdout stderr -> m ()
runProcess_ (ProcessConfig () () () -> IO ())
-> ([FilePath] -> ProcessConfig () () ()) -> [FilePath] -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath] -> ProcessConfig () () ()
proc FilePath
"hoogle" ([FilePath] -> IO ()) -> [FilePath] -> IO ()
forall a b. (a -> b) -> a -> b
$
        [FilePath]
hoogleArgs'
  where
    databaseArg :: FilePath
databaseArg = FilePath
"--database=all.hoo"

findLocalPackagesBuildDir ::
  -- | compiler
  Maybe Text ->
  -- | platform
  Maybe Text ->
  -- | cabal project build dir (often dist-newstyle)
  FilePath ->
  IO FilePath
findLocalPackagesBuildDir :: Maybe Text -> Maybe Text -> FilePath -> IO FilePath
findLocalPackagesBuildDir Maybe Text
compiler Maybe Text
platform FilePath
projectBuildDir = do
  -- directory layout: dist-newstyle/build/x86_64-linux/ghc-9.2.3/$PACKAGE_NAME
  FilePath
projectBuildDir' <- FilePath -> IO FilePath
makeAbsolute FilePath
projectBuildDir
  FilePath
platformDir <- FilePath -> Text -> Maybe FilePath -> IO FilePath
enterSubDir (FilePath
projectBuildDir' FilePath -> FilePath -> FilePath
</> FilePath
"build") Text
"platform" ((Text -> FilePath) -> Maybe Text -> Maybe FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> FilePath
T.unpack Maybe Text
platform)
  FilePath -> Text -> Maybe FilePath -> IO FilePath
enterSubDir FilePath
platformDir Text
"compiler" ((Text -> FilePath) -> Maybe Text -> Maybe FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> FilePath
T.unpack Maybe Text
compiler)

exitOnError :: Text -> IO a
exitOnError :: Text -> IO a
exitOnError Text
log = do
  Handle -> Text -> IO ()
T.hPutStrLn Handle
stderr Text
log
  IO a
forall a. IO a
exitFailure

enterSubDir :: FilePath -> Text -> Maybe FilePath -> IO FilePath
enterSubDir :: FilePath -> Text -> Maybe FilePath -> IO FilePath
enterSubDir FilePath
baseDir Text
realm Maybe FilePath
givenSubDir = do
  [FilePath]
subDirs <- FilePath -> IO [FilePath]
listDirectory FilePath
baseDir
  case Maybe FilePath
givenSubDir Maybe FilePath -> (FilePath -> Maybe FilePath) -> Maybe FilePath
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\FilePath
d -> if FilePath
d FilePath -> [FilePath] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [FilePath]
subDirs then FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
d else Maybe FilePath
forall a. Maybe a
Nothing) of
    Just FilePath
wrongSubDir -> Text -> IO FilePath
forall a. Text -> IO a
exitOnError [i|specified #{realm} #{wrongSubDir} doesn't not exist, make sure to build first|]
    Maybe FilePath
Nothing -> case [FilePath] -> Maybe FilePath -> Maybe FilePath
selectSubDir [FilePath]
subDirs Maybe FilePath
givenSubDir of
      Just FilePath
subDir -> FilePath -> IO FilePath
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FilePath -> IO FilePath) -> FilePath -> IO FilePath
forall a b. (a -> b) -> a -> b
$ FilePath
baseDir FilePath -> FilePath -> FilePath
</> FilePath
subDir
      Maybe FilePath
_ -> Text -> IO FilePath
forall a. Text -> IO a
exitOnError [i|failed to guess #{realm}, please specify one|]
  where
    selectSubDir :: [FilePath] -> Maybe FilePath -> Maybe FilePath
    selectSubDir :: [FilePath] -> Maybe FilePath -> Maybe FilePath
selectSubDir [FilePath
exactlyOne] Maybe FilePath
_ = FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
exactlyOne
    selectSubDir [FilePath]
_ Maybe FilePath
given = Maybe FilePath
given

symlinkLocalPackages :: FilePath -> FilePath -> IO [LocalBuildInfo]
symlinkLocalPackages :: FilePath -> FilePath -> IO [LocalBuildInfo]
symlinkLocalPackages FilePath
localPackagesDir FilePath
destDir = do
  [FilePath]
localPackages <- (FilePath -> Bool) -> [FilePath] -> [FilePath]
forall a. (a -> Bool) -> [a] -> [a]
filter (FilePath -> FilePath -> Bool
forall source source1 target.
(RegexMaker Regex CompOption ExecOption source,
 RegexContext Regex source1 target) =>
source1 -> source -> target
=~ FilePath
packageNameRegex) ([FilePath] -> [FilePath]) -> IO [FilePath] -> IO [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO [FilePath]
listDirectory FilePath
localPackagesDir
  [FilePath]
-> (FilePath -> IO LocalBuildInfo) -> IO [LocalBuildInfo]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [FilePath]
localPackages ((FilePath -> IO LocalBuildInfo) -> IO [LocalBuildInfo])
-> (FilePath -> IO LocalBuildInfo) -> IO [LocalBuildInfo]
forall a b. (a -> b) -> a -> b
$ \FilePath
pkg -> do
    IO () -> (IOError -> IO ()) -> IO ()
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
catch (FilePath -> IO ()
removeDirectoryLink (FilePath
destDir FilePath -> FilePath -> FilePath
</> FilePath
pkg)) ((IOError -> IO ()) -> IO ()) -> (IOError -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(IOError
e :: IOError) ->
      if IOError -> Bool
isDoesNotExistError IOError
e then () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure () else IOError -> IO ()
forall a e. Exception e => e -> a
throw IOError
e
    FilePath -> FilePath -> IO ()
createDirectoryLink (FilePath
localPackagesDir FilePath -> FilePath -> FilePath
</> FilePath
pkg) (FilePath
destDir FilePath -> FilePath -> FilePath
</> FilePath
pkg)
    FilePath -> IO LocalBuildInfo
getPersistBuildConfig (FilePath
localPackagesDir FilePath -> FilePath -> FilePath
</> FilePath
pkg)
  where
    FilePath
packageNameRegex :: String =
      FilePath
"[[:digit:]]*[[:alpha:]][[:alnum:]]*(-[[:digit:]]*[[:alpha:]][[:alnum:]]*)*-[0-9]+([.][0-9]+)*"

symlinkDependencies :: [LocalBuildInfo] -> FilePath -> IO [PackageName]
symlinkDependencies :: [LocalBuildInfo] -> FilePath -> IO [PackageName]
symlinkDependencies [LocalBuildInfo]
localPackages FilePath
hoogleDependenciesDir = do
  let nameToPkgs :: Map PackageName (NonEmpty InstalledPackageInfo)
nameToPkgs =
        (NonEmpty InstalledPackageInfo -> NonEmpty InstalledPackageInfo)
-> Map PackageName (NonEmpty InstalledPackageInfo)
-> Map PackageName (NonEmpty InstalledPackageInfo)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((InstalledPackageInfo -> UnitId)
-> NonEmpty InstalledPackageInfo -> NonEmpty InstalledPackageInfo
forall b a. Ord b => (a -> b) -> NonEmpty a -> NonEmpty a
NonEmpty.nubOrdOn InstalledPackageInfo -> UnitId
installedUnitId) (Map PackageName (NonEmpty InstalledPackageInfo)
 -> Map PackageName (NonEmpty InstalledPackageInfo))
-> ([(PackageName, NonEmpty InstalledPackageInfo)]
    -> Map PackageName (NonEmpty InstalledPackageInfo))
-> [(PackageName, NonEmpty InstalledPackageInfo)]
-> Map PackageName (NonEmpty InstalledPackageInfo)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NonEmpty InstalledPackageInfo
 -> NonEmpty InstalledPackageInfo -> NonEmpty InstalledPackageInfo)
-> [(PackageName, NonEmpty InstalledPackageInfo)]
-> Map PackageName (NonEmpty InstalledPackageInfo)
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith NonEmpty InstalledPackageInfo
-> NonEmpty InstalledPackageInfo -> NonEmpty InstalledPackageInfo
forall a. Semigroup a => a -> a -> a
(<>) ([(PackageName, NonEmpty InstalledPackageInfo)]
 -> Map PackageName (NonEmpty InstalledPackageInfo))
-> [(PackageName, NonEmpty InstalledPackageInfo)]
-> Map PackageName (NonEmpty InstalledPackageInfo)
forall a b. (a -> b) -> a -> b
$
          (LocalBuildInfo -> [(PackageName, NonEmpty InstalledPackageInfo)])
-> [LocalBuildInfo]
-> [(PackageName, NonEmpty InstalledPackageInfo)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap LocalBuildInfo -> [(PackageName, NonEmpty InstalledPackageInfo)]
collectDependenciesForPkg [LocalBuildInfo]
localPackages
  [(PackageName, FilePath)]
pkgs <- ([Maybe (PackageName, FilePath)] -> [(PackageName, FilePath)])
-> IO [Maybe (PackageName, FilePath)]
-> IO [(PackageName, FilePath)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Maybe (PackageName, FilePath)] -> [(PackageName, FilePath)]
forall a. [Maybe a] -> [a]
catMaybes (IO [Maybe (PackageName, FilePath)]
 -> IO [(PackageName, FilePath)])
-> (((PackageName, NonEmpty InstalledPackageInfo)
     -> IO (Maybe (PackageName, FilePath)))
    -> IO [Maybe (PackageName, FilePath)])
-> ((PackageName, NonEmpty InstalledPackageInfo)
    -> IO (Maybe (PackageName, FilePath)))
-> IO [(PackageName, FilePath)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(PackageName, NonEmpty InstalledPackageInfo)]
-> ((PackageName, NonEmpty InstalledPackageInfo)
    -> IO (Maybe (PackageName, FilePath)))
-> IO [Maybe (PackageName, FilePath)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM (Map PackageName (NonEmpty InstalledPackageInfo)
-> [(PackageName, NonEmpty InstalledPackageInfo)]
forall k a. Map k a -> [(k, a)]
Map.toList Map PackageName (NonEmpty InstalledPackageInfo)
nameToPkgs) (((PackageName, NonEmpty InstalledPackageInfo)
  -> IO (Maybe (PackageName, FilePath)))
 -> IO [(PackageName, FilePath)])
-> ((PackageName, NonEmpty InstalledPackageInfo)
    -> IO (Maybe (PackageName, FilePath)))
-> IO [(PackageName, FilePath)]
forall a b. (a -> b) -> a -> b
$ \(PackageName
name, allPkgs :: NonEmpty InstalledPackageInfo
allPkgs@(InstalledPackageInfo
pkg NonEmpty.:| [InstalledPackageInfo]
pkgs)) -> do
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([InstalledPackageInfo] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [InstalledPackageInfo]
pkgs) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
      Text -> IO ()
T.putStrLn [i|Warning: package #{name} has more than 1 version installed, this should not happen. all pkgs: #{fmap installedUnitId allPkgs}|]
    case InstalledPackageInfo -> [FilePath]
haddockHTMLs InstalledPackageInfo
pkg of
      [FilePath
htmlDir] -> Maybe (PackageName, FilePath) -> IO (Maybe (PackageName, FilePath))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (PackageName, FilePath)
 -> IO (Maybe (PackageName, FilePath)))
-> Maybe (PackageName, FilePath)
-> IO (Maybe (PackageName, FilePath))
forall a b. (a -> b) -> a -> b
$ (PackageName, FilePath) -> Maybe (PackageName, FilePath)
forall a. a -> Maybe a
Just (PackageName
name, FilePath
htmlDir)
      [FilePath]
htmlDirs -> do
        Text -> IO ()
T.putStrLn [i|Warning: package #{name} doesn't have exactly one haddock html directory, actual: #{htmlDirs}|]
        Maybe (PackageName, FilePath) -> IO (Maybe (PackageName, FilePath))
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (PackageName, FilePath)
forall a. Maybe a
Nothing
  [(PackageName, FilePath)]
-> ((PackageName, FilePath) -> IO PackageName) -> IO [PackageName]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [(PackageName, FilePath)]
pkgs (((PackageName, FilePath) -> IO PackageName) -> IO [PackageName])
-> ((PackageName, FilePath) -> IO PackageName) -> IO [PackageName]
forall a b. (a -> b) -> a -> b
$ \(PackageName
name, FilePath
dir) -> do
    FilePath -> FilePath -> IO ()
createDirectoryLink FilePath
dir (FilePath
hoogleDependenciesDir FilePath -> FilePath -> FilePath
</> PackageName -> FilePath
unPackageName PackageName
name)
    PackageName -> IO PackageName
forall (f :: * -> *) a. Applicative f => a -> f a
pure PackageName
name
  where
    collectDependenciesForPkg :: LocalBuildInfo -> [(PackageName, NonEmpty InstalledPackageInfo)]
collectDependenciesForPkg LocalBuildInfo
pkg =
      let depsWithName :: [(PackageName, [InstalledPackageInfo])]
depsWithName = PackageIndex InstalledPackageInfo
-> [(PackageName, [InstalledPackageInfo])]
forall a. PackageIndex a -> [(PackageName, [a])]
allPackagesByName (LocalBuildInfo -> PackageIndex InstalledPackageInfo
installedPkgs LocalBuildInfo
pkg)
       in ((PackageName, InstalledPackageInfo)
 -> (PackageName, NonEmpty InstalledPackageInfo))
-> [(PackageName, InstalledPackageInfo)]
-> [(PackageName, NonEmpty InstalledPackageInfo)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((InstalledPackageInfo -> NonEmpty InstalledPackageInfo)
-> (PackageName, InstalledPackageInfo)
-> (PackageName, NonEmpty InstalledPackageInfo)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second (InstalledPackageInfo
-> [InstalledPackageInfo] -> NonEmpty InstalledPackageInfo
forall a. a -> [a] -> NonEmpty a
NonEmpty.:| []))
            ([(PackageName, InstalledPackageInfo)]
 -> [(PackageName, NonEmpty InstalledPackageInfo)])
-> ([(PackageName, [InstalledPackageInfo])]
    -> [(PackageName, InstalledPackageInfo)])
-> [(PackageName, [InstalledPackageInfo])]
-> [(PackageName, NonEmpty InstalledPackageInfo)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((PackageName, [InstalledPackageInfo])
 -> [(PackageName, InstalledPackageInfo)])
-> [(PackageName, [InstalledPackageInfo])]
-> [(PackageName, InstalledPackageInfo)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\(PackageName
name, [InstalledPackageInfo]
pkgs) -> (InstalledPackageInfo -> (PackageName, InstalledPackageInfo))
-> [InstalledPackageInfo] -> [(PackageName, InstalledPackageInfo)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (PackageName
name,) [InstalledPackageInfo]
pkgs)
            ([(PackageName, [InstalledPackageInfo])]
 -> [(PackageName, NonEmpty InstalledPackageInfo)])
-> [(PackageName, [InstalledPackageInfo])]
-> [(PackageName, NonEmpty InstalledPackageInfo)]
forall a b. (a -> b) -> a -> b
$ [(PackageName, [InstalledPackageInfo])]
depsWithName