util.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. #!/usr/bin/env python
  2. import atexit
  3. import contextlib
  4. import datetime
  5. import errno
  6. import platform
  7. import re
  8. import shutil
  9. import ssl
  10. import subprocess
  11. import sys
  12. import tarfile
  13. import tempfile
  14. import urllib2
  15. import os
  16. import zipfile
  17. from config import is_verbose_mode, PLATFORM
  18. from env_util import get_vs_env
  19. BOTO_DIR = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'vendor',
  20. 'boto'))
  21. NPM = 'npm'
  22. if sys.platform in ['win32', 'cygwin']:
  23. NPM += '.cmd'
  24. def get_host_arch():
  25. """Returns the host architecture with a predictable string."""
  26. host_arch = platform.machine()
  27. # Convert machine type to format recognized by gyp.
  28. if re.match(r'i.86', host_arch) or host_arch == 'i86pc':
  29. host_arch = 'ia32'
  30. elif host_arch in ['x86_64', 'amd64']:
  31. host_arch = 'x64'
  32. elif host_arch.startswith('arm'):
  33. host_arch = 'arm'
  34. # platform.machine is based on running kernel. It's possible to use 64-bit
  35. # kernel with 32-bit userland, e.g. to give linker slightly more memory.
  36. # Distinguish between different userland bitness by querying
  37. # the python binary.
  38. if host_arch == 'x64' and platform.architecture()[0] == '32bit':
  39. host_arch = 'ia32'
  40. return host_arch
  41. def tempdir(prefix=''):
  42. directory = tempfile.mkdtemp(prefix=prefix)
  43. atexit.register(shutil.rmtree, directory)
  44. return directory
  45. @contextlib.contextmanager
  46. def scoped_cwd(path):
  47. cwd = os.getcwd()
  48. os.chdir(path)
  49. try:
  50. yield
  51. finally:
  52. os.chdir(cwd)
  53. @contextlib.contextmanager
  54. def scoped_env(key, value):
  55. origin = ''
  56. if key in os.environ:
  57. origin = os.environ[key]
  58. os.environ[key] = value
  59. try:
  60. yield
  61. finally:
  62. os.environ[key] = origin
  63. def download(text, url, path):
  64. safe_mkdir(os.path.dirname(path))
  65. with open(path, 'wb') as local_file:
  66. if hasattr(ssl, '_create_unverified_context'):
  67. ssl._create_default_https_context = ssl._create_unverified_context
  68. web_file = urllib2.urlopen(url)
  69. file_size = int(web_file.info().getheaders("Content-Length")[0])
  70. downloaded_size = 0
  71. block_size = 128
  72. ci = os.environ.get('CI') is not None
  73. while True:
  74. buf = web_file.read(block_size)
  75. if not buf:
  76. break
  77. downloaded_size += len(buf)
  78. local_file.write(buf)
  79. if not ci:
  80. percent = downloaded_size * 100. / file_size
  81. status = "\r%s %10d [%3.1f%%]" % (text, downloaded_size, percent)
  82. print status,
  83. if ci:
  84. print "%s done." % (text)
  85. else:
  86. print
  87. return path
  88. def extract_tarball(tarball_path, member, destination):
  89. with tarfile.open(tarball_path) as tarball:
  90. tarball.extract(member, destination)
  91. def extract_zip(zip_path, destination):
  92. if sys.platform == 'darwin':
  93. # Use unzip command on Mac to keep symbol links in zip file work.
  94. execute(['unzip', zip_path, '-d', destination])
  95. else:
  96. with zipfile.ZipFile(zip_path) as z:
  97. z.extractall(destination)
  98. def make_zip(zip_file_path, files, dirs):
  99. safe_unlink(zip_file_path)
  100. if sys.platform == 'darwin':
  101. files += dirs
  102. execute(['zip', '-r', '-y', zip_file_path] + files)
  103. else:
  104. zip_file = zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED)
  105. for filename in files:
  106. zip_file.write(filename, filename)
  107. for dirname in dirs:
  108. for root, _, filenames in os.walk(dirname):
  109. for f in filenames:
  110. zip_file.write(os.path.join(root, f))
  111. zip_file.close()
  112. def rm_rf(path):
  113. try:
  114. shutil.rmtree(path)
  115. except OSError:
  116. pass
  117. def safe_unlink(path):
  118. try:
  119. os.unlink(path)
  120. except OSError as e:
  121. if e.errno != errno.ENOENT:
  122. raise
  123. def safe_mkdir(path):
  124. try:
  125. os.makedirs(path)
  126. except OSError as e:
  127. if e.errno != errno.EEXIST:
  128. raise
  129. def execute(argv, env=os.environ, cwd=None):
  130. if is_verbose_mode():
  131. print ' '.join(argv)
  132. try:
  133. output = subprocess.check_output(argv, stderr=subprocess.STDOUT, env=env, cwd=cwd)
  134. if is_verbose_mode():
  135. print output
  136. return output
  137. except subprocess.CalledProcessError as e:
  138. print e.output
  139. raise e
  140. def execute_stdout(argv, env=os.environ, cwd=None):
  141. if is_verbose_mode():
  142. print ' '.join(argv)
  143. try:
  144. subprocess.check_call(argv, env=env, cwd=cwd)
  145. except subprocess.CalledProcessError as e:
  146. print e.output
  147. raise e
  148. else:
  149. execute(argv, env, cwd)
  150. def electron_gyp():
  151. SOURCE_ROOT = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
  152. gyp = os.path.join(SOURCE_ROOT, 'electron.gyp')
  153. with open(gyp) as f:
  154. obj = eval(f.read());
  155. return obj['variables']
  156. def electron_features():
  157. SOURCE_ROOT = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
  158. gyp = os.path.join(SOURCE_ROOT, 'features.gypi')
  159. with open(gyp) as f:
  160. obj = eval(f.read());
  161. return obj['variables']['variables']
  162. def get_electron_version():
  163. return 'v' + electron_gyp()['version%']
  164. def parse_version(version):
  165. if version[0] == 'v':
  166. version = version[1:]
  167. vs = version.split('.')
  168. if len(vs) > 4:
  169. return vs[0:4]
  170. else:
  171. return vs + ['0'] * (4 - len(vs))
  172. def boto_path_dirs():
  173. return [
  174. os.path.join(BOTO_DIR, 'build', 'lib'),
  175. os.path.join(BOTO_DIR, 'build', 'lib.linux-x86_64-2.7')
  176. ]
  177. def run_boto_script(access_key, secret_key, script_name, *args):
  178. env = os.environ.copy()
  179. env['AWS_ACCESS_KEY_ID'] = access_key
  180. env['AWS_SECRET_ACCESS_KEY'] = secret_key
  181. env['PYTHONPATH'] = os.path.pathsep.join(
  182. [env.get('PYTHONPATH', '')] + boto_path_dirs())
  183. boto = os.path.join(BOTO_DIR, 'bin', script_name)
  184. execute([sys.executable, boto] + list(args), env)
  185. def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
  186. args = [
  187. '--bucket', bucket,
  188. '--prefix', prefix,
  189. '--key_prefix', key_prefix,
  190. '--grant', 'public-read'
  191. ] + files
  192. run_boto_script(access_key, secret_key, 's3put', *args)
  193. def import_vs_env(target_arch):
  194. if sys.platform != 'win32':
  195. return
  196. if target_arch == 'ia32':
  197. vs_arch = 'amd64_x86'
  198. else:
  199. vs_arch = 'x86_amd64'
  200. env = get_vs_env('[15.0,16.0)', vs_arch)
  201. os.environ.update(env)
  202. def set_clang_env(env):
  203. SOURCE_ROOT = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
  204. llvm_dir = os.path.join(SOURCE_ROOT, 'vendor', 'llvm-build',
  205. 'Release+Asserts', 'bin')
  206. env['CC'] = os.path.join(llvm_dir, 'clang')
  207. env['CXX'] = os.path.join(llvm_dir, 'clang++')
  208. def update_electron_modules(dirname, target_arch, nodedir):
  209. env = os.environ.copy()
  210. version = get_electron_version()
  211. env['npm_config_arch'] = target_arch
  212. env['npm_config_target'] = version
  213. env['npm_config_nodedir'] = nodedir
  214. update_node_modules(dirname, env)
  215. execute_stdout([NPM, 'rebuild'], env, dirname)
  216. def update_node_modules(dirname, env=None):
  217. if env is None:
  218. env = os.environ.copy()
  219. if PLATFORM == 'linux':
  220. # Use prebuilt clang for building native modules.
  221. set_clang_env(env)
  222. env['npm_config_clang'] = '1'
  223. with scoped_cwd(dirname):
  224. args = [NPM, 'install']
  225. if is_verbose_mode():
  226. args += ['--verbose']
  227. # Ignore npm install errors when running in CI.
  228. if os.environ.has_key('CI'):
  229. try:
  230. execute_stdout(args, env)
  231. except subprocess.CalledProcessError:
  232. pass
  233. else:
  234. execute_stdout(args, env)
  235. def clean_parse_version(v):
  236. return parse_version(v.split("-")[0])
  237. def is_stable(v):
  238. return len(v.split(".")) == 3
  239. def is_beta(v):
  240. return 'beta' in v
  241. def is_nightly(v):
  242. return 'nightly' in v
  243. def get_nightly_date():
  244. return datetime.datetime.today().strftime('%Y%m%d')
  245. def get_last_major():
  246. return execute(['node', 'script/get-last-major-for-master.js'])
  247. def get_next_nightly(v):
  248. pv = clean_parse_version(v)
  249. major = pv[0]; minor = pv[1]; patch = pv[2]
  250. if (is_stable(v)):
  251. patch = str(int(pv[2]) + 1)
  252. if execute(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) == "master":
  253. major = str(get_last_major() + 1)
  254. minor = '0'
  255. patch = '0'
  256. pre = 'nightly.' + get_nightly_date()
  257. return make_version(major, minor, patch, pre)
  258. def non_empty(thing):
  259. return thing.strip() != ''
  260. def beta_tag_compare(tag1, tag2):
  261. p1 = parse_version(tag1)
  262. p2 = parse_version(tag2)
  263. return int(p1[3]) - int(p2[3])
  264. def get_next_beta(v):
  265. pv = clean_parse_version(v)
  266. tag_pattern = 'v' + pv[0] + '.' + pv[1] + '.' + pv[2] + '-beta.*'
  267. tag_list = sorted(filter(
  268. non_empty,
  269. execute(['git', 'tag', '--list', '-l', tag_pattern]).strip().split('\n')
  270. ), cmp=beta_tag_compare)
  271. if len(tag_list) == 0:
  272. return make_version(pv[0] , pv[1], pv[2], 'beta.1')
  273. lv = parse_version(tag_list[-1])
  274. return make_version(pv[0], pv[1], pv[2], 'beta.' + str(int(lv[3]) + 1))
  275. def get_next_stable_from_pre(v):
  276. pv = clean_parse_version(v)
  277. major = pv[0]; minor = pv[1]; patch = pv[2]
  278. return make_version(major, minor, patch)
  279. def get_next_stable_from_stable(v):
  280. pv = clean_parse_version(v)
  281. major = pv[0]; minor = pv[1]; patch = pv[2]
  282. return make_version(major, minor, str(int(patch) + 1))
  283. def make_version(major, minor, patch, pre = None):
  284. if pre is None:
  285. return major + '.' + minor + '.' + patch
  286. return major + "." + minor + "." + patch + '-' + pre