nsdk_execute.py 15 KB

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