-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathrun.hs
207 lines (176 loc) · 6.58 KB
/
run.hs
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
202
203
204
205
206
207
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Main where
import Shelly.Lifted
import System.Exit
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Data.Text (Text)
import Data.Time.Clock
import Data.String (fromString)
import Options.Applicative as O
import Control.Monad (unless)
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Maybe
import Control.Error
import System.IO (openFile, IOMode(..))
import System.Directory
import qualified Filesystem.Path.CurrentOS as Path
import Prelude hiding (FilePath)
-- * Things given to us by Harbormaster
newtype Repository = Repo Text
newtype Commit = Commit Text
newtype Revision = Rev Integer
newtype Diff = Diff Integer
newtype BuildId = BuildId Text
repo :: Repository
repo = Repo "rGHC"
sourceRepo :: Text
sourceRepo = "git://git.haskell.org/ghc"
data Options = Options { maxThreads :: Maybe Int
, referenceRepo :: Maybe FilePath
, buildId :: BuildId
, archivePath :: FilePath
}
data Task = BuildDiff { repository :: Repository
, revision :: Revision
, diff :: Diff
}
| BuildCommit { repository :: Repository
, commit :: Commit
}
type BuildM = ReaderT Options Sh
getOptions :: BuildM Options
getOptions = ask
runBuildM :: BuildM a -> Options -> IO a
runBuildM action opts =
shelly $ runReaderT action opts
cpuCount :: BuildM Int
cpuCount =
fromMaybe 1 <$> runMaybeT (config <|> windows <|> linux <|> freebsd)
where
config, windows, linux, freebsd :: MaybeT BuildM Int
config = MaybeT $ fmap maxThreads getOptions
windows = MaybeT $ (>>= readT) <$> get_env "NUMBER_OF_PROCESSORS"
linux = MaybeT $ readT <$> cmd "getconf" "_NPROCESSORS_ONLN"
freebsd = MaybeT $ readT <$> cmd "getconf" "NPROCESSORS_ONLN"
readT :: Read a => Text -> Maybe a
readT = readZ . T.unpack
logStr :: String -> BuildM ()
logStr = liftIO . putStr
timeIt :: String -> BuildM a -> BuildM a
timeIt what action = do
start <- liftIO getCurrentTime
logStr $ "- "<>what
r <- action
end <- liftIO getCurrentTime
let delta = end `diffUTCTime` start
logStr $ "took "<>show delta<>"\n"
return r
cloneGhc :: RepoDir -> BuildM ()
cloneGhc (RepoDir repoDir) = timeIt "cloning tree" $ do
opts <- getOptions
rm_rf repoDir
case referenceRepo opts of
Just refRepo -> do
refExists <- liftIO $ doesDirectoryExist (Path.encodeString refRepo)
unless refExists $ do
cmd "git" "clone" "--bare" sourceRepo refRepo
() <- cmd "git" "-C" refRepo "remote" "update"
() <- cmd "git" "-C" refRepo "submodule" "update"
() <- cmd "git" "clone" "--reference" refRepo sourceRepo repoDir
chdir repoDir $ do
cmd "git" "submodule" "update" "--init"
Nothing ->
cmd "git" "clone" sourceRepo repoDir
newtype RepoDir = RepoDir FilePath
inRepo :: RepoDir -> BuildM a -> BuildM a
inRepo (RepoDir dir) = chdir dir
applyDiff :: RepoDir -> Diff -> BuildM ()
applyDiff repoDir (Diff d) = inRepo repoDir $
cmd "arc" "patch" "--nobranch" "--force" "--nocommit" "--diff" (T.pack $ show d)
checkout :: RepoDir -> Commit -> BuildM ()
checkout repoDir (Commit commit) = timeIt "checking out commit " $ inRepo repoDir $
cmd "git" "checkout" commit
updateSubmodules :: RepoDir -> BuildM ()
updateSubmodules repoDir = timeIt "updating submodules" $ inRepo repoDir $
cmd "git" "submodule" "update"
validate :: RepoDir -> FilePath -> BuildM ExitCode
validate repoDir log = timeIt "validating" $ inRepo repoDir $ do
hdl <- liftIO $ openFile (Path.encodeString log) WriteMode
let handles = [ OutHandle $ UseHandle hdl
, ErrorHandle $ UseHandle hdl
]
errExit False $ do
runHandles "sh" ["validate"] handles (\_ _ _ -> return ())
c <- lastExitCode
case c of
0 -> do liftIO $ putStrLn "Validate finished successfully"
return ExitSuccess
_ -> do liftIO $ putStrLn $ "Validate failed with exit code "<>show c
return $ ExitFailure c
archiveFile :: FilePath -> BuildM ()
archiveFile path = do
opts <- getOptions
let compressedPath = path <.> "xz"
BuildId buildDir = buildId opts
finalPath = (archivePath opts </> buildDir </> compressedPath)
cmd "xz" "-9" "-o" finalPath path
showTestsuiteSummary :: RepoDir -> BuildM ()
showTestsuiteSummary (RepoDir dir) = do
c <- readfile (dir </> "testsuite_summary.txt")
liftIO $ do
putStrLn ""
putStrLn "================== Testsuite summary =================="
T.putStrLn c
options :: Parser Options
options = Options
<$> option auto
(short 't' <> long "threads" <> value Nothing)
<*> option (Just . fromString <$> str)
(short 'r' <> long "reference-repo" <> value Nothing)
<*> option (BuildId . T.pack <$> str)
(short 'B' <> long "build-id")
<*> option (fromString <$> str)
(short 'a' <> long "archive" <> metavar "DIR" <> value ".")
task :: Parser (BuildM ExitCode)
task = subparser $ buildDiff <> buildCommit
where
commit = option (Commit . T.pack <$> str) (short 'c' <> long "commit" <> metavar "COMMIT")
buildDiff = O.command "diff" $ info
(testDiff <$> option (Rev <$> auto) (short 'r' <> long "revision" <> metavar "REV")
<*> option (Diff <$> auto) (short 'd' <> long "diff" <> metavar "DIFF")
<*> commit
)
mempty
buildCommit = O.command "commit" $ info
(testCommit <$> commit)
mempty
main :: IO ()
main = do
(opts, action) <- execParser $ info (helper <*> ((,) <$> options <*> task)) mempty
code <- flip runBuildM opts $ verbosely action
exitWith code
testDiff :: Revision -> Diff -> Commit -> BuildM ExitCode
testDiff rev diff baseCommit = do
let repoDir = RepoDir "ghc-test"
cloneGhc repoDir
checkout repoDir baseCommit
updateSubmodules repoDir
applyDiff repoDir diff
updateSubmodules repoDir
code <- validate repoDir "build.log"
archiveFile "build.log"
showTestsuiteSummary repoDir
return code
testCommit :: Commit -> BuildM ExitCode
testCommit commit = do
let repoDir = RepoDir "ghc-test"
cloneGhc repoDir
checkout repoDir commit
updateSubmodules repoDir
code <- validate repoDir "build.log"
archiveFile "build.log"
showTestsuiteSummary repoDir
return code