Commands.py 11 KB

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