module Data.GI.CodeGen.Cabal
( genCabalProject
, cabalConfig
, setupHs
) where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>), (<*>))
#endif
import Control.Monad (forM_)
import Control.Monad.IO.Class (liftIO)
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Data.Version (Version(..))
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Text (Text)
import Text.Read
import System.IO (stderr)
import Data.GI.CodeGen.API (GIRInfo(..))
import Data.GI.CodeGen.Code
import Data.GI.CodeGen.Config (Config(..))
import Data.GI.CodeGen.Overrides (pkgConfigMap, cabalPkgVersion)
import Data.GI.CodeGen.PkgConfig (pkgConfigGetVersion)
import qualified Data.GI.CodeGen.ProjectInfo as PI
import Data.GI.CodeGen.Util (padTo, tshow)
import Paths_haskell_gi (version)
cabalConfig :: Text
cabalConfig = T.unlines ["optimization: False"]
setupHs :: Text
setupHs = T.unlines ["#!/usr/bin/env runhaskell",
"import Distribution.Simple",
"main = defaultMain"]
haskellGIAPIVersion :: Int
haskellGIAPIVersion = (head . versionBranch) version
minorVersion :: [Int] -> Int
minorVersion (_:y:_) = y
minorVersion v = error $ "Programming error: the haskell-gi version does not have at least two components: " ++ show v ++ "."
haskellGIMinor :: Int
haskellGIMinor = minorVersion (versionBranch version)
giModuleVersion :: Int -> Int -> Text
giModuleVersion major minor =
(T.intercalate "." . map tshow) [haskellGIAPIVersion, major, minor,
haskellGIMinor]
giNextMinor :: Int -> Int -> Text
giNextMinor major minor = (T.intercalate "." . map tshow)
[haskellGIAPIVersion, major, minor+1]
data PkgInfo = PkgInfo { pkgName :: Text
, pkgMajor :: Int
, pkgMinor :: Int
} deriving Show
tryPkgConfig :: Text -> Text -> [Text] -> Bool
-> M.Map Text Text
-> CodeGen (Either Text PkgInfo)
tryPkgConfig name version packages verbose overridenNames =
liftIO (pkgConfigGetVersion name version packages verbose overridenNames) >>= \case
Just (n,v) ->
case readMajorMinor v of
Just (major, minor) ->
return $ Right (PkgInfo { pkgName = n
, pkgMajor = major
, pkgMinor = minor})
Nothing -> return $ Left $ "Cannot parse version \"" <> v <>
"\" for module " <> name
Nothing -> return $ Left $
"Could not determine the pkg-config name corresponding to \"" <> name <> "\".\n" <>
"Try adding an override with the proper package name:\n"
<> "pkg-config-name " <> name <> " [matching pkg-config name here]"
readMajorMinor :: Text -> Maybe (Int, Int)
readMajorMinor version =
case T.splitOn "." version of
(a:b:_) -> (,) <$> readMaybe (T.unpack a) <*> readMaybe (T.unpack b)
_ -> Nothing
genCabalProject :: GIRInfo -> [GIRInfo] -> [Text] -> BaseVersion ->
CodeGen (Maybe Text)
genCabalProject gir deps exposedModules minBaseVersion = do
cfg <- config
let pkMap = pkgConfigMap (overrides cfg)
name = girNSName gir
pkgVersion = girNSVersion gir
packages = girPCPackages gir
line $ "-- Autogenerated, do not edit."
line $ padTo 20 "name:" <> "gi-" <> T.toLower name
eitherPkgInfo <- tryPkgConfig name pkgVersion packages (verbose cfg) pkMap
cabalVersion <- case eitherPkgInfo of
Left err -> do
liftIO $ TIO.hPutStrLn stderr $ "Warning: " <> err
return "0.0.1"
Right (PkgInfo _ major minor) ->
return $ fromMaybe (giModuleVersion major minor)
(cabalPkgVersion $ overrides cfg)
line $ padTo 20 "version:" <> cabalVersion
line $ padTo 20 "synopsis:" <> name
<> " bindings"
line $ padTo 20 "description:" <> "Bindings for " <> name
<> ", autogenerated by haskell-gi."
line $ padTo 20 "homepage:" <> PI.homepage
line $ padTo 20 "license:" <> PI.license
line $ padTo 20 "license-file:" <> "LICENSE"
line $ padTo 20 "author:" <> PI.authors
line $ padTo 20 "maintainer:" <> PI.maintainers
line $ padTo 20 "category:" <> PI.category
line $ padTo 20 "build-type:" <> "Simple"
line $ padTo 20 "cabal-version:" <> ">=1.10"
blank
line $ "library"
indent $ do
line $ padTo 20 "default-language:" <> PI.defaultLanguage
line $ padTo 20 "default-extensions:" <>
T.intercalate ", " PI.defaultExtensions
line $ padTo 20 "other-extensions:" <>
T.intercalate ", " PI.otherExtensions
line $ padTo 20 "ghc-options:" <> T.intercalate " " PI.ghcOptions
line $ padTo 20 "exposed-modules:" <> head exposedModules
forM_ (tail exposedModules) $ \mod ->
line $ padTo 20 "" <> mod
case eitherPkgInfo of
Left _ -> do
line $ padTo 20 "-- pkg-config info for the module not found:"
line $ padTo 20 "-- pkgconfig-depends: XXX"
Right (PkgInfo pcName major minor) ->
line $ padTo 20 "pkgconfig-depends:" <> pcName <> " >= " <>
tshow major <> "." <> tshow minor
line $ "build-depends:"
indent $ do
line $ "haskell-gi-base >= "
<> tshow haskellGIAPIVersion <> "." <> tshow haskellGIMinor
<> " && < " <> tshow (haskellGIAPIVersion + 1) <> ","
forM_ deps $ \dep -> do
let depName = girNSName dep
depVersion = girNSVersion dep
depPackages = girPCPackages dep
eitherDepInfo <- tryPkgConfig depName depVersion
depPackages (verbose cfg) pkMap
case eitherDepInfo of
Left err -> do
liftIO $ TIO.hPutStrLn stderr $ "Warning: " <> err
line $ "-- gi-" <> T.toLower depName <> " >= XXX & < YYY"
Right (PkgInfo _ depMajor depMinor) ->
line $ "gi-" <> T.toLower depName <> " >= "
<> giModuleVersion depMajor depMinor
<> " && < "
<> giNextMinor depMajor depMinor
<> ","
forM_ PI.standardDeps (line . (<> ","))
line $ "base >= " <> showBaseVersion minBaseVersion <> " && <5"
return Nothing