nsdk_execute.py 14 KB

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