1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Main where
import Control.Applicative ((<$>), (<*>))
import Control.Monad
import Shelly hiding (FilePath)
import Data.Char
import Data.List (nub)
import Data.Text (Text)
import Data.Monoid
import qualified Data.Text as T
import Data.Yaml
default (T.Text)
data RegressionTest = RegressionTest
{ name :: Text
, cabal :: Bool
, flags :: [Text]
, aptPPA :: [Text]
, aptPackages :: [Text]
, cabalBuildTools :: [Text]
, specialSetup :: [Text]
, extraPath :: [Text]
, extraSOPath :: [Text]
, extraIncludeDirs :: [Text]
, extraLibDirs :: [Text]
, onTravis :: Bool
, runTests :: Bool
} deriving (Eq, Show)
instance FromJSON RegressionTest where
parseJSON (Object v) = RegressionTest <$> v .: "name"
<*> v .:? "cabal" .!= True
<*> v .:? "flags" .!= []
<*> v .:? "apt-ppa" .!= []
<*> v .:? "apt-packages" .!= []
<*> v .:? "cabal-build-tools" .!= []
<*> v .:? "special-setup" .!= []
<*> v .:? "extra-path" .!= []
<*> v .:? "extra-so-path" .!= []
<*> v .:? "extra-include-dirs" .!= []
<*> v .:? "extra-lib-dirs" .!= []
<*> v .:? "on-travis" .!= True
<*> v .:? "run-tests" .!= False
parseJSON _ = mzero
data Code = TestOK
| DepsFailed
| ConfFailed
| BuildFailed
| TestsFailed
deriving Eq
instance Show Code where
show TestOK = "OK"
show DepsFailed = "dependencies"
show ConfFailed = "configuration"
show BuildFailed = "build"
show TestsFailed = "tests"
makeCode :: (Int, Int, Int, Int) -> Code
makeCode (0, 0, 0, 0) = TestOK
makeCode (0, 0, 0, _) = TestsFailed
makeCode (0, 0, _, _) = BuildFailed
makeCode (0, _, _, _) = ConfFailed
makeCode (_, _, _, _) = DepsFailed
readTests :: FilePath -> IO [RegressionTest]
readTests fp = maybe [] id <$> decodeFile fp
checkApt :: Sh ()
checkApt = do
apt <- which "apt-get"
case apt of
Nothing -> errorExit "Can't find apt-get. Are you sure this is Ubuntu?"
_ -> return ()
main :: IO ()
main = shelly $ do
travis <- maybe False (const True) <$> get_env "TRAVIS"
enabled <- maybe False (const True) <$> get_env "C2HS_REGRESSION_SUITE"
when (not (travis || enabled)) $ do
echo "REGRESSION SUITE IS DISABLED"
exit 0
when travis checkApt
let travisCheck t = case travis of
False -> True
True -> onTravis t
tests <- liftIO $ filter travisCheck <$>
readTests "tests/regression-suite.yaml"
let ppas = nub $ concatMap aptPPA tests
pkgs = nub $ concatMap aptPackages tests
buildTools = nub $ concatMap cabalBuildTools tests
specials = concatMap specialSetup tests
extraPaths = concatMap extraPath tests
extraSOPaths = concatMap extraSOPath tests
when (not travis) $
echo "ASSUMING THAT ALL NECESSARY LIBRARIES ALREADY INSTALLED!\n"
home <- fromText <$> get_env_text "HOME"
appendToPath $ home </> ".cabal/bin"
when travis $ do
when (not (null ppas)) $ do
echo "SETTING UP APT PPAS\n"
forM_ ppas $ \ppa -> run_ "sudo" $ ["apt-add-repository", "ppa:" <> ppa]
run_ "sudo" $ ["apt-get", "update"]
echo "\n"
when (not (null pkgs)) $ do
echo "INSTALLING APT PACKAGES\n"
run_ "sudo" $ ["apt-get", "install", "-y"] ++ pkgs
echo "\n"
when (not (null specials)) $ do
echo "SPECIAL INSTALL STEPS\n"
forM_ specials $ \s -> let (c:as) = escapedWords s in
run_ (fromText c) as
echo "\n"
when (not (null extraPaths)) $ do
echo "ADDING PATHS\n"
forM_ extraPaths $ \p -> do
echo p
appendToPath $ fromText p
echo "\n"
when (not (null extraSOPaths)) $ do
echo "ADDING SHARED LIBRARY PATHS\n"
forM_ extraSOPaths $ \p -> do
echo p
appendToSOPath p
echo "\n"
codes <- forM (filter cabal tests) $ \t -> do
let n = name t
tst = runTests t
infs = concatMap (\f -> ["-f", f]) $ flags t
extralibs = map (\f -> "--extra-lib-dirs=" <> f) $
extraLibDirs t
extraincs = map (\f -> "--extra-include-dirs=" <> f) $
extraIncludeDirs t
mefs <- get_env $ "C2HS_REGRESSION_FLAGS_" <> n
let fs = if tst then ["--enable-tests"] else [] ++ case mefs of
Nothing -> infs
Just efs -> infs ++ concatMap (\f -> ["-f", f]) (T.splitOn "," efs)
echo $ "\nREGRESSION TEST: " <> n <> "\n"
errExit False $ do
unpack <- run "cabal" ["unpack", n]
let d = T.drop (T.length "Unpacking to ") $ T.init $ last $ T.lines unpack
chdir (fromText d) $ do
run_ "cabal" $ ["sandbox", "init"]
run_ "cabal" $ ["install", "--only-dep", "-v"] ++ fs
dep <- lastExitCode
run_ "cabal" $ ["configure"] ++ extraincs ++ extralibs ++ fs
conf <- lastExitCode
run_ "cabal" $ ["build"]
build <- lastExitCode
test <-
if tst then do
run_ "cabal" ["test"]
lastExitCode
else return 0
return $ makeCode (dep, conf, build, test)
if all (== TestOK) codes
then exit 0
else do
echo "\n\nSOME TESTS FAILED\n"
let failed = filter (\(c, _) -> c /= TestOK) $ zip codes (filter cabal tests)
forM_ failed $ \(c, t) -> echo $ "FAILED: " <> name t <>
" (" <> T.pack (show c) <> ")"
exit 1
escapedWords :: Text -> [Text]
escapedWords = map (T.pack . reverse) . escWords False "" . T.unpack
where escWords :: Bool -> String -> String -> [String]
-- End of string: just return the accumulator if there is one.
escWords _ acc "" = case acc of
"" -> []
_ -> [acc]
-- Not escaping.
escWords False acc (c:cs)
| isSpace c = acc : escWords False "" cs
| c == '\'' = case acc of
"" -> escWords True "" cs
_ -> acc : escWords True "" cs
| otherwise = escWords False (c:acc) cs
-- Escaping.
escWords True acc (c:cs)
| c == '\'' = acc : escWords False "" cs
| otherwise = escWords True (c:acc) cs
appendToSOPath :: Text -> Sh ()
appendToSOPath tp = do
pe <- get_env_text "LD_LIBRARY_PATH"
setenv "LD_LIBRARY_PATH" $ pe <> ":" <> tp
|