nsdk_execute.py 14 KB

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