#!/usr/bin/env runhaskell
\begin{code}
{-# LANGUAGE CPP #-}
import Prelude hiding (mod)
import Control.Monad
import Control.Applicative
import Data.List
import Data.Maybe
import Distribution.InstalledPackageInfo hiding (dataDir)
import Distribution.Package (PackageName (..))
import Distribution.Simple.Compiler
import Distribution.Simple.GHC
import Distribution.Simple.PackageIndex
import Distribution.Simple.Program
import Distribution.Simple.Utils
import Distribution.Verbosity
import System.IO
import System.Directory
import System.Environment
import System.Exit
import System.FilePath
import System.Process (ProcessHandle, runProcess, waitForProcess, system)
packageRoot, dataDir, haddockPath, baseDir, testDir, outDir, refDir :: FilePath
baseDir = takeDirectory __FILE__
testDir = baseDir </> "src"
refDir = baseDir </> "ref"
outDir = baseDir </> "out"
packageRoot = baseDir </> ".."
dataDir = packageRoot </> "resources"
haddockPath = packageRoot </> "dist" </> "build" </> "haddock" </> "haddock"
main :: IO ()
main = do
test
putStrLn "All tests passed!"
test :: IO ()
test = do
x <- doesFileExist haddockPath
unless x $ System.Exit.die "you need to run 'cabal build' successfully first"
contents <- getDirectoryContents testDir
args <- getArgs
let (opts, spec) = span ("-" `isPrefixOf`) args
isDir x' = liftM2 (&&) (doesDirectoryExist $ testDir </> x')
(return $ x' /= "." && x' /= "..")
modDirs <- case spec of
y:_ | y /= "all" -> return [y]
_ -> filterM isDir contents
let modDirs' = map (testDir </>) modDirs
-- add haddock_datadir to environment for subprocesses
env <- Just . (:) ("haddock_datadir", dataDir) <$> getEnvironment
putStrLn ""
putStrLn "Haddock version: "
h1 <- runProcess haddockPath ["--version"] Nothing
env Nothing Nothing Nothing
wait h1 "*** Running `haddock --version' failed!"
putStrLn ""
putStrLn "GHC version: "
h2 <- runProcess haddockPath ["--ghc-version"] Nothing
env Nothing Nothing Nothing
wait h2 "*** Running `haddock --ghc-version' failed!"
putStrLn ""
-- TODO: maybe do something more clever here using haddock.cabal
ghcPath <- fmap init $ rawSystemStdout normal haddockPath ["--print-ghc-path"]
(_, _, conf) <- configure normal (Just ghcPath) Nothing defaultProgramConfiguration
pkgIndex <- getInstalledPackages normal [GlobalPackageDB] conf
let mkDep pkgName =
fromMaybe (error "Couldn't find test dependencies") $ do
let pkgs = lookupPackageName pkgIndex (PackageName pkgName)
(_, pkgs') <- listToMaybe pkgs
pkg <- listToMaybe pkgs'
ifacePath <- listToMaybe (haddockInterfaces pkg)
htmlPath <- listToMaybe (haddockHTMLs pkg)
return ("-i " ++ htmlPath ++ "," ++ ifacePath)
let base = mkDep "base"
process = mkDep "process"
ghcprim = mkDep "ghc-prim"
putStrLn "Running tests..."
forM_ modDirs' $ \modDir -> do
testModules <- getDirectoryContents modDir
let mods = filter ((==) ".hs" . takeExtension) testModules
mods' = map (modDir </>) mods
unless (null mods') $ do
handle <- runProcess haddockPath
(["-w", "-o", outDir </> last (splitPath modDir), "--latex"
, "--optghc=-fglasgow-exts"
, "--optghc=-w", base, process, ghcprim] ++ opts ++ mods')
Nothing env Nothing
Nothing Nothing
wait handle "*** Haddock run failed! Exiting."
check modDirs (if not (null args) && args !! 0 == "all" then False else True)
where
wait :: ProcessHandle -> String -> IO ()
wait h msg = do
r <- waitForProcess h
unless (r == ExitSuccess) $ do
hPutStrLn stderr msg
exitFailure
check :: [FilePath] -> Bool -> IO ()
check modDirs strict = do
forM_ modDirs $ \modDir -> do
let oDir = outDir </> modDir
rDir = refDir </> modDir
refDirExists <- doesDirectoryExist rDir
when refDirExists $ do
-- we're not creating sub-directories, I think.
refFiles <- getDirectoryContents rDir >>= filterM doesFileExist
forM_ refFiles $ \rFile -> do
let refFile = rDir </> rFile
outFile = oDir </> rFile
oe <- doesFileExist outFile
if oe
then do
out <- readFile outFile
ref <- readFile refFile
if out /= ref
then do
putStrLn $ "Output for " ++ modDir ++ " has changed! Exiting with diff:"
let reffile' = outDir </> takeFileName refFile ++ ".nolinks"
outfile' = outDir </> takeFileName outFile ++ ".ref.nolinks"
writeFile reffile' ref
writeFile outfile' out
r <- programOnPath "colordiff"
code <- if r
then system $ "colordiff " ++ reffile' ++ " " ++ outfile'
else system $ "diff " ++ reffile' ++ " " ++ outfile'
if strict then exitFailure else return ()
unless (code == ExitSuccess) $ do
hPutStrLn stderr "*** Running diff failed!"
exitFailure
else do
putStrLn $ "Pass: " ++ modDir
else do
putStrLn $ "Pass: " ++ modDir ++ " (no .ref file)"
programOnPath :: FilePath -> IO Bool
programOnPath p = do
result <- findProgramLocation silent p
return (isJust result)
\end{code}