1
0

mkdist.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. #
  2. # File : mkdir.py
  3. # This file is part of RT-Thread RTOS
  4. # COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License along
  17. # with this program; if not, write to the Free Software Foundation, Inc.,
  18. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. #
  20. # Change Logs:
  21. # Date Author Notes
  22. # 2017-10-04 Bernard The first version
  23. # 2025-01-07 ZhaoCake components copy and gen doc
  24. # 2025-03-02 ZhaoCake Add MkDist_Strip
  25. import os
  26. import subprocess
  27. import shutil
  28. from shutil import ignore_patterns
  29. from SCons.Script import *
  30. def do_copy_file(src, dst):
  31. # check source file
  32. if not os.path.exists(src):
  33. return
  34. path = os.path.dirname(dst)
  35. # mkdir if path not exist
  36. if not os.path.exists(path):
  37. os.makedirs(path)
  38. shutil.copy2(src, dst)
  39. def do_copy_folder(src_dir, dst_dir, ignore=None):
  40. import shutil
  41. # check source directory
  42. if not os.path.exists(src_dir):
  43. return
  44. try:
  45. if os.path.exists(dst_dir):
  46. shutil.rmtree(dst_dir)
  47. except:
  48. print('Deletes folder: %s failed.' % dst_dir)
  49. return
  50. shutil.copytree(src_dir, dst_dir, ignore = ignore)
  51. source_ext = ['c', 'h', 's', 'S', 'cpp', 'cxx', 'cc', 'xpm']
  52. source_list = []
  53. def walk_children(child):
  54. global source_list
  55. global source_ext
  56. # print child
  57. full_path = child.rfile().abspath
  58. file_type = full_path.rsplit('.',1)[1]
  59. #print file_type
  60. if file_type in source_ext:
  61. if full_path not in source_list:
  62. source_list.append(full_path)
  63. children = child.all_children()
  64. if children != []:
  65. for item in children:
  66. walk_children(item)
  67. def walk_kconfig(RTT_ROOT, source_list):
  68. for parent, dirnames, filenames in os.walk(RTT_ROOT):
  69. if 'bsp' in parent:
  70. continue
  71. if '.git' in parent:
  72. continue
  73. if 'tools' in parent:
  74. continue
  75. if 'Kconfig' in filenames:
  76. pathfile = os.path.join(parent, 'Kconfig')
  77. source_list.append(pathfile)
  78. if 'KConfig' in filenames:
  79. pathfile = os.path.join(parent, 'KConfig')
  80. source_list.append(pathfile)
  81. def bsp_copy_files(bsp_root, dist_dir):
  82. # copy BSP files
  83. do_copy_folder(os.path.join(bsp_root), dist_dir,
  84. ignore_patterns('build', '__pycache__', 'dist', '*.pyc', '*.old', '*.map', 'rtthread.bin', '.sconsign.dblite', '*.elf', '*.axf', 'cconfig.h'))
  85. def bsp_update_sconstruct(dist_dir):
  86. with open(os.path.join(dist_dir, 'SConstruct'), 'r') as f:
  87. data = f.readlines()
  88. with open(os.path.join(dist_dir, 'SConstruct'), 'w') as f:
  89. for line in data:
  90. if line.find('RTT_ROOT') != -1:
  91. if line.find('sys.path') != -1:
  92. f.write('# set RTT_ROOT\n')
  93. f.write('if not os.getenv("RTT_ROOT"): \n RTT_ROOT="rt-thread"\n\n')
  94. f.write(line)
  95. def bsp_update_kconfig_testcases(dist_dir):
  96. # delete testcases in rt-thread/Kconfig
  97. if not os.path.isfile(os.path.join(dist_dir, 'rt-thread/Kconfig')):
  98. return
  99. with open(os.path.join(dist_dir, 'rt-thread/Kconfig'), 'r') as f:
  100. data = f.readlines()
  101. with open(os.path.join(dist_dir, 'rt-thread/Kconfig'), 'w') as f:
  102. for line in data:
  103. if line.find('examples/utest/testcases/Kconfig') == -1:
  104. f.write(line)
  105. def bsp_update_kconfig(dist_dir):
  106. # change RTT_ROOT in Kconfig
  107. if not os.path.isfile(os.path.join(dist_dir, 'Kconfig')):
  108. return
  109. with open(os.path.join(dist_dir, 'Kconfig'), 'r') as f:
  110. data = f.readlines()
  111. with open(os.path.join(dist_dir, 'Kconfig'), 'w') as f:
  112. for line in data:
  113. if line.find('RTT_DIR') != -1 and line.find(':=') != -1:
  114. line = 'RTT_DIR := rt-thread\n'
  115. f.write(line)
  116. def bsp_update_kconfig_library(dist_dir):
  117. # change RTT_ROOT in Kconfig
  118. if not os.path.isfile(os.path.join(dist_dir, 'Kconfig')):
  119. return
  120. with open(os.path.join(dist_dir, 'Kconfig'), 'r') as f:
  121. data = f.readlines()
  122. with open(os.path.join(dist_dir, 'Kconfig'), 'w') as f:
  123. for line in data:
  124. if line.find('source') != -1 and line.find('../libraries') != -1:
  125. line = line.replace('../libraries', 'libraries')
  126. f.write(line)
  127. # change board/kconfig path
  128. if not os.path.isfile(os.path.join(dist_dir, 'board/Kconfig')):
  129. return
  130. with open(os.path.join(dist_dir, 'board/Kconfig'), 'r') as f:
  131. data = f.readlines()
  132. with open(os.path.join(dist_dir, 'board/Kconfig'), 'w') as f:
  133. for line in data:
  134. if line.find('source') != -1 and line.find('../libraries') != -1:
  135. line = line.replace('../libraries', 'libraries')
  136. f.write(line)
  137. def zip_dist(dist_dir, dist_name):
  138. import zipfile
  139. zip_filename = os.path.join(dist_dir)
  140. zip = zipfile.ZipFile(zip_filename + '.zip', 'w')
  141. pre_len = len(os.path.dirname(dist_dir))
  142. for parent, dirnames, filenames in os.walk(dist_dir):
  143. for filename in filenames:
  144. pathfile = os.path.join(parent, filename)
  145. arcname = pathfile[pre_len:].strip(os.path.sep)
  146. zip.write(pathfile, arcname)
  147. zip.close()
  148. def MkDist(program, BSP_ROOT, RTT_ROOT, Env, project_name, project_path):
  149. print('make distribution....')
  150. if project_path == None:
  151. dist_dir = os.path.join(BSP_ROOT, 'dist', project_name)
  152. else:
  153. dist_dir = project_path
  154. rtt_dir_path = os.path.join(dist_dir, 'rt-thread')
  155. # copy BSP files
  156. print('=> %s' % os.path.basename(BSP_ROOT))
  157. bsp_copy_files(BSP_ROOT, dist_dir)
  158. # do bsp special dist handle
  159. if 'dist_handle' in Env:
  160. print("=> start dist handle")
  161. dist_handle = Env['dist_handle']
  162. dist_handle(BSP_ROOT, dist_dir)
  163. # copy tools directory
  164. print('=> components')
  165. do_copy_folder(os.path.join(RTT_ROOT, 'components'), os.path.join(rtt_dir_path, 'components'))
  166. # skip documentation directory
  167. # skip examples
  168. # copy include directory
  169. print('=> include')
  170. do_copy_folder(os.path.join(RTT_ROOT, 'include'), os.path.join(rtt_dir_path, 'include'))
  171. # copy all libcpu/ARCH directory
  172. print('=> libcpu')
  173. import rtconfig
  174. do_copy_folder(os.path.join(RTT_ROOT, 'libcpu', rtconfig.ARCH), os.path.join(rtt_dir_path, 'libcpu', rtconfig.ARCH))
  175. do_copy_file(os.path.join(RTT_ROOT, 'libcpu', 'Kconfig'), os.path.join(rtt_dir_path, 'libcpu', 'Kconfig'))
  176. do_copy_file(os.path.join(RTT_ROOT, 'libcpu', 'SConscript'), os.path.join(rtt_dir_path, 'libcpu', 'SConscript'))
  177. # copy src directory
  178. print('=> src')
  179. do_copy_folder(os.path.join(RTT_ROOT, 'src'), os.path.join(rtt_dir_path, 'src'))
  180. # copy tools directory
  181. print('=> tools')
  182. do_copy_folder(os.path.join(RTT_ROOT, 'tools'), os.path.join(rtt_dir_path, 'tools'), ignore_patterns('*.pyc'))
  183. do_copy_file(os.path.join(RTT_ROOT, 'Kconfig'), os.path.join(rtt_dir_path, 'Kconfig'))
  184. do_copy_file(os.path.join(RTT_ROOT, 'AUTHORS'), os.path.join(rtt_dir_path, 'AUTHORS'))
  185. do_copy_file(os.path.join(RTT_ROOT, 'COPYING'), os.path.join(rtt_dir_path, 'COPYING'))
  186. do_copy_file(os.path.join(RTT_ROOT, 'README.md'), os.path.join(rtt_dir_path, 'README.md'))
  187. do_copy_file(os.path.join(RTT_ROOT, 'README_zh.md'), os.path.join(rtt_dir_path, 'README_zh.md'))
  188. print('Update configuration files...')
  189. # change RTT_ROOT in SConstruct
  190. bsp_update_sconstruct(dist_dir)
  191. # change RTT_ROOT in Kconfig
  192. bsp_update_kconfig(dist_dir)
  193. bsp_update_kconfig_library(dist_dir)
  194. # delete testcases in Kconfig
  195. bsp_update_kconfig_testcases(dist_dir)
  196. target_project_type = GetOption('target')
  197. if target_project_type:
  198. child = subprocess.Popen('scons --target={} --project-name="{}"'.format(target_project_type, project_name), cwd=dist_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
  199. stdout, stderr = child.communicate()
  200. if child.returncode == 0:
  201. print(stdout)
  202. else:
  203. print(stderr)
  204. else:
  205. print('suggest to use command scons --dist [--target=xxx] [--project-name="xxx"] [--project-path="xxx"]')
  206. # make zip package
  207. if project_path == None:
  208. zip_dist(dist_dir, project_name)
  209. print('dist project successfully!')
  210. def MkDist_Strip(program, BSP_ROOT, RTT_ROOT, env, project_name, project_path=None):
  211. """Create a minimal distribution based on compile_commands.json but keeping all build system files.
  212. First copies everything like MkDist, then only removes unused source files while keeping all headers.
  213. """
  214. print('Making minimal distribution for project...')
  215. if project_path == None:
  216. dist_dir = os.path.join(BSP_ROOT, 'dist', project_name)
  217. else:
  218. dist_dir = project_path
  219. # First do a full distribution copy
  220. MkDist(program, BSP_ROOT, RTT_ROOT, env, project_name, project_path)
  221. print('\n=> Starting source files cleanup...')
  222. # Get the minimal required source paths
  223. import compile_commands
  224. used_paths = compile_commands.get_minimal_dist_paths(
  225. os.path.join(BSP_ROOT, 'compile_commands.json'),
  226. RTT_ROOT
  227. )
  228. # Clean up RT-Thread directory except tools and build files
  229. rt_thread_dir = os.path.join(dist_dir, 'rt-thread')
  230. source_extensions = ('.c', '.cpp', '.cxx', '.cc', '.s', '.S')
  231. removed_files = []
  232. removed_dirs = []
  233. for root, dirs, files in os.walk(rt_thread_dir, topdown=False):
  234. rel_path = os.path.relpath(root, rt_thread_dir)
  235. if rel_path.startswith('tools') or rel_path.startswith('include'):
  236. continue
  237. keep_files = {
  238. 'SConscript',
  239. 'Kconfig',
  240. 'Sconscript',
  241. '.config',
  242. 'rtconfig.h'
  243. }
  244. for f in files:
  245. if f in keep_files:
  246. continue
  247. if not f.endswith(source_extensions):
  248. continue
  249. file_path = os.path.join(root, f)
  250. rel_file_path = os.path.relpath(file_path, rt_thread_dir)
  251. dir_name = os.path.dirname(rel_file_path)
  252. if dir_name not in used_paths and rel_file_path not in used_paths:
  253. os.remove(file_path)
  254. removed_files.append(rel_file_path)
  255. # Remove empty directories
  256. try:
  257. if not os.listdir(root):
  258. os.rmdir(root)
  259. removed_dirs.append(rel_path)
  260. except:
  261. pass
  262. # Output summary
  263. if removed_files:
  264. print(f"Removed {len(removed_files)} unused source files")
  265. log_file = os.path.join(dist_dir, 'cleanup.log')
  266. with open(log_file, 'w') as f:
  267. f.write("Removed source files:\n")
  268. f.write('\n'.join(removed_files))
  269. if removed_dirs:
  270. f.write("\n\nRemoved empty directories:\n")
  271. f.write('\n'.join(removed_dirs))
  272. print(f"Details have been written to {log_file}")
  273. else:
  274. print("No unused source files found")
  275. # Make zip package like MkDist
  276. if project_path is None:
  277. zip_dist(dist_dir, project_name)
  278. print(f"Distribution package created: {dist_dir}.zip")
  279. print('=> Distribution stripped successfully')