air302.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #!/usr/bin/python3
  2. # -*- coding: UTF-8 -*-
  3. import os.path
  4. import shutil
  5. import zipfile
  6. import time
  7. import subprocess
  8. import sys
  9. import json
  10. #------------------------------------------------------------------
  11. # 读取配置信息
  12. import configparser
  13. config = configparser.ConfigParser()
  14. config['air302'] = {
  15. # ============================================================
  16. #
  17. # 配置信息是读取local.ini为主的,以下的只是默认配置.
  18. # 请修改local.ini, 一般不需要修改本脚本里的配置信息.
  19. #
  20. # ============================================================
  21. # 不要修改PLAT_ROOT!不要修改PLAT_ROOT!不要修改PLAT_ROOT!
  22. # PLAT_ROOT仅供SDK源码开发者使用!!!!
  23. # 不要把PLAT_ROOT指向任何存在的路径!!!!!
  24. "PLAT_ROOT" : "D:\\github\\air302\\sdk\\PLAT\\",
  25. # ============================================================
  26. "FTC_PATH" : ".\\FlashToolCLI\\",
  27. "EC_PATH" : ".\\Air302_dev.ec",
  28. "USER_PATH": ".\\user\\",
  29. "LIB_PATH" : ".\\lib\\",
  30. "DEMO_PATH": ".\\demo\\",
  31. "TOOLS_PATH": ".\\tools\\",
  32. "MAIN_LUA_DEBUG" : "false",
  33. "LUA_DEBUG" : "false",
  34. "COM_PORT" : "COM59" # 请修改local.ini文件
  35. }
  36. if os.path.exists("local.ini") :
  37. config.read("local.ini")
  38. if os.path.exists(config["air302"]["PLAT_ROOT"]):
  39. PLAT_ROOT = os.path.abspath(config["air302"]["PLAT_ROOT"]) + os.sep # 源码地址
  40. else:
  41. PLAT_ROOT = config["air302"]["PLAT_ROOT"]
  42. FTC_PATH = os.path.abspath(config["air302"]["FTC_PATH"]) + os.sep # FlashToolCLI刷机工具的目录
  43. EC_PATH = os.path.abspath(config["air302"]["EC_PATH"]) # EC后缀的固件路径
  44. USER_PATH = os.path.abspath(config["air302"]["USER_PATH"]) + os.sep # 用户脚本所在的目录
  45. LIB_PATH = os.path.abspath(config["air302"]["LIB_PATH"]) + os.sep # 用户脚本所在的目录
  46. DEMO_PATH = os.path.abspath(config["air302"]["DEMO_PATH"]) + os.sep # 用户脚本所在的目录
  47. MAIN_LUA_DEBUG = config["air302"]["MAIN_LUA_DEBUG"] == "true"
  48. LUA_DEBUG = config["air302"]["LUA_DEBUG"] == "true"
  49. COM_PORT = config["air302"]["COM_PORT"]
  50. TOOLS_PATH = os.path.abspath(config["air302"]["TOOLS_PATH"]) + os.sep
  51. # TODO 从环境变量获取上述参数
  52. '''
  53. 获取git库的当前版本号,为一个hash值
  54. '''
  55. def get_git_revision_short_hash():
  56. try :
  57. if os.path.exists(PLAT_ROOT):
  58. return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=PLAT_ROOT).strip()
  59. else:
  60. return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).strip()
  61. except:
  62. return ""
  63. '''
  64. 打印帮助信息
  65. '''
  66. def usage():
  67. print('''
  68. python air302.py [action]
  69. lfs - 编译文件系统
  70. dlrom - 下载底层固件
  71. dlfs - 下载lua脚本(即整个文件系统)
  72. dlfull- 下载底层和lua脚本
  73. pkg - 生成发布用的压缩包
  74. build - 构建源码(仅内部使用)
  75. 用例1, 生成文件系统并下载到开发板
  76. python air302.py lfs dlfs
  77. 用例2, 生成文件系统,并下载固件和文件系统到开发板
  78. python air302.py lfs dlfull
  79. 用例3, 仅下载底层固件
  80. python air302.py dlrom
  81. 用例四, 编译,打包,构建文件系统,全量下载
  82. python air302.py build lfs pkg dlfull
  83. ''')
  84. '''
  85. FlashToolCLI所需要的配置信息
  86. '''
  87. FTC_CNF_TMPL = '''
  88. [config]
  89. line_0_com = ${COM}
  90. agbaud = 921600
  91. ;bootloader.bin file infomation
  92. [bootloader]
  93. blpath = .\\image\\bootloader.bin
  94. blloadskip = 0
  95. ;system.bin file infomation
  96. [system]
  97. syspath = .\\system.bin
  98. sysloadskip = 0
  99. ;control such as reset before download
  100. [control]
  101. reset = 0
  102. [flexfile0]
  103. filepath = .\\rfCaliTb\\MergeRfTable.bin
  104. burnaddr = 0x3A4000
  105. [flexfile1]
  106. filepath = .\\rfCaliTb\\MergeRfTable.bin
  107. burnaddr = 0x16000
  108. [flexfile2]
  109. filepath = .\\disk.fs
  110. burnaddr = 0x350000
  111. '''.replace("${COM}", COM_PORT)
  112. '''
  113. 执行打包程序,内部使用
  114. '''
  115. def _pkg():
  116. # TODO 扩展为用户可用的打包ec固件的工具
  117. if os.path.exists("tmp"):
  118. shutil.rmtree("tmp")
  119. _tag = time.strftime("%Y%m%d%H%M%S", time.localtime())
  120. _git_sha1 = get_git_revision_short_hash()
  121. if _git_sha1 and _git_sha1 != "" :
  122. _tag = _tag + "-" + _git_sha1.decode()
  123. os.mkdir("tmp")
  124. os.mkdir("tmp/ec")
  125. # 拷贝固件文件
  126. if os.path.exists(PLAT_ROOT) :
  127. shutil.copy(PLAT_ROOT + "out/ec616_0h00/air302/air302.bin", "tmp/ec/luatos.bin")
  128. shutil.copy(PLAT_ROOT + "out/ec616_0h00/air302/comdb.txt", "tmp/ec/comdb.txt")
  129. shutil.copy(FTC_PATH + "image/bootloader.bin", "tmp/ec/bootloader.bin")
  130. with open("tmp/ec/bootloader_head.bin", "w") as f:
  131. f.write("")
  132. #shutil.copy(FTC_PATH + "image/bootloader_head.bin", "tmp/ec/bootloader_head.bin")
  133. elif os.path.exists(EC_PATH) and EC_PATH.endswith(".ec") :
  134. with zipfile.ZipFile(EC_PATH) as zip :
  135. zip.extractall(path="tmp/ec/")
  136. # 拷贝库文件和demo
  137. shutil.copytree(LIB_PATH, "tmp/lib")
  138. shutil.copytree(DEMO_PATH, "tmp/demo")
  139. shutil.copytree(TOOLS_PATH, "tmp/tools")
  140. #拷贝自身
  141. shutil.copy(sys.argv[0], "tmp/air302.py")
  142. shutil.copy("air302.bat", "tmp/air302.bat")
  143. # 写入默认配置文件
  144. with open("tmp/local.ini", "w") as f:
  145. f.write('''
  146. [air302]
  147. FTC_PATH = FlashToolCLI\\
  148. EC_PATH = ${EC}
  149. USER_PATH = user\\
  150. LIB_PATH = lib\\
  151. DEMO_PATH = demo\\
  152. TOOLS_PATH = tools\\
  153. MAIN_LUA_DEBUG = false
  154. LUA_DEBUG = false
  155. COM_PORT = COM56
  156. '''.replace("${EC}", "Air302_V0003_"+_tag+".ec"))
  157. if os.path.exists("userdoc") :
  158. shutil.copytree("userdoc", "tmp/userdoc")
  159. if os.path.exists("../../docs/api/lua"):
  160. shutil.copytree("../../docs/api/lua", "tmp/userdoc/api")
  161. if os.path.exists(USER_PATH):
  162. shutil.copytree(USER_PATH, "tmp/user")
  163. with open("tmp/文档在userdoc目录.txt", "w") as f:
  164. f.write("QQ群: 1061642968")
  165. with zipfile.ZipFile("tmp/Air302_V0003_"+_tag+".ec", mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zip :
  166. zip.write("tmp/ec/luatos.bin", "luatos.bin") # 底层固件
  167. zip.write("tmp/ec/comdb.txt", "comdb.txt") # uart0输出的unilog所需要的数据库文件,备用
  168. zip.write("tmp/ec/bootloader.bin", "bootloader.bin") # bootloader,备用
  169. #zip.write("tmp/ec/bootloader_head.bin", "bootloader_head.bin") # bootloader_header,备用
  170. zip.write(FTC_PATH + "disk.fs", "disk.bin") # 默认磁盘镜像
  171. shutil.rmtree("tmp/ec/")
  172. if os.path.exists(FTC_PATH):
  173. shutil.copytree(FTC_PATH, "tmp/FlashToolCLI")
  174. pkg_name = "Air302_V0003_"+_tag + ".zip"
  175. shutil.make_archive("Air302_V0003_"+_tag, 'zip', "tmp")
  176. ## 拷贝一份固定路径的
  177. shutil.copy("tmp/Air302_V0003_"+_tag+".ec", "tmp/Air302_dev.ec")
  178. print("ALL DONE===================================================")
  179. print("Package Name", pkg_name)
  180. '''
  181. 下载底层或脚本
  182. '''
  183. def _dl(tp, _path=None):
  184. with open(FTC_PATH + "config.ini", "w") as f :
  185. f.write(FTC_CNF_TMPL)
  186. cmd = [FTC_PATH + "FlashToolCLI.exe", "-p", COM_PORT, "burnbatch", "--imglist"]
  187. if tp == "rom" or tp == "full":
  188. if os.path.exists(PLAT_ROOT + "out/ec616_0h00/air302/air302.bin") :
  189. print("P1 COPY beta version from PLAT_ROOT dir", PLAT_ROOT + "out/ec616_0h00/air302/air302.bin")
  190. shutil.copy(PLAT_ROOT + "out/ec616_0h00/air302/air302.bin", FTC_PATH + "system.bin")
  191. elif EC_PATH.endswith(".ec") :
  192. print("P1. Unzip luatos.bin from " + EC_PATH)
  193. import zipfile
  194. with zipfile.ZipFile(EC_PATH) as zip :
  195. with open(FTC_PATH + "system.bin", "wb") as f:
  196. f.write(zip.read("luatos.bin"))
  197. elif EC_PATH.endswith(".bin"):
  198. print("P1. Using bin file from " + EC_PATH)
  199. shutil.copy(EC_PATH, FTC_PATH + "system.bin")
  200. else:
  201. print("Bad EC_PATH : " + EC_PATH)
  202. return
  203. cmd += ["system"]
  204. cmd += ["bootloader"]
  205. if tp == "fs" or tp == "full" :
  206. cmd += ["flexfile2"]
  207. print("P2. Call", " ".join(cmd))
  208. subprocess.check_call(cmd, cwd=FTC_PATH)
  209. print("P3. Done")
  210. '''
  211. 生成文件系统镜像
  212. '''
  213. def _lfs(_path=None):
  214. print("============================================================")
  215. print(" Build LittltFS disk image")
  216. print("============================================================")
  217. _disk = FTC_PATH + "disk"
  218. if os.path.exists(_disk) :
  219. shutil.rmtree(_disk)
  220. os.mkdir(_disk)
  221. if not _path:
  222. _path = USER_PATH
  223. print("P1. User Lua Dir == ", os.path.abspath(_path))
  224. # 收集需要处理的文件列表
  225. _paths = []
  226. # 首先,遍历lib目录
  227. if os.path.exists(LIB_PATH) :
  228. print("P1. Lib Lua Dir == ", os.path.abspath(LIB_PATH))
  229. for name in os.listdir(LIB_PATH) :
  230. _paths.append(LIB_PATH + name)
  231. # 然后遍历user目录
  232. for name in os.listdir(_path) :
  233. _paths.append(_path + name)
  234. TAG_PROJECT = ""
  235. TAG_VERSION = ""
  236. for name in _paths :
  237. # 如果是lua文件, 编译之
  238. if name.endswith(".lua") :
  239. cmd = [TOOLS_PATH + "luac_536_32bits.exe"]
  240. if name.endswith("main.lua") :
  241. if not MAIN_LUA_DEBUG :
  242. cmd += ["-s"]
  243. with open(name, "rb") as f :
  244. for line in f.readlines() :
  245. if line :
  246. line = line.strip().decode()
  247. if line.startswith("PROJECT =") :
  248. TAG_PROJECT = line[line.index("\"") + 1:][:-1]
  249. elif line.startswith("VERSION =") :
  250. TAG_VERSION = line[line.index("\"") + 1:][:-1]
  251. elif not LUA_DEBUG :
  252. cmd += ["-s"]
  253. else:
  254. print("LUA_DEBUG", LUA_DEBUG, "False" == LUA_DEBUG)
  255. cmd += ["-o", FTC_PATH + "disk/" + os.path.basename(name) + "c", os.path.basename(name)]
  256. print("P2. CALL Luac >> ", os.path.abspath(name))
  257. subprocess.check_call(cmd, cwd=os.path.dirname(name))
  258. # 其他文件直接拷贝
  259. else:
  260. print("P2. COPY", name, FTC_PATH + "disk/" + os.path.basename(name))
  261. shutil.copy(name, FTC_PATH + "disk/" + os.path.basename(name))
  262. if TAG_PROJECT == "" or TAG_VERSION == "" :
  263. print("!!!!!!!miss PROJECT or/and VERSION!!!!!!!!!!")
  264. for root, dirs, files in os.walk(FTC_PATH + "disk", topdown=False):
  265. import struct
  266. print("P3. Make flashx.bin", FTC_PATH + "disk/flashx.bin")
  267. with open(FTC_PATH + "disk/flashx.bin", "wb") as f :
  268. # 写入文件头
  269. f.write(struct.pack("<HHI", 0x1234, 0x00, 0x00))
  270. for name in files:
  271. # 写入文件名
  272. f.write(struct.pack("<HHI", 0x0101, 0x00, len(name)))
  273. f.write(name.encode())
  274. # 写入文件内容
  275. _path = os.path.join(root, name)
  276. _size = os.path.getsize(_path)
  277. f.write(struct.pack("<HHI", 0x0202, 0x00, _size))
  278. with open(_path, "rb") as f2 :
  279. shutil.copyfileobj(f2, f, _size)
  280. if TAG_PROJECT != "" and TAG_VERSION != "":
  281. # otademo_1.2.7_LuatOS_V0003_ec616
  282. TAG_NAME = "%s_%s_LuatOS_V0003_ec616.bin" % (TAG_PROJECT, TAG_VERSION)
  283. print("P4. OTA Update bin --> " + TAG_NAME)
  284. shutil.copy(FTC_PATH + "disk/flashx.bin", TAG_NAME)
  285. print("P5. CALL mklfs to make disk.fs")
  286. subprocess.check_call([TOOLS_PATH + "mklfs.exe"], cwd=FTC_PATH)
  287. print("6. LFS DONE")
  288. def main():
  289. argc = 1
  290. while len(sys.argv) > argc :
  291. if sys.argv[argc] == "build" :
  292. print("Action Build ----------------------------------")
  293. subprocess.check_call([PLAT_ROOT + "KeilBuild.bat"], cwd=PLAT_ROOT)
  294. elif sys.argv[argc] == "lfs" :
  295. print("Action mklfs ----------------------------------")
  296. _lfs()
  297. elif sys.argv[argc] == "pkg" :
  298. print("Action pkg ------------------------------------")
  299. _pkg()
  300. elif sys.argv[argc] == "dlrom": #下载底层
  301. print("Action download ROM ---------------------------")
  302. if len(sys.argv) > argc + 1 and sys.argv[argc+1].startsWith("-path="):
  303. _dl("rom", sys.argv[argc+1][6:])
  304. argc += 1
  305. else:
  306. _dl("rom")
  307. elif sys.argv[argc] == "dlfs":
  308. print("Action download FS ---------------------------")
  309. if len(sys.argv) > argc + 1 and sys.argv[argc+1].startsWith("-path="):
  310. _dl("fs", sys.argv[argc+1][6:])
  311. argc += 1
  312. else:
  313. _dl("fs")
  314. elif sys.argv[argc] == "dlfull":
  315. if len(sys.argv) > argc + 1 and sys.argv[argc+1].startsWith("-path="):
  316. _dl("full", sys.argv[argc+1][6:])
  317. argc += 1
  318. else:
  319. _dl("full")
  320. elif sys.argv[argc] == "clean":
  321. if os.path.exists("tmp"):
  322. shutil.rmtree("tmp")
  323. if os.path.exists(PLAT_ROOT) :
  324. subprocess.call([PLAT_ROOT + "KeilBuild.bat", "clall"], cwd=PLAT_ROOT)
  325. else:
  326. usage()
  327. return
  328. argc += 1
  329. print("============================================================================")
  330. print("every done, bye")
  331. if len(sys.argv) == 1 :
  332. usage()
  333. else :
  334. main()