Как программно получить информацию о пакете GHC?
Вопрос
Точнее, учитывая произвольное имя пакета, мне нужно получить то же самое. library-dirs
поле, которое можно получить с помощью ghc-pkg describe
команду изнутри работающей программы Haskell.
Решение
Вот что я мог придумать, заглянув в ghc-pkg
исходный код.
А getPkgInfos
Функция возвращает определения всех установленных пакетов (надеюсь, включая пакеты, установленные пользователем).Имея это в своих руках, вы можете получить каталоги библиотеки и другую информацию о пакете.Видеть документация для получения подробной информации.
А GHC_PKGCONF
Переменная должна указывать на глобальный файл конфигурации пакета для систем, где он не расположен в обычном месте. ghc-pkg
решает эту проблему, например, получая флаг командной строки через сценарий-оболочку в Ubuntu.
import qualified Config
import qualified System.Info
import Data.List
import Distribution.InstalledPackageInfo
import GHC.Paths
import System.Directory
import System.Environment
import System.FilePath
import System.IO.Error
getPkgInfos :: IO [InstalledPackageInfo]
getPkgInfos = do
global_conf <-
catch (getEnv "GHC_PKGCONF")
(\err -> if isDoesNotExistError err
then do let dir = takeDirectory $ takeDirectory ghc_pkg
path1 = dir </> "package.conf"
path2 = dir </> ".." </> ".." </> ".."
</> "inplace-datadir"
</> "package.conf"
exists1 <- doesFileExist path1
exists2 <- doesFileExist path2
if exists1 then return path1
else if exists2 then return path2
else ioError $ userError "Can't find package.conf"
else ioError err)
let global_conf_dir = global_conf ++ ".d"
global_conf_dir_exists <- doesDirectoryExist global_conf_dir
global_confs <-
if global_conf_dir_exists
then do files <- getDirectoryContents global_conf_dir
return [ global_conf_dir ++ '/' : file
| file <- files
, isSuffixOf ".conf" file]
else return []
user_conf <-
try (getAppUserDataDirectory "ghc") >>= either
(\_ -> return [])
(\appdir -> do
let subdir = currentArch ++ '-':currentOS ++ '-':ghcVersion
user_conf = appdir </> subdir </> "package.conf"
user_exists <- doesFileExist user_conf
return (if user_exists then [user_conf] else []))
let pkg_dbs = user_conf ++ global_confs ++ [global_conf]
return.concat =<< mapM ((>>= return.read).readFile) pkg_dbs
currentArch = System.Info.arch
currentOS = System.Info.os
ghcVersion = Config.cProjectVersion
Я написал этот код сам, но во многом он был вдохновлен ghc-pkg (некоторые фрагменты скопированы дословно).Исходный код был лицензирован под лицензией в стиле BSD. Я думаю, что он может распространяться под лицензией cc-wiki, под которой находится весь контент Stackoverflow, но я не совсем уверен.В любом случае, как и все остальное, я провел первоначальное тестирование, и, похоже, это работает, но используйте его на свой страх и риск.
Другие советы
Формат базы данных установленных пакетов: Distribution.InstalledPackageInfo
.
import Distribution.InstalledPackageInfo
import Distribution.Package
import Distribution.Text
import GHC.Paths
import System
import System.FilePath
main = do
name:_ <- getArgs
packages <- fmap read $ readFile $ joinPath [libdir, "package.conf"]
let matches = filter ((PackageName name ==) . pkgName . package) packages
mapM_ (print . libraryDirs) (matches :: [InstalledPackageInfo_ String])
Это не подчиняется конфигурации пакета пользователя, но должно быть началом.
Спросите Дункана Куттса в списках рассылки haskell-cafe@ или Cabal.(Я серьезно.Это лучший форум для вопросов Кабалы, чем переполнение стека).
Иногда вам просто нужно указать людям на другой форум.
Если вы используете Cabal для настройки и сборки своей программы/библиотеки, вы можете использовать автоматически сгенерированный модуль Paths_*.
Например, если у вас есть foo.cabal
файл, Cabal сгенерирует Paths_foo
модуль (см. его исходный код в разделе dist/build/autogen
), который вы можете импортировать.Этот модуль экспортирует функцию getLibDir :: IO FilePath
который имеет значение, которое вы ищете.