-- copyright (c) 2008 Duncan Coutts -- portions copyright (c) 2008 David Roundy -- portions copyright (c) 2007-2009 Judah Jacobson {-# OPTIONS_GHC -Wall #-} import Distribution.Simple ( defaultMainWithHooks, UserHooks(..), simpleUserHooks ) import Distribution.PackageDescription ( PackageDescription ) import Distribution.Package ( packageVersion ) import Distribution.Version( Version ) import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..), absoluteInstallDirs ) import Distribution.Simple.InstallDirs (mandir, CopyDest (NoCopyDest)) import Distribution.Simple.Setup (buildVerbosity, copyDest, copyVerbosity, fromFlag, haddockVerbosity, installVerbosity, replVerbosity ) import Distribution.Simple.BuildPaths ( autogenPackageModulesDir ) import Distribution.Simple.Utils (copyFiles, createDirectoryIfMissingVerbose, rawSystemStdout, rewriteFileEx) import Distribution.Verbosity ( Verbosity, silent ) import Distribution.Text ( display ) import Control.Monad ( unless, when, void ) import System.Directory ( doesDirectoryExist, doesFileExist ) import System.IO ( openFile, IOMode(..) ) import System.Process (callProcess, runProcess) import Data.List( isInfixOf ) import System.FilePath ( () ) import qualified Control.Exception as Exception catchAny :: IO a -> (Exception.SomeException -> IO a) -> IO a catchAny f h = Exception.catch f (\e -> h (e :: Exception.SomeException)) main :: IO () main = defaultMainWithHooks $ simpleUserHooks { buildHook = \ pkg lbi hooks flags -> let verb = fromFlag $ buildVerbosity flags in commonBuildHook buildHook pkg lbi hooks verb >>= ($ flags), haddockHook = \ pkg lbi hooks flags -> let verb = fromFlag $ haddockVerbosity flags in commonBuildHook haddockHook pkg lbi hooks verb >>= ($ flags) , replHook = \pkg lbi hooks flags args -> let verb = fromFlag $ replVerbosity flags in commonBuildHook replHook pkg lbi hooks verb >>= (\f -> f flags args) , postBuild = \ _ _ _ lbi -> buildManpage lbi, postCopy = \ _ flags pkg lbi -> installManpage pkg lbi (fromFlag $ copyVerbosity flags) (fromFlag $ copyDest flags), postInst = \ _ flags pkg lbi -> installManpage pkg lbi (fromFlag $ installVerbosity flags) NoCopyDest } -- | For @./Setup build@ and @./Setup haddock@, do some unusual -- things, then invoke the base behaviour ("simple hook"). commonBuildHook :: (UserHooks -> PackageDescription -> LocalBuildInfo -> t -> a) -> PackageDescription -> LocalBuildInfo -> t -> Verbosity -> IO a commonBuildHook runHook pkg lbi hooks verbosity = do versionInfoExists <- (&&) <$> doesFileExist "release/distributed-context" <*> doesFileExist "release/distributed-version" unless versionInfoExists $ callProcess "cabal" ["run", "./release/gen-version-info.hs"] (version, state) <- determineVersion verbosity pkg generateVersionModule verbosity lbi version state return $ runHook simpleUserHooks pkg lbi hooks -- --------------------------------------------------------------------- -- man page -- --------------------------------------------------------------------- buildManpage :: LocalBuildInfo -> IO () buildManpage lbi = do have_darcs_exe_dir <- doesDirectoryExist (buildDir lbi "darcs") when have_darcs_exe_dir $ do let darcs = buildDir lbi "darcs/darcs" manpage = buildDir lbi "darcs/darcs.1" manpageHandle <- openFile manpage WriteMode void $ runProcess darcs ["help","manpage"] Nothing Nothing Nothing (Just manpageHandle) Nothing installManpage :: PackageDescription -> LocalBuildInfo -> Verbosity -> CopyDest -> IO () installManpage pkg lbi verbosity copy = do have_manpage <- doesFileExist (buildDir lbi "darcs" "darcs.1") when have_manpage $ copyFiles verbosity (mandir (absoluteInstallDirs pkg lbi copy) "man1") [(buildDir lbi "darcs", "darcs.1")] -- --------------------------------------------------------------------- -- version module -- --------------------------------------------------------------------- determineVersion :: Verbosity -> PackageDescription -> IO (String, String) determineVersion verbosity pkg = do let darcsVersion = packageVersion pkg numPatches <- versionPatches verbosity darcsVersion return (display darcsVersion, versionStateString numPatches) where versionStateString :: Maybe Int -> String versionStateString Nothing = "unknown" versionStateString (Just 0) = "release" versionStateString (Just 1) = "+ 1 patch" versionStateString (Just n) = "+ " ++ show n ++ " patches" versionPatches :: Verbosity -> Version -> IO (Maybe Int) versionPatches verbosity darcsVersion = do numPatchesDarcs <- do out <- rawSystemStdout verbosity "darcs" ["log", "-a", "--from-tag", display darcsVersion, "--count"] case reads out of ((n,_):_) -> return $ Just ((n :: Int) - 1) _ -> return Nothing `catchAny` \_ -> return Nothing numPatchesDist <- parseFile "release/distributed-version" return $ case (numPatchesDarcs, numPatchesDist) of (Just x, _) -> Just x (Nothing, Just x) -> Just x (Nothing, Nothing) -> Nothing generateVersionModule :: Verbosity -> LocalBuildInfo -> String -> String -> IO () generateVersionModule verbosity lbi version state = do let dir = autogenPackageModulesDir lbi createDirectoryIfMissingVerbose verbosity True dir ctx <- context verbosity hash <- weakhash verbosity rewriteFileEx silent (dir "Version.hs") $ unlines ["module Version where" ,"import Darcs.Prelude" ,"version, weakhash, context :: String" ,"version = \"" ++ version ++ " (" ++ state ++ ")\"" ,"weakhash = " ++ case hash of Just x -> show x Nothing -> show "not available" ,"context = " ++ case ctx of Just x -> show x Nothing -> show "context not available" ] weakhash :: Verbosity -> IO (Maybe String) weakhash verbosity = do inrepo <- doesDirectoryExist "_darcs" unless inrepo $ fail "Not a repository." out <- rawSystemStdout verbosity "darcs" ["show", "repo"] let line = filter ("Weak Hash:" `isInfixOf`) $ lines out return $ case line of [] -> Nothing x:_ -> Just $ last $ words x `catchAny` \_ -> return Nothing context :: Verbosity -> IO (Maybe String) context verbosity = do inrepo <- doesDirectoryExist "_darcs" unless inrepo $ fail "Not a repository." out <- rawSystemStdout verbosity "darcs" ["log", "-a", "--context"] return $ Just out `catchAny` \_ -> parseFile "release/distributed-context" parseFile :: Read a => FilePath -> IO (Maybe a) parseFile f = do exist <- doesFileExist f if exist then do content <- readFile f case reads content of ((s, _):_) -> return s _ -> return Nothing else return Nothing