Como recuperar programaticamente GHC informações pacote?
Pergunta
Mais especificamente, dado um nome de pacote arbritary eu preciso para recuperar o mesmo campo library-dirs
que pode ser obtido com o comando ghc-pkg describe
de dentro de um programa Haskell em execução.
Solução
Aqui está o que eu poderia vir acima com por espreitar para o código-fonte ghc-pkg
.
A função getPkgInfos
retorna as definições de pacote para todos os pacotes instalados (espero incluindo pacotes instalados pelo usuário). Com isto em suas mãos, você pode recuperar os diretórios de bibliotecas e outras informações pacote. Consulte documentação para detalhes.
A variável GHC_PKGCONF
precisa apontar para o arquivo de configuração pacote global para sistemas onde ele não está localizado no lugar de costume. resolve ghc-pkg
esse problema ao receber um sinalizador de linha de comando por meio de um script wrapper no Ubuntu, por exemplo.
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
Eu escrevi este código de mim mesmo, mas foi em grande parte inspirado pelo GHC-pacote (com algumas peças copiada verbatim). O código original foi licenciada sob uma licença BSD, eu acho que isso pode ser distribuído sob a cc-wiki licenciar todos os conteúdos Stackoverflow é baixo, mas eu não tenho certeza. Enfim, como qualquer outra coisa, eu fiz alguns testes iniciais e parece trabalho, mas usá-lo em seu próprio risco.
Outras dicas
O formato do banco de dados de pacotes instalados é 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])
Esta não obedecer a configuração do pacote do usuário, mas deve ser um começo.
Pergunte Duncan Coutts na listas de discussão cabal haskell-cafe @ ou. (Estou falando sério. Isso é um melhor fórum para perguntas Cabal que estouro de pilha).
Às vezes você apenas tem que ponto as pessoas em um fórum diferente.
Se você estiver usando cabal para configurar e construir o seu programa / biblioteca você pode utilizar o Paths_ * módulo gerada automaticamente.
Por exemplo, se você tem um arquivo foo.cabal
, cabala irá gerar um módulo Paths_foo
(ver sua fonte sob dist/build/autogen
), que você pode importar. Este exportações módulo A getLibDir :: IO FilePath
função que tem o valor que você está procurando.