Commands.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. import subprocess
  2. import colorama
  3. from colorama import init,Fore, Back, Style
  4. import argparse
  5. import os
  6. import os.path
  7. from contextlib import contextmanager
  8. import shutil
  9. import glob
  10. from pathlib import Path
  11. import sys
  12. DEBUGMODE = False
  13. KEEPBUILDFOLDER = False
  14. DEBUGLIST=[
  15. "-DBASICMATH=ON",
  16. "-DCOMPLEXMATH=OFF",
  17. "-DCONTROLLER=OFF",
  18. "-DFASTMATH=OFF",
  19. "-DFILTERING=OFF",
  20. "-DMATRIX=OFF",
  21. "-DSTATISTICS=OFF",
  22. "-DSUPPORT=OFF",
  23. "-DTRANSFORM=OFF",
  24. "-DSVM=OFF",
  25. "-DBAYES=OFF",
  26. "-DDISTANCE=OFF"]
  27. NOTESTFAILED = 0
  28. MAKEFAILED = 1
  29. TESTFAILED = 2
  30. FLOWFAILURE = 3
  31. CALLFAILURE = 4
  32. def setDebugMode():
  33. global DEBUGMODE
  34. DEBUGMODE=True
  35. def isDebugMode():
  36. return(DEBUGMODE)
  37. def joinit(iterable, delimiter):
  38. it = iter(iterable)
  39. yield next(it)
  40. for x in it:
  41. yield delimiter
  42. yield x
  43. def addToDb(db,desc,runid):
  44. msg("Add %s to summary database\n" % desc)
  45. completed = subprocess.run([sys.executable, "addToDB.py","-o",db,"-r",str(runid),desc], timeout=3600)
  46. check(completed)
  47. def addToRegDb(db,desc,runid):
  48. msg("Add %s to regression database\n" % desc)
  49. completed = subprocess.run([sys.executable, "addToRegDB.py","-o",db,"-r",str(runid),desc], timeout=3600)
  50. check(completed)
  51. class TestFlowFailure(Exception):
  52. def __init__(self,completed):
  53. self._errorcode = completed.returncode
  54. def errorCode(self):
  55. return(self._errorcode)
  56. class CallFailure(Exception):
  57. pass
  58. def check(n):
  59. #print(n)
  60. if n is not None:
  61. if n.returncode != 0:
  62. raise TestFlowFailure(n)
  63. else:
  64. raise CallFailure()
  65. def msg(t):
  66. print(Fore.CYAN + t + Style.RESET_ALL)
  67. def errorMsg(t):
  68. print(Fore.RED + t + Style.RESET_ALL)
  69. def fullTestFolder(rootFolder):
  70. return(os.path.join(rootFolder,"CMSIS","DSP","Testing","fulltests"))
  71. class BuildConfig:
  72. def __init__(self,toUnset,toSet,rootFolder,buildFolder,compiler,toolchain,core,cmake):
  73. self._toUnset = toUnset
  74. self._toSet = toSet
  75. self._buildFolder = buildFolder
  76. self._rootFolder = os.path.abspath(rootFolder)
  77. self._dspFolder = os.path.join(self._rootFolder,"CMSIS","DSP")
  78. self._testingFolder = os.path.join(self._dspFolder,"Testing")
  79. self._fullTests = os.path.join(self._testingFolder,"fulltests")
  80. self._compiler = compiler
  81. self._toolchain = toolchain
  82. self._core = core
  83. self._cmake = cmake
  84. self._savedEnv = {}
  85. def compiler(self):
  86. return(self._compiler)
  87. def toolChainFile(self):
  88. return(self._toolchain)
  89. def core(self):
  90. return(self._core)
  91. def path(self):
  92. return(os.path.join(self._fullTests,self._buildFolder))
  93. def archivePath(self):
  94. return(os.path.join(self._fullTests,"archive",self._buildFolder))
  95. def archiveResultPath(self):
  96. return(os.path.join(self._fullTests,"archive",self._buildFolder,"results"))
  97. def archiveLogPath(self):
  98. return(os.path.join(self._fullTests,"archive",self._buildFolder,"logs"))
  99. def archiveErrorPath(self):
  100. return(os.path.join(self._fullTests,"archive",self._buildFolder,"errors"))
  101. def toolChainPath(self):
  102. return(self._dspFolder)
  103. def cmakeFilePath(self):
  104. return(self._testingFolder)
  105. def buildFolderName(self):
  106. return(self._buildFolder)
  107. def saveEnv(self):
  108. if self._toUnset is not None:
  109. for v in self._toUnset:
  110. if v in os.environ:
  111. self._savedEnv[v] = os.environ[v]
  112. else:
  113. self._savedEnv[v] = None
  114. del os.environ[v]
  115. if self._toSet is not None:
  116. for v in self._toSet:
  117. for varName in v:
  118. if varName in os.environ:
  119. self._savedEnv[varName] = os.environ[varName]
  120. else:
  121. self._savedEnv[varName] = None
  122. os.environ[varName] = v[varName]
  123. def restoreEnv(self):
  124. for v in self._savedEnv:
  125. if self._savedEnv[v] is not None:
  126. os.environ[v] = self._savedEnv[v]
  127. else:
  128. if v in os.environ:
  129. del os.environ[v]
  130. self._savedEnv = {}
  131. # Build for a folder
  132. # We need to be able to detect failed build
  133. def build(self,test):
  134. completed=None
  135. # Save and unset some environment variables
  136. self.saveEnv()
  137. with self.buildFolder() as b:
  138. msg(" Build %s\n" % self.buildFolderName())
  139. with open(os.path.join(self.archiveLogPath(),"makelog_%s.txt" % test),"w") as makelog:
  140. with open(os.path.join(self.archiveErrorPath(),"makeerror_%s.txt" % test),"w") as makeerr:
  141. if DEBUGMODE:
  142. completed=subprocess.run(["make","-j4","VERBOSE=1"],timeout=3600)
  143. else:
  144. completed=subprocess.run(["make","-j4","VERBOSE=1"],stdout=makelog,stderr=makeerr,timeout=3600)
  145. # Restore environment variables
  146. self.restoreEnv()
  147. check(completed)
  148. def getTest(self,test):
  149. return(Test(self,test))
  150. # Launch cmake command.
  151. def createCMake(self,configid,flags,benchMode,platform):
  152. with self.buildFolder() as b:
  153. self.saveEnv()
  154. if benchMode:
  155. msg("Benchmark mode\n")
  156. msg("Create cmake for %s\n" % self.buildFolderName())
  157. toolchainCmake = os.path.join(self.toolChainPath(),self.toolChainFile())
  158. cmd = [self._cmake]
  159. cmd += ["-DCMAKE_PREFIX_PATH=%s" % self.compiler(),
  160. "-DCMAKE_TOOLCHAIN_FILE=%s" % toolchainCmake,
  161. "-DARM_CPU=%s" % self.core(),
  162. "-DPLATFORM=%s" % platform,
  163. "-DCONFIGID=%s" % configid
  164. ]
  165. cmd += flags
  166. if DEBUGMODE:
  167. cmd += DEBUGLIST
  168. if benchMode:
  169. cmd += ["-DBENCHMARK=ON"]
  170. cmd += ["-DWRAPPER=ON"]
  171. else:
  172. cmd += ["-DBENCHMARK=OFF"]
  173. cmd += ["-DWRAPPER=OFF"]
  174. cmd += ["-DCONFIGTABLE=OFF",
  175. "-DROOT=%s" % self._rootFolder,
  176. "-DCMAKE_BUILD_TYPE=Release",
  177. "-G", "Unix Makefiles" ,"%s" % self.cmakeFilePath()]
  178. if DEBUGMODE:
  179. print(cmd)
  180. with open(os.path.join(self.archiveLogPath(),"cmakecmd.txt"),"w") as cmakecmd:
  181. cmakecmd.write("".join(joinit(cmd," ")))
  182. with open(os.path.join(self.archiveLogPath(),"cmakelog.txt"),"w") as cmakelog:
  183. with open(os.path.join(self.archiveErrorPath(),"cmakeerror.txt"),"w") as cmakeerr:
  184. completed=subprocess.run(cmd, stdout=cmakelog,stderr=cmakeerr, timeout=3600)
  185. self.restoreEnv()
  186. check(completed)
  187. # Create the build folder if missing
  188. def createFolder(self):
  189. os.makedirs(self.path(),exist_ok=True)
  190. def createArchive(self, flags):
  191. os.makedirs(self.archivePath(),exist_ok=True)
  192. os.makedirs(self.archiveResultPath(),exist_ok=True)
  193. os.makedirs(self.archiveErrorPath(),exist_ok=True)
  194. os.makedirs(self.archiveLogPath(),exist_ok=True)
  195. with open(os.path.join(self.archivePath(),"flags.txt"),"w") as f:
  196. for flag in flags:
  197. f.write(flag)
  198. f.write("\n")
  199. # Delete the build folder
  200. def cleanFolder(self):
  201. print("Delete %s\n" % self.path())
  202. #DEBUG
  203. if not DEBUGMODE and not KEEPBUILDFOLDER:
  204. shutil.rmtree(self.path())
  205. # Archive results and currentConfig.csv to another folder
  206. def archiveResults(self):
  207. results=glob.glob(os.path.join(self.path(),"results_*"))
  208. for result in results:
  209. dst=os.path.join(self.archiveResultPath(),os.path.basename(result))
  210. shutil.copy(result,dst)
  211. src = os.path.join(self.path(),"currentConfig.csv")
  212. dst = os.path.join(self.archiveResultPath(),os.path.basename(src))
  213. shutil.copy(src,dst)
  214. @contextmanager
  215. def buildFolder(self):
  216. current=os.getcwd()
  217. try:
  218. os.chdir(self.path() )
  219. yield self.path()
  220. finally:
  221. os.chdir(current)
  222. @contextmanager
  223. def archiveFolder(self):
  224. current=os.getcwd()
  225. try:
  226. os.chdir(self.archivePath() )
  227. yield self.archivePath()
  228. finally:
  229. os.chdir(current)
  230. @contextmanager
  231. def resultFolder(self):
  232. current=os.getcwd()
  233. try:
  234. os.chdir(self.archiveResultPath())
  235. yield self.archiveResultPath()
  236. finally:
  237. os.chdir(current)
  238. @contextmanager
  239. def logFolder(self):
  240. current=os.getcwd()
  241. try:
  242. os.chdir(self.archiveLogPath())
  243. yield self.archiveLogPath()
  244. finally:
  245. os.chdir(current)
  246. @contextmanager
  247. def errorFolder(self):
  248. current=os.getcwd()
  249. try:
  250. os.chdir(self.archiveErrorPath())
  251. yield self.archiveErrorPath()
  252. finally:
  253. os.chdir(current)
  254. class Test:
  255. def __init__(self,build,test):
  256. self._test = test
  257. self._buildConfig = build
  258. def buildConfig(self):
  259. return(self._buildConfig)
  260. def testName(self):
  261. return(self._test)
  262. # Process a test from the test description file
  263. def processTest(self):
  264. completed=subprocess.run([sys.executable,"processTests.py","-e",self.testName()],timeout=3600)
  265. check(completed)
  266. def getResultPath(self):
  267. return(os.path.join(self.buildConfig().path() ,self.resultName()))
  268. def resultName(self):
  269. return("results_%s.txt" % self.testName())
  270. # Run a specific test in the current folder
  271. # A specific results.txt file is created in
  272. # the build folder for this test
  273. #
  274. # We need a timeout and detect failed run
  275. def run(self,fvp,benchmode):
  276. timeoutVal=3600
  277. if benchmode:
  278. timeoutVal = 3600 * 4
  279. completed = None
  280. with self.buildConfig().buildFolder() as b:
  281. msg(" Run %s\n" % self.testName() )
  282. with open(self.resultName(),"w") as results:
  283. completed=subprocess.run(fvp.split(),stdout=results,timeout=timeoutVal)
  284. check(completed)
  285. # Process results of the given tests
  286. # in given build folder
  287. # We need to detect failed tests
  288. def processResult(self):
  289. msg(" Parse result for %s\n" % self.testName())
  290. with open(os.path.join(self.buildConfig().archiveResultPath(),"processedResult_%s.txt" % self.testName()),"w") as presult:
  291. completed=subprocess.run([sys.executable,"processResult.py","-e","-r",self.getResultPath()],stdout=presult,timeout=3600)
  292. # When a test fail, the regression is continuing but we
  293. # track that a test has failed
  294. if completed.returncode==0:
  295. return(NOTESTFAILED)
  296. else:
  297. return(TESTFAILED)
  298. # Compute the regression data
  299. def computeSummaryStat(self):
  300. msg(" Compute regressions for %s\n" % self.testName())
  301. with open(os.path.join(self.buildConfig().archiveResultPath(),"processedResult_%s.txt" % self.testName()),"w") as presult:
  302. completed=subprocess.run([sys.executable,"summaryBench.py","-r",self.getResultPath()],stdout=presult,timeout=3600)
  303. # When a test fail, the regression is continuing but we
  304. # track that a test has failed
  305. if completed.returncode==0:
  306. return(NOTESTFAILED)
  307. else:
  308. return(TESTFAILED)
  309. def runAndProcess(self,compiler,fvp,sim,benchmode,db,regdb,benchid,regid):
  310. # If we can't parse test description we fail all tests
  311. self.processTest()
  312. # Otherwise if only building or those tests are failing, we continue
  313. # with other tests
  314. try:
  315. self.buildConfig().build(self.testName())
  316. except:
  317. return(MAKEFAILED)
  318. # We run tests only for AC6
  319. # For other compilers only build is tests
  320. # Since full build is no more possible because of huge pattersn,
  321. # build is done per test suite.
  322. if sim:
  323. if fvp is not None:
  324. self.run(fvp,benchmode)
  325. error=self.processResult()
  326. if benchmode and (error == NOTESTFAILED):
  327. error = self.computeSummaryStat()
  328. if db is not None:
  329. addToDb(db,self.testName(),benchid)
  330. if regdb is not None:
  331. addToRegDb(regdb,self.testName(),regid)
  332. return(error)
  333. else:
  334. msg("No FVP available")
  335. return(NOTESTFAILED)
  336. else:
  337. return(NOTESTFAILED)
  338. # Preprocess the test description
  339. def preprocess(desc):
  340. msg("Process test description file %s\n" % desc)
  341. completed = subprocess.run([sys.executable, "preprocess.py","-f",desc],timeout=3600)
  342. check(completed)
  343. # Generate all missing C code by using all classes in the
  344. # test description file
  345. def generateAllCCode():
  346. msg("Generate all missing C files\n")
  347. completed = subprocess.run([sys.executable,"processTests.py", "-e"],timeout=3600)
  348. check(completed)
  349. # Create db
  350. def createDb(sqlite,desc):
  351. msg("Create database %s\n" % desc)
  352. with open("createDb.sql") as db:
  353. completed = subprocess.run([sqlite, desc],stdin=db, timeout=3600)
  354. check(completed)