nsdk_execute.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. import time
  5. import copy
  6. import glob
  7. import tempfile
  8. import json
  9. import argparse
  10. from nsdk_builder import *
  11. from nsdk_utils import *
  12. class nsdk_executor(nsdk_runner):
  13. def __init__(self):
  14. super().__init__()
  15. pass
  16. def build_apps(self, config:dict, show_output=True, logdir=None, stoponfail=False):
  17. if isinstance(config, dict) == False:
  18. return False, None
  19. global_build_config = config.get("build_config", dict())
  20. global_target = config.get("build_target", "all")
  21. global_cpobjs = config.get("copy_objects", None)
  22. global_parallel = config.get("parallel", "")
  23. rootdirs = config.get("appdirs", [])
  24. ignored_rootdirs = config.get("appdirs_ignore", [])
  25. if (isinstance(rootdirs, list) and isinstance(ignored_rootdirs, list)) == False:
  26. print("appdirs type in config should be list, please check!")
  27. return False, None
  28. if len(rootdirs) == 0:
  29. print("No appdirs specified in config, please check!")
  30. return False, None
  31. root_appdirs = dict()
  32. appnum = 0
  33. # Find all the apps in rootdirs
  34. for rootdir in rootdirs:
  35. root_appdirs[rootdir] = self.find_apps(rootdir)
  36. appnum += len(root_appdirs[rootdir])
  37. # Find all the ignored apps in ignored_rootdirs
  38. ignored_apps = []
  39. for ig_rootdir in ignored_rootdirs:
  40. ignored_apps.extend(self.find_apps(ig_rootdir))
  41. if appnum == 0:
  42. print("No application found in appdirs specified in config")
  43. return False, None
  44. createlog = False
  45. if isinstance(logdir, str):
  46. createlog = True
  47. if os.path.isdir(logdir) == False:
  48. os.makedirs(logdir)
  49. appconfigs = config.get("appconfig", dict())
  50. # Construct all the applications' configuration
  51. apps_config = dict()
  52. for rootdir in root_appdirs:
  53. for appdir in root_appdirs[rootdir]:
  54. if appdir in ignored_apps:
  55. print("%s is ignored by appdirs_ignore section" % (appdir))
  56. continue
  57. appdir = appdir.replace("\\", "/") # Change windows \\ path to /
  58. applogfile = None
  59. if createlog:
  60. applogfile = get_logfile(appdir, rootdir, logdir, "build.log")
  61. app_buildcfg = copy.deepcopy(global_build_config)
  62. app_buildtarget = copy.deepcopy(global_target)
  63. app_cpobjs = copy.deepcopy(global_cpobjs)
  64. app_parallel = copy.deepcopy(global_parallel)
  65. found_cfg = find_local_appconfig(appdir, appconfigs)
  66. if found_cfg:
  67. appcfg = appconfigs[found_cfg]
  68. # merge_global is True if not present
  69. merge_global = appcfg.get("merge_global", True)
  70. # Merge global config when merge_global is True
  71. if merge_global:
  72. app_buildcfg.update(appcfg.get("build_config", dict()))
  73. if "build_target" in appcfg:
  74. app_buildtarget = appcfg["build_target"]
  75. if "parallel" in appcfg:
  76. app_parallel = appcfg["parallel"]
  77. if "copy_objects" in appcfg:
  78. app_cpobjs = appcfg["copy_objects"]
  79. else: # if merge_global is false, then use app config only
  80. app_buildcfg = appcfg.get("build_config", dict())
  81. app_buildtarget = appcfg.get("build_target", "all")
  82. app_parallel = appcfg.get("parallel", "")
  83. app_cpobjs = appcfg.get("copy_objects", None)
  84. appconfig = {"build_config": app_buildcfg, "build_target": app_buildtarget, \
  85. "parallel": app_parallel, "logs": {"build": applogfile}}
  86. if app_cpobjs is not None:
  87. appconfig["copy_objects"] = app_cpobjs
  88. apps_config[appdir] = copy.deepcopy(appconfig)
  89. if len(apps_config) == 0:
  90. print("No applications need to be run according to the configuration")
  91. return False, None
  92. # Build all the applications
  93. print("Build %d applications defined by configuration" % (len(apps_config)))
  94. cmdsts, build_status = self.build_apps_with_config(apps_config, show_output, stoponfail)
  95. return cmdsts, build_status
  96. def run_apps(self, config:dict, show_output=True, logdir=None, stoponfail=False):
  97. if isinstance(config, dict) == False:
  98. return False, None
  99. global_build_config = config.get("build_config", dict())
  100. global_target = config.get("build_target", "clean all")
  101. global_cpobjs = config.get("copy_objects", None)
  102. global_parallel = config.get("parallel", "")
  103. global_run_config = config.get("run_config", dict())
  104. global_checks = config.get("checks", dict())
  105. rootdirs = config.get("appdirs", [])
  106. ignored_rootdirs = config.get("appdirs_ignore", [])
  107. if (isinstance(rootdirs, list) and isinstance(ignored_rootdirs, list)) == False:
  108. print("appdirs type in config should be list, please check!")
  109. return False, None
  110. if len(rootdirs) == 0:
  111. print("No appdirs specified in config, please check!")
  112. return False, None
  113. root_appdirs = dict()
  114. appnum = 0
  115. # Find all the apps in rootdirs
  116. for rootdir in rootdirs:
  117. root_appdirs[rootdir] = self.find_apps(rootdir)
  118. appnum += len(root_appdirs[rootdir])
  119. # Find all the ignored apps in ignored_rootdirs
  120. ignored_apps = []
  121. for ig_rootdir in ignored_rootdirs:
  122. ignored_apps.extend(self.find_apps(ig_rootdir))
  123. if appnum == 0:
  124. print("No application found in appdirs specified in config")
  125. return False, None
  126. createlog = False
  127. if isinstance(logdir, str):
  128. createlog = True
  129. if os.path.isdir(logdir) == False:
  130. os.makedirs(logdir)
  131. appconfigs = config.get("appconfig", dict())
  132. # Construct all the applications' configuration
  133. apps_config = dict()
  134. for rootdir in root_appdirs:
  135. for appdir in root_appdirs[rootdir]:
  136. if appdir in ignored_apps:
  137. print("%s is ignored according to appdirs_ignore settings in configuration" % (appdir))
  138. continue
  139. appdir = appdir.replace("\\", "/") # Change windows \\ path to /
  140. app_buildlogfile = None
  141. app_runlogfile = None
  142. if createlog:
  143. app_buildlogfile = get_logfile(appdir, rootdir, logdir, "build.log")
  144. app_runlogfile = get_logfile(appdir, rootdir, logdir, "run.log")
  145. app_buildcfg = copy.deepcopy(global_build_config)
  146. app_buildtarget = copy.deepcopy(global_target)
  147. app_parallel = copy.deepcopy(global_parallel)
  148. app_cpobjs = copy.deepcopy(global_cpobjs)
  149. app_runcfg = copy.deepcopy(global_run_config)
  150. app_checks = copy.deepcopy(global_checks)
  151. found_cfg = find_local_appconfig(appdir, appconfigs)
  152. if found_cfg:
  153. appcfg = appconfigs[found_cfg]
  154. # merge_global is True if not present
  155. merge_global = appcfg.get("merge_global", True)
  156. # Merge global config when merge_global is True
  157. if merge_global:
  158. app_buildcfg.update(appcfg.get("build_config", dict()))
  159. app_runcfg.update(appcfg.get("run_config", dict()))
  160. app_checks.update(appcfg.get("checks", dict()))
  161. if "build_target" in appcfg:
  162. app_buildtarget = appcfg["build_target"]
  163. if "parallel" in appcfg:
  164. app_parallel = appcfg["parallel"]
  165. if "copy_objects" in appcfg:
  166. app_cpobjs = appcfg["copy_objects"]
  167. else: # if merge_global is false, then use app config only
  168. app_buildcfg = appcfg.get("build_config", dict())
  169. app_buildtarget = appcfg.get("build_target", "clean all")
  170. app_parallel = appcfg.get("parallel", "")
  171. app_runcfg = appcfg.get("run_config", dict())
  172. app_checks = appcfg.get("checks", dict())
  173. app_cpobjs = appcfg.get("copy_objects", None)
  174. appconfig = {"build_config": app_buildcfg, "build_target": app_buildtarget, \
  175. "parallel": app_parallel, "run_config": app_runcfg, "checks": app_checks, \
  176. "logs": {"build": app_buildlogfile, "run": app_runlogfile}}
  177. if app_cpobjs is not None:
  178. appconfig["copy_objects"] = app_cpobjs
  179. apps_config[appdir] = copy.deepcopy(appconfig)
  180. if len(apps_config) == 0:
  181. print("No applications need to be run according to the configuration")
  182. return False, None
  183. print("Run %d applications defined by configuration" % (len(apps_config)))
  184. # Run all the applications
  185. cmdsts, build_status = self.run_apps_with_config(apps_config, show_output, stoponfail)
  186. return cmdsts, build_status
  187. def merge_config(appcfg, hwcfg):
  188. return merge_two_config(appcfg, hwcfg)
  189. def merge_cmd_config(config, args_dict):
  190. return merge_config_with_args(config, args_dict)
  191. def check_expected(build_status, expected, run=False):
  192. if isinstance(build_status, dict) == False:
  193. return False
  194. ret = True
  195. for app in build_status:
  196. app_status = build_status[app]["status"]
  197. build_ret = app_status.get("build", False)
  198. run_ret = app_status.get("run", False)
  199. if isinstance(expected, dict) and app in expected:
  200. expected_build_ret = expected[app].get("build", True)
  201. expected_run_ret = expected[app].get("run", True)
  202. if expected_build_ret != build_ret:
  203. ret = False
  204. if run:
  205. if expected_run_ret != run_ret:
  206. ret = False
  207. else:
  208. if build_ret == False:
  209. ret = False
  210. if run:
  211. if run_ret == False:
  212. ret = False
  213. return ret
  214. def save_results(appcfg, hwcfg, mergedcfg, result, savedir):
  215. if not (isinstance(savedir, str) and os.path.isdir(savedir)):
  216. return
  217. if isinstance(appcfg, dict):
  218. sfn = os.path.join(savedir, "appcfg.json")
  219. save_json(sfn, appcfg)
  220. if isinstance(hwcfg, dict):
  221. sfn = os.path.join(savedir, "hwcfg.json")
  222. save_json(sfn, hwcfg)
  223. if isinstance(mergedcfg, dict):
  224. sfn = os.path.join(savedir, "mergedcfg.json")
  225. save_json(sfn, mergedcfg)
  226. if isinstance(result, dict):
  227. sfn = os.path.join(savedir, "result.json")
  228. save_json(sfn, result)
  229. pass
  230. if __name__ == '__main__':
  231. parser = argparse.ArgumentParser(description="Nuclei SDK Executor Tool")
  232. parser.add_argument('--appcfg', required=True, help="Application JSON Configuration File")
  233. parser.add_argument('--hwcfg', help="Hardware Target JSON Configuration File, if specified, will overwrite configuration defined in appcfg")
  234. parser.add_argument('--logdir', default='logs', help="logs directory, default logs")
  235. parser.add_argument('--serport', help="Serial port for monitor, if not specified, it will use the specified by appcfg and hwcfg")
  236. parser.add_argument('--baudrate', help="Serial port baudrate for monitor, it will use the specified by appcfg and hwcfg")
  237. parser.add_argument('--make_options', help="Extra make options passed to overwrite default build configuration passed via appcfg and hwcfg")
  238. parser.add_argument('--build_target', help="Build target passed to make, to overwrite default build_target defined in configuration file")
  239. parser.add_argument('--run_target', help="Run target which program will run, such as hardware, qemu or xlspike")
  240. parser.add_argument('--parallel', help="parallel value, such as -j4 or -j or -j8, default None")
  241. parser.add_argument('--run', action='store_true', help="If specified, will do run not build process")
  242. parser.add_argument('--verbose', action='store_true', help="If specified, will show detailed build/run messsage")
  243. parser.add_argument('--uniqueid', default="", help="Pass pipeline$CI_PIPELINE_ID, such as pipeline123456")
  244. args = parser.parse_args()
  245. # Load appcfg and hwcfg
  246. ret, appcfg = load_json(args.appcfg)
  247. if ret != JSON_OK:
  248. print("Please provide valid json config file")
  249. sys.exit(1)
  250. # record start time
  251. start_time = time.time()
  252. ret, hwcfg = load_json(args.hwcfg)
  253. # Merge appcfg and hwcfg, hwcfg has higher priority
  254. config = merge_config(appcfg, hwcfg)
  255. # Merge options passed by serport, baudrate, make_options
  256. config = merge_cmd_config(config, vars(args))
  257. # Set global variables from config file
  258. set_global_variables(config)
  259. nsdk_ext = nsdk_executor()
  260. if args.run:
  261. cmdsts, result = nsdk_ext.run_apps(config, args.verbose, args.logdir, False)
  262. else:
  263. cmdsts, result = nsdk_ext.build_apps(config, args.verbose, args.logdir, False)
  264. runtime = round(time.time() - start_time, 2)
  265. print("Applications specified in %s build or run status: %s, time cost %s seconds" % (args.appcfg, cmdsts, runtime))
  266. expected = config.get("expected", None)
  267. ret = check_expected(result, expected, args.run)
  268. print("Applications build or run as expected: %s" % (ret))
  269. save_results(appcfg, hwcfg, config, result, args.logdir)
  270. if result:
  271. csvfile = os.path.join(args.logdir, "result.csv")
  272. save_execute_csv(result, csvfile)
  273. print("Generate report csv file to %s" % (csvfile))
  274. # Exit with ret value
  275. if ret and cmdsts:
  276. sys.exit(0)
  277. else:
  278. sys.exit(1)