module MicroCabal.Backend.GHC(ghcBackend) where
import Control.Monad
import Data.Version
import System.Directory
import MicroCabal.Cabal
import MicroCabal.Env
import MicroCabal.Unix

ghcBackend :: Backend
ghcBackend = Backend {
  backendName = "ghc",
  doesPkgExist = ghcExists,
  buildPkgExe = ghcBuildExe,
  buildPkgLib = ghcBuildLib,
  installPkgExe = ghcInstallExe,
  installPkgLib = ghcInstallLib
  }

getGhcName :: Env -> IO FilePath
getGhcName env = ("ghc-" ++) . takeWhile (/= '\n') <$> cmdOut env "ghc --numeric-version"

getGhcDir :: Env -> IO FilePath
getGhcDir env = ((cabalDir env ++ "/") ++) <$> getGhcName env

getBuildDir :: Env -> IO FilePath
getBuildDir env = do
  ghc <- getGhcName env
  return $ distDir env ++ "/build/" ++ ghc

initDB :: Env -> IO ()
initDB env = do
  dir <- getGhcDir env
  b <- doesDirectoryExist dir
  when (not b) $ do
    when (verbose env > 0) $
      putStrLn $ "Creating GHC package db " ++ dir
    cmd env $ "ghc-pkg init " ++ dir

ghcExists :: Env -> PackageName -> IO Bool
ghcExists env pkgname = do
  dir <- getGhcDir env
  tryCmd env $ "ghc-pkg --package-db=" ++ dir ++ " describe >/dev/null 2>/dev/null " ++ pkgname

setupStdArgs :: Env -> [Field] -> IO [String]
setupStdArgs env flds = do
  db <- getGhcDir env
  let srcDirs = getFieldStrings flds ["."]   "hs-source-dirs"
      defExts = getFieldStrings flds []      "default-extensions"
      exts    = getFieldStrings flds defExts "extensions"
      opts    = getFieldStrings flds []      "ghc-options"
      cppOpts = getFieldStrings flds []      "cpp-options"
      deps    = getBuildDependsPkg flds
  buildDir <- getBuildDir env
  return $ [ "-package-env=-", "-package-db=" ++ db, "-outputdir=" ++ buildDir, "-w"] ++
           map ("-i" ++) srcDirs ++
           map ("-X" ++) exts ++
           map ("-package " ++) deps ++
           opts ++ cppOpts

binGhc :: String
binGhc  = "/bin/ghc/"

ghcBuildExe :: Env -> Section -> Section -> IO ()
ghcBuildExe env _ (Section _ name flds) = do
  initDB env
  let mainIs  = getFieldString flds "main-is"
      srcDirs = getFieldStrings flds ["."] "hs-source-dirs"
      bin     = distDir env ++ binGhc ++ name
  mkdir env $ distDir env ++ binGhc
  mainIs' <- findMainIs env srcDirs mainIs
  stdArgs <- setupStdArgs env flds
  let args    = unwords $ ["-O"] ++ stdArgs ++ ["-o", bin, "--make", mainIs']
  when (verbose env >= 0) $
    putStrLn $ "Build executable " ++ bin ++ " with ghc"
  cmd env $ "ghc " ++ args

findMainIs :: Env -> [FilePath] -> FilePath -> IO FilePath
findMainIs _ [] fn = error $ "cannot find " ++ show fn
findMainIs env (d:ds) fn = do
  let fn' = d ++ "/" ++ fn
  b <- doesFileExist fn'
  if b then
    return fn'
   else
    findMainIs env ds fn

getExposedModules :: [Field] -> [String]
getExposedModules flds = getFieldStrings flds (error "no exposed-modules") "exposed-modules"

ghcBuildLib :: Env -> Section -> Section -> IO ()
ghcBuildLib env (Section _ _ glob) (Section _ name flds) = do
  initDB env
  stdArgs <- setupStdArgs env flds
  let mdls = getExposedModules flds
      ver  = getVersion glob "version"
      args = unwords $ ["-O"] ++ stdArgs ++
                       ["--make", "-no-link", "-this-unit-id", key ] ++
                       ["-fbuilding-cabal-package", "-static" ] ++
                       mdls
      key = name ++ "-" ++ showVersion ver ++ "-mcabal"
  when (verbose env >= 0) $
    putStrLn $ "Build library " ++ name ++ " with ghc"
  cmd env $ "ghc " ++ args

ghcInstallExe :: Env -> Section -> Section -> IO ()
ghcInstallExe env (Section _ _ _glob) (Section _ name _) = do
  let bin = distDir env ++ binGhc ++ name
      binDir = cabalDir env ++ "/bin"
  mkdir env binDir
  cp env bin (binDir ++ "/" ++ name)

ghcInstallLib :: Env -> Section -> Section -> IO ()
ghcInstallLib env (Section _ _ glob) (Section _ name flds) = do
  initDB env
  buildDir <- getBuildDir env
  ghc <- getGhcDir env
  let namever = name ++ "-" ++ showVersion vers
      destDir = ghc ++ "/" ++ namever
      vers = getVersion glob "version"
      arch = destDir ++ "/" ++ "libHS" ++ namever ++ "-mcabal.a"
  mkdir env destDir
  rmrf env arch
  cmd env $ "ar -c -r -s " ++ arch ++ " `find " ++ buildDir ++ " -name '*.o'`"

  let files = map mdlToHi mdls
      mdls = getExposedModules flds
      mdlToHi = (++ ".hi") . map (\ c -> if c == '.' then '/' else c)
  copyFiles env buildDir files destDir

  db <- getGhcDir env
  let desc = unlines
        [ "name: " ++ name
        , "version: " ++ showVersion vers
        , "visibility: public"
        , "id: " ++ key
        , "key: " ++ key
        , "exposed: True"
        , "exposed-modules: " ++ unwords mdls
        , "import-dirs: " ++ destDir
        , "library-dirs: " ++ destDir
        , "library-dirs-static: " ++ destDir
        , "hs-libraries: HS" ++ key
        , "depends: " ++ unwords depends
        ]
      depends = []  -- XXX
      key = namever ++ "-mcabal"
      pkgFn = db ++ "/" ++ key ++ ".conf"
  writeFile pkgFn desc
  cmd env $ "ghc-pkg update --package-db=" ++ db ++ " " ++ pkgFn
