Commands.py 11 KB

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