import System.IO import System.Exit import System.Directory import System.FilePath import Control.Applicative import Control.Monad import Control.Exception(bracket) import Data.List import Distribution.PackageDescription import Distribution.Simple import Distribution.Simple.Program import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Setup import Distribution.System import Distribution.Verbosity import Distribution.Version import qualified Distribution.ModuleName main = defaultMainWithHooks $ simpleUserHooks { buildHook = ogreBuildHook , instHook = ogreInstHook , copyHook = ogreCopyHook , regHook = ogreRegHook , cleanHook = ogreCleanHook , haddockHook = ogreHaddockHook , hookedPrograms = [simpleProgram "cgen", simpleProgram "grgen", simpleProgram "cgen-hs"] } err :: String -> IO a err str = hPutStrLn stderr str >> exitWith (ExitFailure 1) getSrcFile :: PackageDescription -> String -> IO FilePath getSrcFile pd ext = do let files = filter (\f -> takeExtension f == (extSeparator:ext)) $ extraSrcFiles pd case files of [file] -> return file files -> err $ "Error in extra source files configuration: Exactly one file with the extension \"" ++ ext ++ "\" allowed. Found:\n\t" ++ show files getFiles :: String -> FilePath -> IO [FilePath] getFiles ext dir = map (dir ) <$> filter (\f -> takeExtension f == (extSeparator:ext)) <$> getDirectoryContents dir resRoot :: String resRoot = "res" resDir :: String -> FilePath resDir n = resRoot n resCgen = resDir "cgen" resCgenHsBase = resDir "cgen-hs" resCgenHs = resCgenHsBase"Graphics""Ogre" resLib = resDir "lib" graphFile = resDir "graph" "graph.txt" inDir :: FilePath -> IO () -> IO () inDir path act = bracket getCwd putCwd (\_ -> setCurrentDirectory path >> act) where getCwd = getCurrentDirectory putCwd = setCurrentDirectory pathToModuleName :: FilePath -> Distribution.ModuleName.ModuleName pathToModuleName = Distribution.ModuleName.fromString . intercalate "." . splitBy isPathSeparator where splitBy :: (Char -> Bool) -> String -> [String] splitBy fun str = let (w1, rest) = break fun str in if null rest then if null w1 then [] else [w1] else w1 : splitBy fun (tail rest) fullModuleName :: String -> Distribution.ModuleName.ModuleName fullModuleName s = Distribution.ModuleName.fromString $ "Graphics.Ogre." ++ s mkLibrary :: LocalBuildInfo -> IO Library mkLibrary lb = do genhsmodulenames <- map takeBaseName <$> getFiles "hs" resCgenHs cppfiles <- getFiles "cpp" resCgen includepaths <- getIncludePaths lb let expModules = map fullModuleName ["HOgre", "Types"] libbuildinfo = emptyBuildInfo { buildable = True, pkgconfigDepends = [(Dependency (PackageName "OgreMain") (laterVersion (Version [1,8] [])))], cSources = cppfiles, hsSourceDirs = [resCgenHsBase], otherModules = (filter (`notElem` expModules) (map fullModuleName genhsmodulenames)), extraLibs = ["OgreMain"], includeDirs = includepaths, targetBuildDepends = [Dependency (PackageName "base") (intersectVersionRanges (orLaterVersion (Version [3] [])) (earlierVersion (Version [5] []))), Dependency (PackageName "haskell98") anyVersion] } return $ Library expModules True libbuildinfo getConfiguredIncludePaths :: LocalBuildInfo -> [String] getConfiguredIncludePaths lb = case library (localPkgDescr lb) of Nothing -> [] Just l -> includeDirs (libBuildInfo l) getIncludePaths :: LocalBuildInfo -> IO [String] getIncludePaths lb = do let defincludepaths = getConfiguredIncludePaths lb case buildOS of Windows -> return defincludepaths _ -> do pkgconfig <- getProgram "pkg-config" mogreincpath <- (filter (\w -> take 2 w == "-I") . words) <$> getProgramOutput normal pkgconfig ["--cflags", "OGRE"] case mogreincpath of [] -> do putStrLn "Warning: Could not find OGRE include path with pkg-config - will assume" putStrLn " the OGRE headers are in the default include path. If the" putStrLn " compilation fails, try installing pkg-config and make sure that" putStrLn " OGRE is installed and pkg-config correctly configured." putStrLn " Alternatively, use the cabal --extra-include-dirs option." return defincludepaths (x:_) -> return $ [drop 2 x] getProgram pn = fst <$> requireProgram normal (simpleProgram pn) defaultProgramConfiguration ogreBuildHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO () ogreBuildHook pd lb uh bf = do geniffile <- getSrcFile pd "if" winiffile <- getSrcFile pd "if_win" igfile <- getSrcFile pd "ig" hiffile <- getSrcFile pd "hif" listfile <- getSrcFile pd "list" winlistfile <- getSrcFile pd "list_win" cgen <- getProgram "cgen" grgen <- getProgram "grgen" cgenhs <- getProgram "cgen-hs" let headerlistfile = case buildOS of Windows -> winlistfile _ -> listfile let iffile = case buildOS of Windows -> winiffile _ -> geniffile headerlist <- lines <$> readFile headerlistfile includepaths <- getIncludePaths lb let includepath = case includepaths of [] -> "" (x:_) -> x when (not $ null includepath) $ putStrLn $ "Searching for OGRE headers in: " ++ includepath let includesetup = if null includepath then [] else ["--include", includepath] runProgram normal cgen (["-o", resCgen, "--interface", iffile] ++ includesetup ++ headerlist) runProgram normal grgen (["--interface", igfile] ++ includesetup ++ ["-o", graphFile] ++ headerlist) headerfiles <- getFiles "h" resCgen runProgram normal cgenhs (["--interface", hiffile, "-u", "HOgre.hs", "--hierarchy", "Graphics.Ogre.", "--inherit", graphFile, "-o", resCgenHs] ++ headerfiles) createDirectoryIfMissing True resLib lib <- mkLibrary lb (buildHook simpleUserHooks) pd{library = Just lib} lb uh bf ogreInstHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO () ogreInstHook pd lb uh ifl = do lib <- mkLibrary lb (instHook simpleUserHooks) pd{library = Just lib} lb uh ifl ogreCopyHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags -> IO () ogreCopyHook pd lb uh cfl = do lib <- mkLibrary lb (copyHook simpleUserHooks) pd{library = Just lib} lb uh cfl ogreRegHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO () ogreRegHook pd lb uh rfl = do lib <- mkLibrary lb (regHook simpleUserHooks) pd{library = Just lib} lb uh rfl ogreCleanHook pd n uh cf = do exists <- doesDirectoryExist resRoot when exists (removeDirectoryRecursive resRoot) (cleanHook simpleUserHooks) pd n uh cf ogreHaddockHook pd lb uh hf = do lib <- mkLibrary lb (haddockHook simpleUserHooks) pd{library = Just lib} lb uh hf