Commands.py 14 KB

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