nsdk_execute.py 14 KB

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