Comment récupérer des informations de programme package GHC?
Question
Plus précisément, étant donné un nom de package arbritary je dois récupérer le même champ de library-dirs
qui peut être obtenu avec la commande ghc-pkg describe
à l'intérieur d'un programme Haskell en cours d'exécution.
La solution
Voici ce que je pouvais venir avec par jeter un oeil dans le code source de ghc-pkg
.
La fonction getPkgInfos
renvoie les définitions de paquet pour tous les paquets installés (nous l'espérons, y compris les paquets installés par l'utilisateur). Avec cela dans vos mains, vous pouvez récupérer les répertoires de bibliothèques et d'autres informations sur le package. Voir la documentation détails.
La variable GHC_PKGCONF
doit pointer vers le fichier de configuration de package globale pour les systèmes où il n'est pas situé à l'endroit habituel. ghc-pkg
résout ce problème en recevant un indicateur de ligne de commande via un script wrapper dans Ubuntu, par exemple.
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
J'ai écrit ce code moi-même, mais il a été largement inspiré par GHC-pkg (avec quelques morceaux copiés tels quels). Le code original a été distribué sous licence une licence de type BSD, je pense que cela peut être distribué sous la licence cc-wiki tout le contenu Stackoverflow est sous, mais je ne suis pas vraiment sûr. Quoi qu'il en soit, comme toute autre chose, je fait quelques tests initiaux et il semble fonctionner, mais l'utiliser à vos propres risques.
Autres conseils
Le format de la base de données de paquets installés est 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])
Cela ne respecte pas la configuration du package de l'utilisateur, mais devrait être un début.
Demandez Duncan Coutts sur le haskell-café @ ou listes de diffusion cabale. (Je suis sérieux. C'est un meilleur forum pour les questions Cabal que débordement de la pile).
Parfois, il vous suffit de pointer les gens à un autre forum.
Si vous utilisez cabale pour configurer et construire votre programme / bibliothèque, vous pouvez utiliser le module autogenerated Paths_ *.
Par exemple, si vous avez un fichier foo.cabal
, cabale va générer un module Paths_foo
(voir sa source sous dist/build/autogen
) que vous pouvez importer. Ce module exporte une getLibDir :: IO FilePath
fonction qui a la valeur que vous cherchez.