create-dist.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. #!/usr/bin/env python
  2. import argparse
  3. import glob
  4. import os
  5. import re
  6. import shutil
  7. import subprocess
  8. import sys
  9. import stat
  10. if sys.platform == "win32":
  11. import _winreg
  12. from lib.config import BASE_URL, PLATFORM, enable_verbose_mode, \
  13. get_target_arch, get_zip_name, build_env
  14. from lib.util import scoped_cwd, rm_rf, get_electron_version, make_zip, \
  15. execute, electron_gyp
  16. ELECTRON_VERSION = get_electron_version()
  17. SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
  18. DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
  19. OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'R')
  20. CHROMIUM_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'download',
  21. 'libchromiumcontent', 'static_library')
  22. NATIVE_MKSNAPSHOT_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'native_mksnapshot')
  23. PROJECT_NAME = electron_gyp()['project_name%']
  24. PRODUCT_NAME = electron_gyp()['product_name%']
  25. TARGET_BINARIES = {
  26. 'darwin': [
  27. ],
  28. 'win32': [
  29. '{0}.exe'.format(PROJECT_NAME), # 'electron.exe'
  30. 'content_shell.pak',
  31. 'pdf_viewer_resources.pak',
  32. 'd3dcompiler_47.dll',
  33. 'icudtl.dat',
  34. 'libEGL.dll',
  35. 'libGLESv2.dll',
  36. 'ffmpeg.dll',
  37. 'node.dll',
  38. 'blink_image_resources_200_percent.pak',
  39. 'content_resources_200_percent.pak',
  40. 'ui_resources_200_percent.pak',
  41. 'views_resources_200_percent.pak',
  42. 'natives_blob.bin',
  43. 'snapshot_blob.bin',
  44. ],
  45. 'linux': [
  46. PROJECT_NAME, # 'electron'
  47. 'content_shell.pak',
  48. 'pdf_viewer_resources.pak',
  49. 'icudtl.dat',
  50. 'libffmpeg.so',
  51. 'libnode.so',
  52. 'blink_image_resources_200_percent.pak',
  53. 'content_resources_200_percent.pak',
  54. 'ui_resources_200_percent.pak',
  55. 'views_resources_200_percent.pak',
  56. 'natives_blob.bin',
  57. 'snapshot_blob.bin',
  58. ],
  59. }
  60. TARGET_BINARIES_EXT = []
  61. TARGET_DIRECTORIES = {
  62. 'darwin': [
  63. '{0}.app'.format(PRODUCT_NAME),
  64. ],
  65. 'win32': [
  66. 'resources',
  67. 'locales',
  68. ],
  69. 'linux': [
  70. 'resources',
  71. 'locales',
  72. ],
  73. }
  74. def main():
  75. args = parse_args()
  76. if args.chromium_dir:
  77. globals().update(CHROMIUM_DIR=args.chromium_dir)
  78. if args.verbose:
  79. enable_verbose_mode()
  80. rm_rf(DIST_DIR)
  81. os.makedirs(DIST_DIR)
  82. force_build()
  83. create_symbols()
  84. copy_binaries()
  85. copy_chrome_binary('chromedriver')
  86. copy_chrome_binary('mksnapshot')
  87. copy_license()
  88. if PLATFORM == 'win32':
  89. copy_vcruntime_binaries()
  90. copy_ucrt_binaries()
  91. if PLATFORM != 'win32' and not args.no_api_docs:
  92. create_api_json_schema()
  93. create_typescript_definitions()
  94. if PLATFORM == 'linux':
  95. strip_binaries()
  96. create_version()
  97. create_dist_zip()
  98. create_chrome_binary_zip('chromedriver', ELECTRON_VERSION)
  99. create_chrome_binary_zip('mksnapshot', ELECTRON_VERSION)
  100. create_ffmpeg_zip()
  101. create_symbols_zip()
  102. def force_build():
  103. build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
  104. execute([sys.executable, build, '-c', 'Release'])
  105. def copy_binaries():
  106. for binary in TARGET_BINARIES[PLATFORM]:
  107. shutil.copy2(os.path.join(OUT_DIR, binary), DIST_DIR)
  108. for directory in TARGET_DIRECTORIES[PLATFORM]:
  109. shutil.copytree(os.path.join(OUT_DIR, directory),
  110. os.path.join(DIST_DIR, directory),
  111. symlinks=True)
  112. def copy_chrome_binary(binary):
  113. if PLATFORM == 'win32':
  114. binary += '.exe'
  115. src = os.path.join(CHROMIUM_DIR, binary)
  116. dest = os.path.join(DIST_DIR, binary)
  117. # Copy file and keep the executable bit.
  118. shutil.copyfile(src, dest)
  119. os.chmod(dest, os.stat(dest).st_mode | stat.S_IEXEC)
  120. def copy_vcruntime_binaries():
  121. with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
  122. r"SOFTWARE\Microsoft\VisualStudio\14.0\Setup\VC", 0,
  123. _winreg.KEY_READ | _winreg.KEY_WOW64_32KEY) as key:
  124. crt_dir = _winreg.QueryValueEx(key, "ProductDir")[0]
  125. arch = get_target_arch()
  126. if arch == "ia32":
  127. arch = "x86"
  128. crt_dir += r"redist\{0}\Microsoft.VC140.CRT\\".format(arch)
  129. dlls = ["msvcp140.dll", "vcruntime140.dll"]
  130. # Note: copyfile is used to remove the read-only flag
  131. for dll in dlls:
  132. shutil.copyfile(crt_dir + dll, os.path.join(DIST_DIR, dll))
  133. TARGET_BINARIES_EXT.append(dll)
  134. def copy_ucrt_binaries():
  135. with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
  136. r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"
  137. ) as key:
  138. ucrt_dir = _winreg.QueryValueEx(key, "KitsRoot10")[0]
  139. arch = get_target_arch()
  140. if arch == "ia32":
  141. arch = "x86"
  142. ucrt_dir += r"Redist\ucrt\DLLs\{0}".format(arch)
  143. dlls = glob.glob(os.path.join(ucrt_dir, '*.dll'))
  144. if len(dlls) == 0:
  145. raise Exception('UCRT files not found')
  146. for dll in dlls:
  147. shutil.copy2(dll, DIST_DIR)
  148. TARGET_BINARIES_EXT.append(os.path.basename(dll))
  149. def copy_license():
  150. shutil.copy2(os.path.join(CHROMIUM_DIR, '..', 'LICENSES.chromium.html'),
  151. DIST_DIR)
  152. shutil.copy2(os.path.join(SOURCE_ROOT, 'LICENSE'), DIST_DIR)
  153. def create_api_json_schema():
  154. node_bin_dir = os.path.join(SOURCE_ROOT, 'node_modules', '.bin')
  155. env = os.environ.copy()
  156. env['PATH'] = os.path.pathsep.join([node_bin_dir, env['PATH']])
  157. outfile = os.path.relpath(os.path.join(DIST_DIR, 'electron-api.json'))
  158. execute(['electron-docs-linter', 'docs', '--outfile={0}'.format(outfile),
  159. '--version={}'.format(ELECTRON_VERSION.replace('v', ''))],
  160. env=env)
  161. def create_typescript_definitions():
  162. node_bin_dir = os.path.join(SOURCE_ROOT, 'node_modules', '.bin')
  163. env = os.environ.copy()
  164. env['PATH'] = os.path.pathsep.join([node_bin_dir, env['PATH']])
  165. infile = os.path.relpath(os.path.join(DIST_DIR, 'electron-api.json'))
  166. outfile = os.path.relpath(os.path.join(DIST_DIR, 'electron.d.ts'))
  167. execute(['electron-typescript-definitions', '--in={0}'.format(infile),
  168. '--out={0}'.format(outfile)], env=env)
  169. def strip_binaries():
  170. for binary in TARGET_BINARIES[PLATFORM]:
  171. if binary.endswith('.so') or '.' not in binary:
  172. strip_binary(os.path.join(DIST_DIR, binary))
  173. def strip_binary(binary_path):
  174. if get_target_arch() == 'arm':
  175. strip = 'arm-linux-gnueabihf-strip'
  176. elif get_target_arch() == 'arm64':
  177. strip = 'aarch64-linux-gnu-strip'
  178. elif get_target_arch() == 'mips64el':
  179. strip = 'mips64el-redhat-linux-strip'
  180. else:
  181. strip = 'strip'
  182. execute([strip, binary_path], env=build_env())
  183. def create_version():
  184. version_path = os.path.join(SOURCE_ROOT, 'dist', 'version')
  185. with open(version_path, 'w') as version_file:
  186. version_file.write(ELECTRON_VERSION)
  187. def create_symbols():
  188. if get_target_arch() == 'mips64el':
  189. return
  190. destination = os.path.join(DIST_DIR, '{0}.breakpad.syms'.format(PROJECT_NAME))
  191. dump_symbols = os.path.join(SOURCE_ROOT, 'script', 'dump-symbols.py')
  192. execute([sys.executable, dump_symbols, destination])
  193. if PLATFORM == 'darwin':
  194. dsyms = glob.glob(os.path.join(OUT_DIR, '*.dSYM'))
  195. for dsym in dsyms:
  196. shutil.copytree(dsym, os.path.join(DIST_DIR, os.path.basename(dsym)))
  197. elif PLATFORM == 'win32':
  198. pdbs = glob.glob(os.path.join(OUT_DIR, '*.pdb'))
  199. for pdb in pdbs:
  200. shutil.copy2(pdb, DIST_DIR)
  201. def create_dist_zip():
  202. dist_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION)
  203. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  204. with scoped_cwd(DIST_DIR):
  205. files = TARGET_BINARIES[PLATFORM] + TARGET_BINARIES_EXT + ['LICENSE',
  206. 'LICENSES.chromium.html', 'version']
  207. dirs = TARGET_DIRECTORIES[PLATFORM]
  208. make_zip(zip_file, files, dirs)
  209. def create_chrome_binary_zip(binary, version):
  210. file_suffix = ''
  211. create_native_mksnapshot = False
  212. if binary == 'mksnapshot':
  213. arch = get_target_arch()
  214. if arch.startswith('arm'):
  215. # if the arch is arm/arm64 the mksnapshot executable is an x64 binary,
  216. # so name it as such.
  217. file_suffix = 'x64'
  218. create_native_mksnapshot = True
  219. dist_name = get_zip_name(binary, version, file_suffix)
  220. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  221. files = ['LICENSE', 'LICENSES.chromium.html']
  222. if PLATFORM == 'win32':
  223. files += [binary + '.exe']
  224. else:
  225. files += [binary]
  226. with scoped_cwd(DIST_DIR):
  227. make_zip(zip_file, files, [])
  228. if create_native_mksnapshot == True:
  229. # Create a zip with the native version of the mksnapshot binary.
  230. src = os.path.join(NATIVE_MKSNAPSHOT_DIR, binary)
  231. dest = os.path.join(DIST_DIR, binary)
  232. # Copy file and keep the executable bit.
  233. shutil.copyfile(src, dest)
  234. os.chmod(dest, os.stat(dest).st_mode | stat.S_IEXEC)
  235. dist_name = get_zip_name(binary, version)
  236. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  237. with scoped_cwd(DIST_DIR):
  238. make_zip(zip_file, files, [])
  239. def create_ffmpeg_zip():
  240. dist_name = get_zip_name('ffmpeg', ELECTRON_VERSION)
  241. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  242. if PLATFORM == 'darwin':
  243. ffmpeg_name = 'libffmpeg.dylib'
  244. elif PLATFORM == 'linux':
  245. ffmpeg_name = 'libffmpeg.so'
  246. elif PLATFORM == 'win32':
  247. ffmpeg_name = 'ffmpeg.dll'
  248. shutil.copy2(os.path.join(CHROMIUM_DIR, '..', 'ffmpeg', ffmpeg_name),
  249. DIST_DIR)
  250. if PLATFORM == 'linux':
  251. strip_binary(os.path.join(DIST_DIR, ffmpeg_name))
  252. with scoped_cwd(DIST_DIR):
  253. make_zip(zip_file, [ffmpeg_name, 'LICENSE', 'LICENSES.chromium.html'], [])
  254. def create_symbols_zip():
  255. if get_target_arch() == 'mips64el':
  256. return
  257. dist_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'symbols')
  258. zip_file = os.path.join(DIST_DIR, dist_name)
  259. licenses = ['LICENSE', 'LICENSES.chromium.html', 'version']
  260. with scoped_cwd(DIST_DIR):
  261. dirs = ['{0}.breakpad.syms'.format(PROJECT_NAME)]
  262. make_zip(zip_file, licenses, dirs)
  263. if PLATFORM == 'darwin':
  264. dsym_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'dsym')
  265. with scoped_cwd(DIST_DIR):
  266. dsyms = glob.glob('*.dSYM')
  267. make_zip(os.path.join(DIST_DIR, dsym_name), licenses, dsyms)
  268. elif PLATFORM == 'win32':
  269. pdb_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'pdb')
  270. with scoped_cwd(DIST_DIR):
  271. pdbs = glob.glob('*.pdb')
  272. make_zip(os.path.join(DIST_DIR, pdb_name), pdbs + licenses, [])
  273. def parse_args():
  274. parser = argparse.ArgumentParser(description='Create Electron Distribution')
  275. parser.add_argument('--no_api_docs',
  276. action='store_true',
  277. help='Skip generating the Electron API Documentation!')
  278. parser.add_argument('--chromium_dir',
  279. help='Specify a custom libchromiumcontent dist directory '
  280. + 'if manually compiled')
  281. parser.add_argument('-v', '--verbose',
  282. action='store_true',
  283. help='Prints the output of the subprocesses')
  284. return parser.parse_args()
  285. if __name__ == '__main__':
  286. sys.exit(main())