create-dist.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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, electron_features, parse_version
  16. from lib.env_util import get_vs_location
  17. ELECTRON_VERSION = get_electron_version()
  18. SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
  19. DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
  20. OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'R')
  21. CHROMIUM_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'download',
  22. 'libchromiumcontent', 'static_library')
  23. NATIVE_MKSNAPSHOT_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'native_mksnapshot')
  24. PROJECT_NAME = electron_gyp()['project_name%']
  25. PRODUCT_NAME = electron_gyp()['product_name%']
  26. PDF_VIEWER_ENABLED = electron_features()['enable_pdf_viewer%']
  27. TARGET_BINARIES = {
  28. 'darwin': [
  29. ],
  30. 'win32': [
  31. '{0}.exe'.format(PROJECT_NAME), # 'electron.exe'
  32. 'content_shell.pak',
  33. 'd3dcompiler_47.dll',
  34. 'icudtl.dat',
  35. 'libEGL.dll',
  36. 'libGLESv2.dll',
  37. 'ffmpeg.dll',
  38. 'node.dll',
  39. 'blink_image_resources_200_percent.pak',
  40. 'content_resources_200_percent.pak',
  41. 'ui_resources_200_percent.pak',
  42. 'views_resources_200_percent.pak',
  43. 'natives_blob.bin',
  44. 'snapshot_blob.bin',
  45. ],
  46. 'linux': [
  47. PROJECT_NAME, # 'electron'
  48. 'content_shell.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. if PLATFORM != 'darwin' and PDF_VIEWER_ENABLED:
  109. shutil.copy2(os.path.join(OUT_DIR, 'pdf_viewer_resources.pak'),
  110. DIST_DIR)
  111. for directory in TARGET_DIRECTORIES[PLATFORM]:
  112. shutil.copytree(os.path.join(OUT_DIR, directory),
  113. os.path.join(DIST_DIR, directory),
  114. symlinks=True)
  115. def copy_chrome_binary(binary):
  116. if PLATFORM == 'win32':
  117. binary += '.exe'
  118. src = os.path.join(CHROMIUM_DIR, binary)
  119. dest = os.path.join(DIST_DIR, binary)
  120. # Copy file and keep the executable bit.
  121. shutil.copyfile(src, dest)
  122. os.chmod(dest, os.stat(dest).st_mode | stat.S_IEXEC)
  123. def copy_vcruntime_binaries():
  124. arch = get_target_arch()
  125. if arch == "ia32":
  126. arch = "x86"
  127. subkey = r"SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\\"
  128. with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey + arch, 0,
  129. _winreg.KEY_READ | _winreg.KEY_WOW64_32KEY) as key:
  130. runtime_version = _winreg.QueryValueEx(key, "Version")[0][1:]
  131. version_parts = parse_version(runtime_version)
  132. if len(version_parts) > 3:
  133. runtime_version = '.'.join(version_parts[0:3])
  134. vs_location = get_vs_location('[15.0,16.0)')
  135. crt_dir = os.path.join(vs_location, 'VC', 'Redist', 'MSVC', runtime_version,
  136. arch, 'Microsoft.VC141.CRT')
  137. dlls = ["msvcp140.dll", "vcruntime140.dll"]
  138. # Note: copyfile is used to remove the read-only flag
  139. for dll in dlls:
  140. shutil.copyfile(os.path.join(crt_dir, dll), os.path.join(DIST_DIR, dll))
  141. TARGET_BINARIES_EXT.append(dll)
  142. def copy_ucrt_binaries():
  143. with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
  144. r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"
  145. ) as key:
  146. ucrt_dir = _winreg.QueryValueEx(key, "KitsRoot10")[0]
  147. arch = get_target_arch()
  148. if arch == "ia32":
  149. arch = "x86"
  150. ucrt_dir += r"Redist\ucrt\DLLs\{0}".format(arch)
  151. dlls = glob.glob(os.path.join(ucrt_dir, '*.dll'))
  152. if len(dlls) == 0:
  153. raise Exception('UCRT files not found')
  154. for dll in dlls:
  155. shutil.copy2(dll, DIST_DIR)
  156. TARGET_BINARIES_EXT.append(os.path.basename(dll))
  157. def copy_license():
  158. shutil.copy2(os.path.join(CHROMIUM_DIR, '..', 'LICENSES.chromium.html'),
  159. DIST_DIR)
  160. shutil.copy2(os.path.join(SOURCE_ROOT, 'LICENSE'), DIST_DIR)
  161. def create_api_json_schema():
  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. outfile = os.path.relpath(os.path.join(DIST_DIR, 'electron-api.json'))
  166. execute(['electron-docs-linter', 'docs', '--outfile={0}'.format(outfile),
  167. '--version={}'.format(ELECTRON_VERSION.replace('v', ''))],
  168. env=env)
  169. def create_typescript_definitions():
  170. node_bin_dir = os.path.join(SOURCE_ROOT, 'node_modules', '.bin')
  171. env = os.environ.copy()
  172. env['PATH'] = os.path.pathsep.join([node_bin_dir, env['PATH']])
  173. infile = os.path.relpath(os.path.join(DIST_DIR, 'electron-api.json'))
  174. outfile = os.path.relpath(os.path.join(DIST_DIR, 'electron.d.ts'))
  175. execute(['electron-typescript-definitions', '--in={0}'.format(infile),
  176. '--out={0}'.format(outfile)], env=env)
  177. def strip_binaries():
  178. for binary in TARGET_BINARIES[PLATFORM]:
  179. if binary.endswith('.so') or '.' not in binary:
  180. strip_binary(os.path.join(DIST_DIR, binary))
  181. def strip_binary(binary_path):
  182. if get_target_arch() == 'arm':
  183. strip = 'arm-linux-gnueabihf-strip'
  184. elif get_target_arch() == 'arm64':
  185. strip = 'aarch64-linux-gnu-strip'
  186. elif get_target_arch() == 'mips64el':
  187. strip = 'mips64el-redhat-linux-strip'
  188. else:
  189. strip = 'strip'
  190. execute([strip, binary_path], env=build_env())
  191. def create_version():
  192. version_path = os.path.join(SOURCE_ROOT, 'dist', 'version')
  193. with open(version_path, 'w') as version_file:
  194. version_file.write(ELECTRON_VERSION)
  195. def create_symbols():
  196. if get_target_arch() == 'mips64el':
  197. return
  198. destination = os.path.join(DIST_DIR, '{0}.breakpad.syms'.format(PROJECT_NAME))
  199. dump_symbols = os.path.join(SOURCE_ROOT, 'script', 'dump-symbols.py')
  200. execute([sys.executable, dump_symbols, destination])
  201. if PLATFORM == 'darwin':
  202. dsyms = glob.glob(os.path.join(OUT_DIR, '*.dSYM'))
  203. for dsym in dsyms:
  204. shutil.copytree(dsym, os.path.join(DIST_DIR, os.path.basename(dsym)))
  205. elif PLATFORM == 'win32':
  206. pdbs = glob.glob(os.path.join(OUT_DIR, '*.pdb'))
  207. for pdb in pdbs:
  208. shutil.copy2(pdb, DIST_DIR)
  209. def create_dist_zip():
  210. dist_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION)
  211. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  212. with scoped_cwd(DIST_DIR):
  213. files = TARGET_BINARIES[PLATFORM] + TARGET_BINARIES_EXT + ['LICENSE',
  214. 'LICENSES.chromium.html', 'version']
  215. if PLATFORM != 'darwin' and PDF_VIEWER_ENABLED:
  216. files += ['pdf_viewer_resources.pak']
  217. dirs = TARGET_DIRECTORIES[PLATFORM]
  218. make_zip(zip_file, files, dirs)
  219. def create_chrome_binary_zip(binary, version):
  220. file_suffix = ''
  221. create_native_mksnapshot = False
  222. if binary == 'mksnapshot':
  223. arch = get_target_arch()
  224. if arch.startswith('arm'):
  225. # if the arch is arm/arm64 the mksnapshot executable is an x64 binary,
  226. # so name it as such.
  227. file_suffix = 'x64'
  228. create_native_mksnapshot = True
  229. dist_name = get_zip_name(binary, version, file_suffix)
  230. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  231. files = ['LICENSE', 'LICENSES.chromium.html']
  232. if PLATFORM == 'win32':
  233. files += [binary + '.exe']
  234. else:
  235. files += [binary]
  236. with scoped_cwd(DIST_DIR):
  237. make_zip(zip_file, files, [])
  238. if create_native_mksnapshot == True:
  239. # Create a zip with the native version of the mksnapshot binary.
  240. src = os.path.join(NATIVE_MKSNAPSHOT_DIR, binary)
  241. dest = os.path.join(DIST_DIR, binary)
  242. # Copy file and keep the executable bit.
  243. shutil.copyfile(src, dest)
  244. os.chmod(dest, os.stat(dest).st_mode | stat.S_IEXEC)
  245. dist_name = get_zip_name(binary, version)
  246. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  247. with scoped_cwd(DIST_DIR):
  248. make_zip(zip_file, files, [])
  249. def create_ffmpeg_zip():
  250. dist_name = get_zip_name('ffmpeg', ELECTRON_VERSION)
  251. zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
  252. if PLATFORM == 'darwin':
  253. ffmpeg_name = 'libffmpeg.dylib'
  254. elif PLATFORM == 'linux':
  255. ffmpeg_name = 'libffmpeg.so'
  256. elif PLATFORM == 'win32':
  257. ffmpeg_name = 'ffmpeg.dll'
  258. shutil.copy2(os.path.join(CHROMIUM_DIR, '..', 'ffmpeg', ffmpeg_name),
  259. DIST_DIR)
  260. if PLATFORM == 'linux':
  261. strip_binary(os.path.join(DIST_DIR, ffmpeg_name))
  262. with scoped_cwd(DIST_DIR):
  263. make_zip(zip_file, [ffmpeg_name, 'LICENSE', 'LICENSES.chromium.html'], [])
  264. def create_symbols_zip():
  265. if get_target_arch() == 'mips64el':
  266. return
  267. dist_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'symbols')
  268. zip_file = os.path.join(DIST_DIR, dist_name)
  269. licenses = ['LICENSE', 'LICENSES.chromium.html', 'version']
  270. with scoped_cwd(DIST_DIR):
  271. dirs = ['{0}.breakpad.syms'.format(PROJECT_NAME)]
  272. make_zip(zip_file, licenses, dirs)
  273. if PLATFORM == 'darwin':
  274. dsym_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'dsym')
  275. with scoped_cwd(DIST_DIR):
  276. dsyms = glob.glob('*.dSYM')
  277. make_zip(os.path.join(DIST_DIR, dsym_name), licenses, dsyms)
  278. elif PLATFORM == 'win32':
  279. pdb_name = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'pdb')
  280. with scoped_cwd(DIST_DIR):
  281. pdbs = glob.glob('*.pdb')
  282. make_zip(os.path.join(DIST_DIR, pdb_name), pdbs + licenses, [])
  283. def parse_args():
  284. parser = argparse.ArgumentParser(description='Create Electron Distribution')
  285. parser.add_argument('--no_api_docs',
  286. action='store_true',
  287. help='Skip generating the Electron API Documentation!')
  288. parser.add_argument('--chromium_dir',
  289. help='Specify a custom libchromiumcontent dist directory '
  290. + 'if manually compiled')
  291. parser.add_argument('-v', '--verbose',
  292. action='store_true',
  293. help='Prints the output of the subprocesses')
  294. return parser.parse_args()
  295. if __name__ == '__main__':
  296. sys.exit(main())