automate-git.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494
  1. # Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
  2. # reserved. Use of this source code is governed by a BSD-style license that
  3. # can be found in the LICENSE file.
  4. from __future__ import absolute_import
  5. from __future__ import print_function
  6. from datetime import datetime
  7. from io import open
  8. from optparse import OptionParser
  9. import os
  10. import re
  11. import shlex
  12. import shutil
  13. import subprocess
  14. import sys
  15. import tempfile
  16. import zipfile
  17. is_python2 = sys.version_info.major == 2
  18. if is_python2:
  19. from urllib import FancyURLopener
  20. from urllib2 import urlopen
  21. else:
  22. from urllib.request import FancyURLopener, urlopen
  23. ##
  24. # Default URLs.
  25. ##
  26. depot_tools_url = 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
  27. depot_tools_archive_url = 'https://storage.googleapis.com/chrome-infra/depot_tools.zip'
  28. cef_git_url = 'https://bitbucket.org/chromiumembedded/cef.git'
  29. ##
  30. # Global system variables.
  31. ##
  32. # Script directory.
  33. script_dir = os.path.dirname(__file__)
  34. ##
  35. # Helper functions.
  36. ##
  37. def msg(message):
  38. """ Output a message. """
  39. sys.stdout.write('--> ' + message + "\n")
  40. def run(command_line, working_dir, depot_tools_dir=None, output_file=None):
  41. """ Runs the specified command. """
  42. # add depot_tools to the path
  43. env = os.environ
  44. if not depot_tools_dir is None:
  45. env['PATH'] = depot_tools_dir + os.pathsep + env['PATH']
  46. sys.stdout.write('-------- Running "'+command_line+'" in "'+\
  47. working_dir+'"...'+"\n")
  48. if not options.dryrun:
  49. args = shlex.split(command_line.replace('\\', '\\\\'))
  50. if not output_file:
  51. return subprocess.check_call(
  52. args, cwd=working_dir, env=env, shell=(sys.platform == 'win32'))
  53. try:
  54. msg('Writing %s' % output_file)
  55. with open(output_file, 'w', encoding='utf-8') as fp:
  56. return subprocess.check_call(
  57. args,
  58. cwd=working_dir,
  59. env=env,
  60. shell=(sys.platform == 'win32'),
  61. stderr=subprocess.STDOUT,
  62. stdout=fp)
  63. except subprocess.CalledProcessError:
  64. msg('ERROR Run failed. See %s for output.' % output_file)
  65. raise
  66. def create_directory(path):
  67. """ Creates a directory if it doesn't already exist. """
  68. if not os.path.exists(path):
  69. msg("Creating directory %s" % (path))
  70. if not options.dryrun:
  71. os.makedirs(path)
  72. def delete_directory(path):
  73. """ Removes an existing directory. """
  74. if os.path.exists(path):
  75. msg("Removing directory %s" % (path))
  76. if not options.dryrun:
  77. shutil.rmtree(path, onerror=onerror)
  78. def copy_directory(source, target, allow_overwrite=False):
  79. """ Copies a directory from source to target. """
  80. if not options.dryrun and os.path.exists(target):
  81. if not allow_overwrite:
  82. raise Exception("Directory %s already exists" % (target))
  83. remove_directory(target)
  84. if os.path.exists(source):
  85. msg("Copying directory %s to %s" % (source, target))
  86. if not options.dryrun:
  87. shutil.copytree(source, target)
  88. def move_directory(source, target, allow_overwrite=False):
  89. """ Copies a directory from source to target. """
  90. if not options.dryrun and os.path.exists(target):
  91. if not allow_overwrite:
  92. raise Exception("Directory %s already exists" % (target))
  93. remove_directory(target)
  94. if os.path.exists(source):
  95. msg("Moving directory %s to %s" % (source, target))
  96. if not options.dryrun:
  97. shutil.move(source, target)
  98. def is_git_checkout(path):
  99. """ Returns true if the path represents a git checkout. """
  100. return os.path.exists(os.path.join(path, '.git'))
  101. def exec_cmd(cmd, path):
  102. """ Execute the specified command and return the result. """
  103. out = ''
  104. err = ''
  105. sys.stdout.write("-------- Running \"%s\" in \"%s\"...\n" % (cmd, path))
  106. parts = cmd.split()
  107. try:
  108. process = subprocess.Popen(
  109. parts,
  110. cwd=path,
  111. stdout=subprocess.PIPE,
  112. stderr=subprocess.PIPE,
  113. shell=(sys.platform == 'win32'))
  114. out, err = process.communicate()
  115. except IOError as e:
  116. (errno, strerror) = e.args
  117. raise
  118. except:
  119. raise
  120. return {'out': out.decode('utf-8'), 'err': err.decode('utf-8')}
  121. def get_git_hash(path, branch):
  122. """ Returns the git hash for the specified branch/tag/hash. """
  123. cmd = "%s rev-parse %s" % (git_exe, branch)
  124. result = exec_cmd(cmd, path)
  125. if result['out'] != '':
  126. return result['out'].strip()
  127. return 'Unknown'
  128. def get_git_url(path):
  129. """ Returns the origin url for the specified path. """
  130. cmd = "%s config --get remote.origin.url" % (git_exe)
  131. result = exec_cmd(cmd, path)
  132. if result['out'] != '':
  133. return result['out'].strip()
  134. return 'Unknown'
  135. def download_and_extract(src, target):
  136. """ Extracts the contents of src, which may be a URL or local file, to the
  137. target directory. """
  138. temporary = False
  139. if src[:4] == 'http':
  140. # Attempt to download a URL.
  141. opener = FancyURLopener({})
  142. response = opener.open(src)
  143. temporary = True
  144. handle, archive_path = tempfile.mkstemp(suffix='.zip')
  145. os.write(handle, response.read())
  146. os.close(handle)
  147. elif os.path.exists(src):
  148. # Use a local file.
  149. archive_path = src
  150. else:
  151. raise Exception('Path type is unsupported or does not exist: ' + src)
  152. if not zipfile.is_zipfile(archive_path):
  153. raise Exception('Not a valid zip archive: ' + src)
  154. # Attempt to extract the archive file.
  155. try:
  156. os.makedirs(target)
  157. zf = zipfile.ZipFile(archive_path, 'r')
  158. zf.extractall(target)
  159. except:
  160. shutil.rmtree(target, onerror=onerror)
  161. raise
  162. zf.close()
  163. # Delete the archive file if temporary.
  164. if temporary and os.path.exists(archive_path):
  165. os.remove(archive_path)
  166. def read_file(path):
  167. """ Read a file. """
  168. if os.path.exists(path):
  169. with open(path, 'r', encoding='utf-8') as fp:
  170. return fp.read()
  171. else:
  172. raise Exception("Path does not exist: %s" % (path))
  173. def write_fp(fp, data):
  174. if is_python2:
  175. fp.write(data.decode('utf-8'))
  176. else:
  177. fp.write(data)
  178. def write_file(path, data):
  179. """ Write a file. """
  180. msg('Writing %s' % path)
  181. if not options.dryrun:
  182. with open(path, 'w', encoding='utf-8') as fp:
  183. write_fp(fp, data)
  184. def read_config_file(path):
  185. """ Read a configuration file. """
  186. # Parse the contents.
  187. return eval(read_file(path), {'__builtins__': None}, None)
  188. def write_config_file(path, contents):
  189. """ Write a configuration file. """
  190. data = "{\n"
  191. for key in sorted(contents.keys()):
  192. data += " '%s': '%s',\n" % (key, contents[key])
  193. data += "}\n"
  194. write_file(path, data)
  195. def read_branch_config_file(path):
  196. """ Read the CEF branch from the specified path. """
  197. config_file = os.path.join(path, 'cef.branch')
  198. if os.path.isfile(config_file):
  199. contents = read_config_file(config_file)
  200. if 'branch' in contents:
  201. return contents['branch']
  202. return ''
  203. def write_branch_config_file(path, branch):
  204. """ Write the CEF branch to the specified path. """
  205. config_file = os.path.join(path, 'cef.branch')
  206. if not os.path.isfile(config_file):
  207. write_config_file(config_file, {'branch': branch})
  208. def apply_patch(name):
  209. patch_file = os.path.join(cef_dir, 'patch', 'patches', name)
  210. if os.path.exists(patch_file + ".patch"):
  211. # Attempt to apply the patch file.
  212. patch_tool = os.path.join(cef_dir, 'tools', 'patcher.py')
  213. run('%s %s --patch-file "%s" --patch-dir "%s"' %
  214. (python_exe, patch_tool, patch_file,
  215. chromium_src_dir), chromium_src_dir, depot_tools_dir)
  216. def apply_deps_patch():
  217. """ Patch the Chromium DEPS file before `gclient sync` if necessary. """
  218. deps_path = os.path.join(chromium_src_dir, deps_file)
  219. if os.path.isfile(deps_path):
  220. msg("Chromium DEPS file: %s" % (deps_path))
  221. apply_patch(deps_file)
  222. else:
  223. raise Exception("Path does not exist: %s" % (deps_path))
  224. def apply_runhooks_patch():
  225. """ Patch the Chromium runhooks files before `gclient runhooks` if necessary. """
  226. apply_patch('runhooks')
  227. def run_patch_updater(args='', output_file=None):
  228. """ Run the patch updater script. """
  229. tool = os.path.join(cef_src_dir, 'tools', 'patch_updater.py')
  230. if len(args) > 0:
  231. args = ' ' + args
  232. run('%s %s%s' % (python_exe, tool, args), cef_src_dir, depot_tools_dir,
  233. output_file)
  234. def onerror(func, path, exc_info):
  235. """
  236. Error handler for ``shutil.rmtree``.
  237. If the error is due to an access error (read only file)
  238. it attempts to add write permission and then retries.
  239. If the error is for another reason it re-raises the error.
  240. Usage : ``shutil.rmtree(path, onerror=onerror)``
  241. """
  242. import stat
  243. if not os.access(path, os.W_OK):
  244. # Is the error an access error ?
  245. os.chmod(path, stat.S_IWUSR)
  246. func(path)
  247. else:
  248. raise
  249. def get_chromium_main_position(commit):
  250. """ Returns the closest main position for the specified Chromium commit. """
  251. # Using -2 because a "Publish DEPS" commit which does not have a master
  252. # position may be first.
  253. cmd = "%s log -2 %s" % (git_exe, commit)
  254. result = exec_cmd(cmd, chromium_src_dir)
  255. if result['out'] != '':
  256. match = re.search(r'refs/heads/(?:master|main)@{#([\d]+)}', result['out'])
  257. assert match != None, 'Failed to find position'
  258. return int(match.groups()[0])
  259. return None
  260. def get_chromium_main_commit(position):
  261. """ Returns the main commit for the specified Chromium commit position. """
  262. cmd = '%s log -1 --grep=refs/heads/master@{#%s} --grep=refs/heads/main@{#%s} origin/main' % (
  263. git_exe, str(position), str(position))
  264. result = exec_cmd(cmd, chromium_src_dir)
  265. if result['out'] != '':
  266. match = re.search(r'^commit ([a-f0-9]+)', result['out'])
  267. assert match != None, 'Failed to find commit'
  268. return match.groups()[0]
  269. return None
  270. def get_build_compat_versions():
  271. """ Returns the compatible Chromium and (optionally) depot_tools versions
  272. specified by the CEF checkout. """
  273. compat_path = os.path.join(cef_dir, 'CHROMIUM_BUILD_COMPATIBILITY.txt')
  274. msg("Reading %s" % compat_path)
  275. config = read_config_file(compat_path)
  276. if not 'chromium_checkout' in config:
  277. raise Exception("Missing chromium_checkout value in %s" % (compat_path))
  278. return config
  279. def get_build_directory_name(is_debug):
  280. build_dir = ('Debug' if is_debug else 'Release') + '_'
  281. # CEF uses a consistent directory naming scheme for GN via
  282. # GetAllPlatformConfigs in tools/gn_args.py.
  283. if options.x64build:
  284. build_dir += 'GN_x64'
  285. elif options.armbuild:
  286. build_dir += 'GN_arm'
  287. elif options.arm64build:
  288. build_dir += 'GN_arm64'
  289. else:
  290. build_dir += 'GN_x86'
  291. return build_dir
  292. def read_update_file():
  293. update_path = os.path.join(cef_src_dir, 'CHROMIUM_UPDATE.txt')
  294. if not os.path.exists(update_path):
  295. msg("Missing file: %s" % update_path)
  296. return None
  297. msg("Reading %s" % update_path)
  298. return read_config_file(update_path)
  299. def log_chromium_changes():
  300. """ Evaluate the Chromium checkout for changes. """
  301. config = read_update_file()
  302. if config is None:
  303. msg("Skipping Chromium changes log.")
  304. return
  305. if 'files' in config:
  306. out_file = os.path.join(download_dir, 'chromium_update_changes.diff')
  307. if os.path.exists(out_file):
  308. os.remove(out_file)
  309. old_commit = get_chromium_main_commit(
  310. get_chromium_main_position(chromium_compat_version))
  311. new_commit = get_chromium_main_commit(
  312. get_chromium_main_position(chromium_checkout))
  313. cmd = '%s diff --relative --no-prefix %s..%s -- %s' % (
  314. git_exe, old_commit, new_commit, ' '.join(config['files']))
  315. result = exec_cmd(cmd, chromium_src_dir)
  316. if result['out'] != '':
  317. write_file(out_file, result['out'])
  318. def check_pattern_matches(output_file=None):
  319. """ Evaluate the Chromium checkout for pattern matches. """
  320. config = read_update_file()
  321. if config is None:
  322. msg("Skipping Chromium pattern matching.")
  323. return
  324. if 'patterns' in config:
  325. if output_file is None:
  326. fp = sys.stdout
  327. else:
  328. msg('Writing %s' % output_file)
  329. fp = open(output_file, 'w', encoding='utf-8')
  330. has_output = False
  331. for entry in config['patterns']:
  332. msg("Evaluating pattern: %s" % entry['pattern'])
  333. # Read patterns from a file to avoid formatting problems.
  334. pattern_handle, pattern_file = tempfile.mkstemp()
  335. os.write(pattern_handle, entry['pattern'])
  336. os.close(pattern_handle)
  337. cmd = '%s grep -n -f %s' % (git_exe, pattern_file)
  338. result = exec_cmd(cmd, chromium_src_dir)
  339. os.remove(pattern_file)
  340. if result['out'] != '':
  341. write_msg = True
  342. re_exclude = re.compile(
  343. entry['exclude_matches']) if 'exclude_matches' in entry else None
  344. for line in result['out'].split('\n'):
  345. line = line.strip()
  346. if len(line) == 0:
  347. continue
  348. skip = not re_exclude is None and re_exclude.match(line) != None
  349. if not skip:
  350. if write_msg:
  351. if has_output:
  352. write_fp(fp, '\n')
  353. write_fp(fp,
  354. '!!!! WARNING: FOUND PATTERN: %s\n' % entry['pattern'])
  355. if 'message' in entry:
  356. write_fp(fp, entry['message'] + '\n')
  357. write_fp(fp, '\n')
  358. write_msg = False
  359. write_fp(fp, line + '\n')
  360. has_output = True
  361. if not output_file is None:
  362. if has_output:
  363. msg('ERROR Matches found. See %s for output.' % out_file)
  364. else:
  365. write_fp(fp, 'Good news! No matches.\n')
  366. fp.close()
  367. if has_output:
  368. # Don't continue when we know the build will be wrong.
  369. sys.exit(1)
  370. ##
  371. # Program entry point.
  372. ##
  373. # Cannot be loaded as a module.
  374. if __name__ != "__main__":
  375. sys.stderr.write('This file cannot be loaded as a module!')
  376. sys.exit(1)
  377. # Parse command-line options.
  378. disc = """
  379. This utility implements automation for the download, update, build and
  380. distribution of CEF.
  381. """
  382. parser = OptionParser(description=disc)
  383. # Setup options.
  384. parser.add_option(
  385. '--download-dir',
  386. dest='downloaddir',
  387. metavar='DIR',
  388. help='Download directory with no spaces [required].')
  389. parser.add_option(
  390. '--depot-tools-dir',
  391. dest='depottoolsdir',
  392. metavar='DIR',
  393. help='Download directory for depot_tools.',
  394. default='')
  395. parser.add_option('--depot-tools-archive', dest='depottoolsarchive',
  396. help='Zip archive file that contains a single top-level '+\
  397. 'depot_tools directory.', default='')
  398. parser.add_option('--branch', dest='branch',
  399. help='Branch of CEF to build (master, 3987, ...). This '+\
  400. 'will be used to name the CEF download directory and '+\
  401. 'to identify the correct URL if --url is not '+\
  402. 'specified. The default value is master.',
  403. default='master')
  404. parser.add_option('--url', dest='url',
  405. help='CEF download URL. If not specified the default URL '+\
  406. 'will be used.',
  407. default='')
  408. parser.add_option('--chromium-url', dest='chromiumurl',
  409. help='Chromium download URL. If not specified the default '+\
  410. 'URL will be used.',
  411. default='')
  412. parser.add_option('--checkout', dest='checkout',
  413. help='Version of CEF to checkout. If not specified the '+\
  414. 'most recent remote version of the branch will be used.',
  415. default='')
  416. parser.add_option('--chromium-checkout', dest='chromiumcheckout',
  417. help='Version of Chromium to checkout (Git '+\
  418. 'branch/hash/tag). This overrides the value specified '+\
  419. 'by CEF in CHROMIUM_BUILD_COMPATIBILITY.txt.',
  420. default='')
  421. # Miscellaneous options.
  422. parser.add_option(
  423. '--force-config',
  424. action='store_true',
  425. dest='forceconfig',
  426. default=False,
  427. help='Force creation of a new gclient config file.')
  428. parser.add_option('--force-clean',
  429. action='store_true', dest='forceclean', default=False,
  430. help='Force a clean checkout of Chromium and CEF. This will'+\
  431. ' trigger a new update, build and distribution.')
  432. parser.add_option('--force-clean-deps',
  433. action='store_true', dest='forcecleandeps', default=False,
  434. help='Force a clean checkout of Chromium dependencies. Used'+\
  435. ' in combination with --force-clean.')
  436. parser.add_option(
  437. '--dry-run',
  438. action='store_true',
  439. dest='dryrun',
  440. default=False,
  441. help="Output commands without executing them.")
  442. parser.add_option('--dry-run-platform', dest='dryrunplatform', default=None,
  443. help='Simulate a dry run on the specified platform '+\
  444. '(windows, mac, linux). Must be used in combination'+\
  445. ' with the --dry-run flag.')
  446. # Update-related options.
  447. parser.add_option('--force-update',
  448. action='store_true', dest='forceupdate', default=False,
  449. help='Force a Chromium and CEF update. This will trigger a '+\
  450. 'new build and distribution.')
  451. parser.add_option('--no-update',
  452. action='store_true', dest='noupdate', default=False,
  453. help='Do not update Chromium or CEF. Pass --force-build or '+\
  454. '--force-distrib if you desire a new build or '+\
  455. 'distribution.')
  456. parser.add_option('--no-cef-update',
  457. action='store_true', dest='nocefupdate', default=False,
  458. help='Do not update CEF. Pass --force-build or '+\
  459. '--force-distrib if you desire a new build or '+\
  460. 'distribution.')
  461. parser.add_option('--force-cef-update',
  462. action='store_true', dest='forcecefupdate', default=False,
  463. help='Force a CEF update. This will cause local changes in '+\
  464. 'the CEF checkout to be discarded and patch files to '+\
  465. 'be reapplied.')
  466. parser.add_option(
  467. '--no-chromium-update',
  468. action='store_true',
  469. dest='nochromiumupdate',
  470. default=False,
  471. help='Do not update Chromium.')
  472. parser.add_option(
  473. '--no-depot-tools-update',
  474. action='store_true',
  475. dest='nodepottoolsupdate',
  476. default=False,
  477. help='Do not update depot_tools.')
  478. parser.add_option('--fast-update',
  479. action='store_true', dest='fastupdate', default=False,
  480. help='Update existing Chromium/CEF checkouts for fast incremental '+\
  481. 'builds by attempting to minimize the number of modified files. '+\
  482. 'The update will fail if there are unstaged CEF changes or if '+\
  483. 'Chromium changes are not included in a patch file.')
  484. parser.add_option(
  485. '--force-patch-update',
  486. action='store_true',
  487. dest='forcepatchupdate',
  488. default=False,
  489. help='Force update of patch files.')
  490. parser.add_option(
  491. '--resave',
  492. action='store_true',
  493. dest='resave',
  494. default=False,
  495. help='Resave patch files.')
  496. parser.add_option(
  497. '--log-chromium-changes',
  498. action='store_true',
  499. dest='logchromiumchanges',
  500. default=False,
  501. help='Create a log of the Chromium changes.')
  502. # Build-related options.
  503. parser.add_option('--force-build',
  504. action='store_true', dest='forcebuild', default=False,
  505. help='Force CEF debug and release builds. This builds '+\
  506. '[build-target] on all platforms and chrome_sandbox '+\
  507. 'on Linux.')
  508. parser.add_option(
  509. '--no-build',
  510. action='store_true',
  511. dest='nobuild',
  512. default=False,
  513. help='Do not build CEF.')
  514. parser.add_option(
  515. '--build-target',
  516. dest='buildtarget',
  517. default='cefclient',
  518. help='Target name(s) to build (defaults to "cefclient").')
  519. parser.add_option(
  520. '--build-tests',
  521. action='store_true',
  522. dest='buildtests',
  523. default=False,
  524. help='Also build the test target specified via --test-target.')
  525. parser.add_option(
  526. '--no-debug-build',
  527. action='store_true',
  528. dest='nodebugbuild',
  529. default=False,
  530. help="Don't perform the CEF debug build.")
  531. parser.add_option(
  532. '--no-release-build',
  533. action='store_true',
  534. dest='noreleasebuild',
  535. default=False,
  536. help="Don't perform the CEF release build.")
  537. parser.add_option(
  538. '--verbose-build',
  539. action='store_true',
  540. dest='verbosebuild',
  541. default=False,
  542. help='Show all command lines while building.')
  543. parser.add_option(
  544. '--build-failure-limit',
  545. dest='buildfailurelimit',
  546. default=1,
  547. type="int",
  548. help='Keep going until N jobs fail.')
  549. parser.add_option('--build-log-file',
  550. action='store_true', dest='buildlogfile', default=False,
  551. help='Write build logs to file. The file will be named '+\
  552. '"build-[branch]-[debug|release].log" in the download '+\
  553. 'directory.')
  554. parser.add_option(
  555. '--x64-build',
  556. action='store_true',
  557. dest='x64build',
  558. default=False,
  559. help='Create a 64-bit build.')
  560. parser.add_option(
  561. '--arm-build',
  562. action='store_true',
  563. dest='armbuild',
  564. default=False,
  565. help='Create an ARM build.')
  566. parser.add_option(
  567. '--arm64-build',
  568. action='store_true',
  569. dest='arm64build',
  570. default=False,
  571. help='Create an ARM64 build.')
  572. parser.add_option(
  573. '--with-pgo-profiles',
  574. action='store_true',
  575. dest='withpgoprofiles',
  576. default=False,
  577. help='Download PGO profiles for the build.')
  578. # Test-related options.
  579. parser.add_option(
  580. '--run-tests',
  581. action='store_true',
  582. dest='runtests',
  583. default=False,
  584. help='Run the ceftests target.')
  585. parser.add_option(
  586. '--no-debug-tests',
  587. action='store_true',
  588. dest='nodebugtests',
  589. default=False,
  590. help="Don't run debug build tests.")
  591. parser.add_option(
  592. '--no-release-tests',
  593. action='store_true',
  594. dest='noreleasetests',
  595. default=False,
  596. help="Don't run release build tests.")
  597. parser.add_option(
  598. '--test-target',
  599. dest='testtarget',
  600. default='ceftests',
  601. help='Test target name to build (defaults to "ceftests").')
  602. parser.add_option(
  603. '--test-prefix',
  604. dest='testprefix',
  605. default='',
  606. help='Prefix for running the test executable (e.g. `xvfb-run` on Linux).')
  607. parser.add_option(
  608. '--test-args',
  609. dest='testargs',
  610. default='',
  611. help='Arguments that will be passed to the test executable.')
  612. # Distribution-related options.
  613. parser.add_option(
  614. '--force-distrib',
  615. action='store_true',
  616. dest='forcedistrib',
  617. default=False,
  618. help='Force creation of a CEF binary distribution.')
  619. parser.add_option(
  620. '--no-distrib',
  621. action='store_true',
  622. dest='nodistrib',
  623. default=False,
  624. help="Don't create a CEF binary distribution.")
  625. parser.add_option(
  626. '--minimal-distrib',
  627. action='store_true',
  628. dest='minimaldistrib',
  629. default=False,
  630. help='Create a minimal CEF binary distribution.')
  631. parser.add_option(
  632. '--minimal-distrib-only',
  633. action='store_true',
  634. dest='minimaldistribonly',
  635. default=False,
  636. help='Create a minimal CEF binary distribution only.')
  637. parser.add_option(
  638. '--client-distrib',
  639. action='store_true',
  640. dest='clientdistrib',
  641. default=False,
  642. help='Create a client CEF binary distribution.')
  643. parser.add_option(
  644. '--client-distrib-only',
  645. action='store_true',
  646. dest='clientdistribonly',
  647. default=False,
  648. help='Create a client CEF binary distribution only.')
  649. parser.add_option(
  650. '--sandbox-distrib',
  651. action='store_true',
  652. dest='sandboxdistrib',
  653. default=False,
  654. help='Create a cef_sandbox static library distribution.')
  655. parser.add_option(
  656. '--sandbox-distrib-only',
  657. action='store_true',
  658. dest='sandboxdistribonly',
  659. default=False,
  660. help='Create a cef_sandbox static library distribution only.')
  661. parser.add_option(
  662. '--no-distrib-docs',
  663. action='store_true',
  664. dest='nodistribdocs',
  665. default=False,
  666. help="Don't create CEF documentation.")
  667. parser.add_option(
  668. '--no-distrib-archive',
  669. action='store_true',
  670. dest='nodistribarchive',
  671. default=False,
  672. help="Don't create archives for output directories.")
  673. parser.add_option(
  674. '--clean-artifacts',
  675. action='store_true',
  676. dest='cleanartifacts',
  677. default=False,
  678. help='Clean the artifacts output directory.')
  679. parser.add_option(
  680. '--distrib-subdir',
  681. dest='distribsubdir',
  682. default='',
  683. help='CEF distrib dir name, child of chromium/src/cef/binary_distrib')
  684. parser.add_option(
  685. '--distrib-subdir-suffix',
  686. dest='distribsubdirsuffix',
  687. default='',
  688. help='CEF distrib dir name suffix, child of chromium/src/cef/binary_distrib'
  689. )
  690. (options, args) = parser.parse_args()
  691. if options.downloaddir is None:
  692. print("The --download-dir option is required.")
  693. parser.print_help(sys.stderr)
  694. sys.exit(1)
  695. # Opt into component-specific flags for later use.
  696. if options.noupdate:
  697. options.nocefupdate = True
  698. options.nochromiumupdate = True
  699. options.nodepottoolsupdate = True
  700. if options.runtests:
  701. options.buildtests = True
  702. if (options.nochromiumupdate and options.forceupdate) or \
  703. (options.nocefupdate and options.forceupdate) or \
  704. (options.nobuild and options.forcebuild) or \
  705. (options.nodistrib and options.forcedistrib) or \
  706. ((options.forceclean or options.forcecleandeps) and options.fastupdate):
  707. print("Invalid combination of options.")
  708. parser.print_help(sys.stderr)
  709. sys.exit(1)
  710. if (options.noreleasebuild and \
  711. (options.minimaldistrib or options.minimaldistribonly or \
  712. options.clientdistrib or options.clientdistribonly)) or \
  713. (options.minimaldistribonly + options.clientdistribonly + options.sandboxdistribonly > 1):
  714. print('Invalid combination of options.')
  715. parser.print_help(sys.stderr)
  716. sys.exit(1)
  717. if options.x64build + options.armbuild + options.arm64build > 1:
  718. print('Invalid combination of options.')
  719. parser.print_help(sys.stderr)
  720. sys.exit(1)
  721. if (options.buildtests or options.runtests) and len(options.testtarget) == 0:
  722. print("A test target must be specified via --test-target.")
  723. parser.print_help(sys.stderr)
  724. sys.exit(1)
  725. # Operating system.
  726. if options.dryrun and options.dryrunplatform is not None:
  727. platform = options.dryrunplatform
  728. if not platform in ['windows', 'mac', 'linux']:
  729. print('Invalid dry-run-platform value: %s' % (platform))
  730. sys.exit(1)
  731. elif sys.platform == 'win32':
  732. platform = 'windows'
  733. elif sys.platform == 'darwin':
  734. platform = 'mac'
  735. elif sys.platform.startswith('linux'):
  736. platform = 'linux'
  737. else:
  738. print('Unknown operating system platform')
  739. sys.exit(1)
  740. if options.clientdistrib or options.clientdistribonly:
  741. if platform == 'linux' or (platform == 'windows' and options.arm64build):
  742. client_app = 'cefsimple'
  743. else:
  744. client_app = 'cefclient'
  745. if options.buildtarget.find(client_app) == -1:
  746. print('A client distribution cannot be generated if --build-target ' +
  747. 'excludes %s.' % client_app)
  748. parser.print_help(sys.stderr)
  749. sys.exit(1)
  750. # CEF branch.
  751. cef_branch = options.branch
  752. branch_is_master = (cef_branch == 'master' or cef_branch == 'trunk')
  753. if not branch_is_master:
  754. # Verify that the branch value is numeric.
  755. if not cef_branch.isdigit():
  756. print('Invalid branch value: %s' % cef_branch)
  757. sys.exit(1)
  758. # Verify the minimum supported branch number.
  759. if int(cef_branch) < 3071:
  760. print('The requested branch (%s) is too old to build using this tool. ' +
  761. 'The minimum supported branch is 3071.' % cef_branch)
  762. sys.exit(1)
  763. # True if the requested branch is 3538 or newer.
  764. branch_is_3538_or_newer = (branch_is_master or int(cef_branch) >= 3538)
  765. # True if the requested branch is 3945 or newer.
  766. branch_is_3945_or_newer = (branch_is_master or int(cef_branch) >= 3945)
  767. # Enable Python 3 usage in Chromium for branches 3945 and newer.
  768. if branch_is_3945_or_newer and not is_python2 and \
  769. not 'GCLIENT_PY3' in os.environ.keys():
  770. os.environ['GCLIENT_PY3'] = '1'
  771. if not branch_is_3945_or_newer and \
  772. (not is_python2 or bool(int(os.environ.get('GCLIENT_PY3', '0')))):
  773. print('Python 3 is not supported with branch 3904 and older ' +
  774. '(set GCLIENT_PY3=0 and run with Python 2 executable).')
  775. sys.exit(1)
  776. if options.armbuild:
  777. if platform != 'linux':
  778. print('The ARM build option is only supported on Linux.')
  779. sys.exit(1)
  780. deps_file = 'DEPS'
  781. if platform == 'mac' and not (options.x64build or options.arm64build):
  782. print('32-bit MacOS builds are not supported. ' +
  783. 'Add --x64-build or --arm64-build flag to generate a 64-bit build.')
  784. sys.exit(1)
  785. # Platforms that build a cef_sandbox library.
  786. sandbox_lib_platforms = ['windows']
  787. if branch_is_3538_or_newer:
  788. sandbox_lib_platforms.append('mac')
  789. if not platform in sandbox_lib_platforms and (options.sandboxdistrib or
  790. options.sandboxdistribonly):
  791. print('The sandbox distribution is not supported on this platform.')
  792. sys.exit(1)
  793. # Options that force the sources to change.
  794. force_change = options.forceclean or options.forceupdate
  795. # Options that cause local changes to be discarded.
  796. discard_local_changes = force_change or options.forcecefupdate
  797. if options.resave and (options.forcepatchupdate or discard_local_changes):
  798. print('--resave cannot be combined with options that modify or discard ' +
  799. 'patches.')
  800. parser.print_help(sys.stderr)
  801. sys.exit(1)
  802. if platform == 'windows':
  803. # Avoid errors when the "vs_toolchain.py update" Chromium hook runs.
  804. os.environ['DEPOT_TOOLS_WIN_TOOLCHAIN'] = '0'
  805. download_dir = os.path.abspath(options.downloaddir)
  806. chromium_dir = os.path.join(download_dir, 'chromium')
  807. chromium_src_dir = os.path.join(chromium_dir, 'src')
  808. out_src_dir = os.path.join(chromium_src_dir, 'out')
  809. cef_src_dir = os.path.join(chromium_src_dir, 'cef')
  810. if options.fastupdate and os.path.exists(cef_src_dir):
  811. cef_dir = cef_src_dir
  812. else:
  813. cef_dir = os.path.join(download_dir, 'cef')
  814. ##
  815. # Manage the download directory.
  816. ##
  817. # Create the download directory if necessary.
  818. create_directory(download_dir)
  819. msg("Download Directory: %s" % (download_dir))
  820. ##
  821. # Manage the depot_tools directory.
  822. ##
  823. # Check if the depot_tools directory exists.
  824. if options.depottoolsdir != '':
  825. depot_tools_dir = os.path.abspath(options.depottoolsdir)
  826. else:
  827. depot_tools_dir = os.path.join(download_dir, 'depot_tools')
  828. msg("Depot Tools Directory: %s" % (depot_tools_dir))
  829. if not os.path.exists(depot_tools_dir):
  830. if platform == 'windows' and options.depottoolsarchive == '':
  831. # On Windows download depot_tools as an archive file since we can't assume
  832. # that git is already installed.
  833. options.depottoolsarchive = depot_tools_archive_url
  834. if options.depottoolsarchive != '':
  835. # Extract depot_tools from an archive file.
  836. msg('Extracting %s to %s.' % \
  837. (options.depottoolsarchive, depot_tools_dir))
  838. if not options.dryrun:
  839. download_and_extract(options.depottoolsarchive, depot_tools_dir)
  840. else:
  841. # On Linux and OS X check out depot_tools using Git.
  842. run('git clone ' + depot_tools_url + ' ' + depot_tools_dir, download_dir)
  843. if not options.nodepottoolsupdate:
  844. # Update depot_tools.
  845. # On Windows this will download required python and git binaries.
  846. msg('Updating depot_tools')
  847. if platform == 'windows':
  848. run('update_depot_tools.bat', depot_tools_dir, depot_tools_dir)
  849. else:
  850. run('update_depot_tools', depot_tools_dir, depot_tools_dir)
  851. # Determine the executables to use.
  852. if platform == 'windows':
  853. # Force use of the version bundled with depot_tools.
  854. git_exe = os.path.join(depot_tools_dir, 'git.bat')
  855. python_bat = 'python.bat' if is_python2 else 'python3.bat'
  856. python_exe = os.path.join(depot_tools_dir, python_bat)
  857. if options.dryrun and not os.path.exists(git_exe):
  858. sys.stdout.write("WARNING: --dry-run assumes that depot_tools" \
  859. " is already in your PATH. If it isn't\nplease" \
  860. " specify a --depot-tools-dir value.\n")
  861. git_exe = 'git.bat'
  862. python_exe = python_bat
  863. else:
  864. git_exe = 'git'
  865. python_exe = sys.executable
  866. ##
  867. # Manage the cef directory.
  868. ##
  869. # Delete the existing CEF directory if requested.
  870. if options.forceclean and os.path.exists(cef_dir):
  871. delete_directory(cef_dir)
  872. # Determine the type of CEF checkout to use.
  873. if os.path.exists(cef_dir) and not is_git_checkout(cef_dir):
  874. raise Exception("Not a valid CEF Git checkout: %s" % (cef_dir))
  875. # Determine the CEF download URL to use.
  876. cef_url = options.url.strip()
  877. if cef_url == '':
  878. cef_url = cef_git_url
  879. # Verify that the requested CEF URL matches the existing checkout.
  880. if not options.nocefupdate and os.path.exists(cef_dir):
  881. cef_existing_url = get_git_url(cef_dir)
  882. if cef_url != cef_existing_url:
  883. raise Exception(
  884. 'Requested CEF checkout URL %s does not match existing URL %s' %
  885. (cef_url, cef_existing_url))
  886. msg("CEF Branch: %s" % (cef_branch))
  887. msg("CEF URL: %s" % (cef_url))
  888. msg("CEF Source Directory: %s" % (cef_dir))
  889. # Determine the CEF Git branch to use.
  890. if options.checkout == '':
  891. # Target the most recent branch commit from the remote repo.
  892. if branch_is_master:
  893. cef_checkout = 'origin/master'
  894. else:
  895. cef_checkout = 'origin/' + cef_branch
  896. else:
  897. cef_checkout = options.checkout
  898. # Create the CEF checkout if necessary.
  899. if not options.nocefupdate and not os.path.exists(cef_dir):
  900. cef_checkout_new = True
  901. run('%s clone %s %s' % (git_exe, cef_url, cef_dir), download_dir,
  902. depot_tools_dir)
  903. else:
  904. cef_checkout_new = False
  905. # Determine if the CEF checkout needs to change.
  906. if not options.nocefupdate and os.path.exists(cef_dir):
  907. cef_current_hash = get_git_hash(cef_dir, 'HEAD')
  908. if not cef_checkout_new:
  909. # Fetch updated sources.
  910. run('%s fetch' % (git_exe), cef_dir, depot_tools_dir)
  911. cef_desired_hash = get_git_hash(cef_dir, cef_checkout)
  912. cef_checkout_changed = cef_checkout_new or force_change or \
  913. options.forcecefupdate or \
  914. cef_current_hash != cef_desired_hash
  915. msg("CEF Current Checkout: %s" % (cef_current_hash))
  916. msg("CEF Desired Checkout: %s (%s)" % (cef_desired_hash, cef_checkout))
  917. if cef_checkout_changed:
  918. if cef_dir == cef_src_dir:
  919. # Running in fast update mode. Backup and revert the patched files before
  920. # changing the CEF checkout.
  921. run_patch_updater("--backup --revert")
  922. # Update the CEF checkout.
  923. run('%s checkout %s%s' %
  924. (git_exe, '--force ' if discard_local_changes else '', cef_checkout), \
  925. cef_dir, depot_tools_dir)
  926. else:
  927. cef_checkout_changed = False
  928. build_compat_versions = get_build_compat_versions()
  929. if not options.nodepottoolsupdate and \
  930. 'depot_tools_checkout' in build_compat_versions:
  931. # Update the depot_tools checkout.
  932. depot_tools_compat_version = build_compat_versions['depot_tools_checkout']
  933. run('%s checkout %s%s' %
  934. (git_exe, '--force ' if discard_local_changes else '', depot_tools_compat_version), \
  935. depot_tools_dir, depot_tools_dir)
  936. # Disable further depot_tools updates.
  937. os.environ['DEPOT_TOOLS_UPDATE'] = '0'
  938. ##
  939. # Manage the out directory.
  940. ##
  941. out_dir = os.path.join(download_dir, 'out_' + cef_branch)
  942. # Delete the existing out directory if requested.
  943. if options.forceclean and os.path.exists(out_dir):
  944. delete_directory(out_dir)
  945. msg("CEF Output Directory: %s" % (out_dir))
  946. ##
  947. # Manage the chromium directory.
  948. ##
  949. # Create the chromium directory if necessary.
  950. create_directory(chromium_dir)
  951. if options.chromiumurl != '':
  952. chromium_url = options.chromiumurl
  953. else:
  954. chromium_url = 'https://chromium.googlesource.com/chromium/src.git'
  955. # Create gclient configuration file.
  956. gclient_file = os.path.join(chromium_dir, '.gclient')
  957. if not os.path.exists(gclient_file) or options.forceconfig:
  958. # Exclude unnecessary directories. Intentionally written without newlines.
  959. gclient_spec = \
  960. "solutions = [{"+\
  961. "'managed': False,"+\
  962. "'name': 'src', "+\
  963. "'url': '" + chromium_url + "', "+\
  964. "'custom_vars': {"+\
  965. "'checkout_pgo_profiles': " + ('True' if options.withpgoprofiles else 'False') + ", "+\
  966. "}, "+\
  967. "'custom_deps': {"+\
  968. "'build': None, "+\
  969. "'build/scripts/command_wrapper/bin': None, "+\
  970. "'build/scripts/gsd_generate_index': None, "+\
  971. "'build/scripts/private/data/reliability': None, "+\
  972. "'build/scripts/tools/deps2git': None, "+\
  973. "'build/third_party/lighttpd': None, "+\
  974. "'commit-queue': None, "+\
  975. "'depot_tools': None, "+\
  976. "'src/chrome_frame/tools/test/reference_build/chrome': None, "+\
  977. "'src/chrome/tools/test/reference_build/chrome_linux': None, "+\
  978. "'src/chrome/tools/test/reference_build/chrome_mac': None, "+\
  979. "'src/chrome/tools/test/reference_build/chrome_win': None, "+\
  980. "}, "+\
  981. "'deps_file': '" + deps_file + "', "+\
  982. "'safesync_url': ''"+\
  983. "}]"
  984. msg('Writing %s' % gclient_file)
  985. if not options.dryrun:
  986. with open(gclient_file, 'w', encoding='utf-8') as fp:
  987. write_fp(fp, gclient_spec)
  988. # Initial Chromium checkout.
  989. if not options.nochromiumupdate and not os.path.exists(chromium_src_dir):
  990. chromium_checkout_new = True
  991. run("gclient sync --nohooks --with_branch_heads --jobs 16", \
  992. chromium_dir, depot_tools_dir)
  993. else:
  994. chromium_checkout_new = False
  995. # Verify the Chromium checkout.
  996. if not options.dryrun and not is_git_checkout(chromium_src_dir):
  997. raise Exception('Not a valid git checkout: %s' % (chromium_src_dir))
  998. if os.path.exists(chromium_src_dir):
  999. msg("Chromium URL: %s" % (get_git_url(chromium_src_dir)))
  1000. # Fetch Chromium changes so that we can perform the necessary calculations using
  1001. # local history.
  1002. if not options.nochromiumupdate and os.path.exists(chromium_src_dir):
  1003. # Fetch updated sources.
  1004. run("%s fetch" % (git_exe), chromium_src_dir, depot_tools_dir)
  1005. # Also fetch tags, which are required for release branch builds.
  1006. run("%s fetch --tags" % (git_exe), chromium_src_dir, depot_tools_dir)
  1007. # Determine the Chromium checkout options required by CEF.
  1008. chromium_compat_version = build_compat_versions['chromium_checkout']
  1009. if len(options.chromiumcheckout) > 0:
  1010. chromium_checkout = options.chromiumcheckout
  1011. else:
  1012. chromium_checkout = chromium_compat_version
  1013. # Determine if the Chromium checkout needs to change.
  1014. if not options.nochromiumupdate and os.path.exists(chromium_src_dir):
  1015. chromium_current_hash = get_git_hash(chromium_src_dir, 'HEAD')
  1016. chromium_desired_hash = get_git_hash(chromium_src_dir, chromium_checkout)
  1017. chromium_checkout_changed = chromium_checkout_new or force_change or \
  1018. chromium_current_hash != chromium_desired_hash
  1019. msg("Chromium Current Checkout: %s" % (chromium_current_hash))
  1020. msg("Chromium Desired Checkout: %s (%s)" % \
  1021. (chromium_desired_hash, chromium_checkout))
  1022. else:
  1023. chromium_checkout_changed = options.dryrun
  1024. if cef_checkout_changed:
  1025. if cef_dir != cef_src_dir and os.path.exists(cef_src_dir):
  1026. # Delete the existing src/cef directory. It will be re-copied from the
  1027. # download directory later.
  1028. delete_directory(cef_src_dir)
  1029. elif chromium_checkout_changed and cef_dir == cef_src_dir:
  1030. # Running in fast update mode. Backup and revert the patched files before
  1031. # changing the Chromium checkout.
  1032. run_patch_updater("--backup --revert")
  1033. # Delete the existing src/out directory if requested.
  1034. if options.forceclean and os.path.exists(out_src_dir):
  1035. delete_directory(out_src_dir)
  1036. # Move the existing src/out directory to the correct location in the download
  1037. # directory. It will be moved back from the download directory later.
  1038. if os.path.exists(out_src_dir):
  1039. old_branch = read_branch_config_file(out_src_dir)
  1040. if old_branch != '' and (chromium_checkout_changed or
  1041. old_branch != cef_branch):
  1042. old_out_dir = os.path.join(download_dir, 'out_' + old_branch)
  1043. move_directory(out_src_dir, old_out_dir)
  1044. # Update the Chromium checkout.
  1045. if chromium_checkout_changed:
  1046. if not chromium_checkout_new and not options.fastupdate:
  1047. if options.forceclean and options.forcecleandeps:
  1048. # Remove all local changes including third-party git checkouts managed by
  1049. # gclient.
  1050. run("%s clean -dffx" % (git_exe), chromium_src_dir, depot_tools_dir)
  1051. else:
  1052. # Revert all changes in the Chromium checkout.
  1053. run("gclient revert --nohooks", chromium_dir, depot_tools_dir)
  1054. # Checkout the requested branch.
  1055. run("%s checkout %s%s" % \
  1056. (git_exe, '--force ' if discard_local_changes else '', chromium_checkout), \
  1057. chromium_src_dir, depot_tools_dir)
  1058. # Patch the Chromium DEPS file if necessary.
  1059. apply_deps_patch()
  1060. # Update third-party dependencies including branch/tag information.
  1061. run("gclient sync %s--nohooks --with_branch_heads --jobs 16" % \
  1062. ('--reset ' if discard_local_changes else ''), chromium_dir, depot_tools_dir)
  1063. # Patch the Chromium runhooks scripts if necessary.
  1064. apply_runhooks_patch()
  1065. # Runs hooks for files that have been modified in the local working copy.
  1066. run("gclient runhooks --jobs 16", chromium_dir, depot_tools_dir)
  1067. # Delete the src/out directory created by `gclient sync`.
  1068. delete_directory(out_src_dir)
  1069. if cef_dir == cef_src_dir:
  1070. # Running in fast update mode.
  1071. if cef_checkout_changed or chromium_checkout_changed:
  1072. # Check and restore the patched files.
  1073. run_patch_updater("--reapply --restore")
  1074. elif os.path.exists(cef_dir) and not os.path.exists(cef_src_dir):
  1075. # Restore the src/cef directory.
  1076. copy_directory(cef_dir, cef_src_dir)
  1077. # Restore the src/out directory.
  1078. out_src_dir_exists = os.path.exists(out_src_dir)
  1079. if os.path.exists(out_dir) and not out_src_dir_exists:
  1080. move_directory(out_dir, out_src_dir)
  1081. out_src_dir_exists = True
  1082. elif not out_src_dir_exists:
  1083. create_directory(out_src_dir)
  1084. # Write the config file for identifying the branch.
  1085. write_branch_config_file(out_src_dir, cef_branch)
  1086. if options.logchromiumchanges and chromium_checkout != chromium_compat_version:
  1087. log_chromium_changes()
  1088. if options.forcepatchupdate or ((chromium_checkout_new or not options.fastupdate) and \
  1089. chromium_checkout_changed and \
  1090. chromium_checkout != chromium_compat_version):
  1091. # Not using the known-compatible Chromium version. Try to update patch files.
  1092. if options.logchromiumchanges:
  1093. out_file = os.path.join(download_dir, 'chromium_update_patches.txt')
  1094. if os.path.exists(out_file):
  1095. os.remove(out_file)
  1096. else:
  1097. out_file = None
  1098. run_patch_updater(output_file=out_file)
  1099. elif options.resave:
  1100. # Resave patch files.
  1101. run_patch_updater("--resave")
  1102. if chromium_checkout != chromium_compat_version:
  1103. if options.logchromiumchanges:
  1104. out_file = os.path.join(download_dir, 'chromium_update_patterns.txt')
  1105. if os.path.exists(out_file):
  1106. os.remove(out_file)
  1107. else:
  1108. out_file = None
  1109. check_pattern_matches(output_file=out_file)
  1110. ##
  1111. # Build CEF.
  1112. ##
  1113. if not options.nobuild and (chromium_checkout_changed or \
  1114. cef_checkout_changed or options.forcebuild or \
  1115. not out_src_dir_exists):
  1116. # Building should also force a distribution.
  1117. options.forcedistrib = True
  1118. # Make sure the GN configuration exists.
  1119. if not options.dryrun and \
  1120. not os.path.exists(os.path.join(cef_src_dir, 'BUILD.gn')):
  1121. raise Exception('GN configuration does not exist.')
  1122. # Print all build-related environment variables including any that were set
  1123. # previously.
  1124. for key in os.environ.keys():
  1125. if key.startswith('CEF_') or key.startswith('GCLIENT_') or \
  1126. key.startswith('GN_') or key.startswith('GYP_') or \
  1127. key.startswith('DEPOT_TOOLS_'):
  1128. msg('%s=%s' % (key, os.environ[key]))
  1129. # Generate project files.
  1130. tool = os.path.join(cef_src_dir, 'tools', 'gclient_hook.py')
  1131. run('%s %s' % (python_exe, tool), cef_src_dir, depot_tools_dir)
  1132. # Build using Ninja.
  1133. command = 'ninja '
  1134. if options.verbosebuild:
  1135. command += '-v '
  1136. if options.buildfailurelimit != 1:
  1137. command += '-k %d ' % options.buildfailurelimit
  1138. command += '-C '
  1139. target = ' ' + options.buildtarget
  1140. if options.buildtests:
  1141. target += ' ' + options.testtarget
  1142. if platform == 'linux':
  1143. target += ' chrome_sandbox'
  1144. # Make a CEF Debug build.
  1145. if not options.nodebugbuild:
  1146. build_path = os.path.join('out', get_build_directory_name(True))
  1147. args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
  1148. msg(args_path + ' contents:\n' + read_file(args_path))
  1149. run(command + build_path + target, chromium_src_dir, depot_tools_dir,
  1150. os.path.join(download_dir, 'build-%s-debug.log' % (cef_branch)) \
  1151. if options.buildlogfile else None)
  1152. if platform in sandbox_lib_platforms:
  1153. # Make the separate cef_sandbox build when GN is_official_build=true.
  1154. build_path += '_sandbox'
  1155. if os.path.exists(os.path.join(chromium_src_dir, build_path)):
  1156. args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
  1157. msg(args_path + ' contents:\n' + read_file(args_path))
  1158. run(command + build_path + ' cef_sandbox', chromium_src_dir, depot_tools_dir,
  1159. os.path.join(download_dir, 'build-%s-debug-sandbox.log' % (cef_branch)) \
  1160. if options.buildlogfile else None)
  1161. # Make a CEF Release build.
  1162. if not options.noreleasebuild:
  1163. build_path = os.path.join('out', get_build_directory_name(False))
  1164. args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
  1165. msg(args_path + ' contents:\n' + read_file(args_path))
  1166. run(command + build_path + target, chromium_src_dir, depot_tools_dir,
  1167. os.path.join(download_dir, 'build-%s-release.log' % (cef_branch)) \
  1168. if options.buildlogfile else None)
  1169. if platform in sandbox_lib_platforms:
  1170. # Make the separate cef_sandbox build when GN is_official_build=true.
  1171. build_path += '_sandbox'
  1172. if os.path.exists(os.path.join(chromium_src_dir, build_path)):
  1173. args_path = os.path.join(chromium_src_dir, build_path, 'args.gn')
  1174. msg(args_path + ' contents:\n' + read_file(args_path))
  1175. run(command + build_path + ' cef_sandbox', chromium_src_dir, depot_tools_dir,
  1176. os.path.join(download_dir, 'build-%s-release-sandbox.log' % (cef_branch)) \
  1177. if options.buildlogfile else None)
  1178. elif not options.nobuild:
  1179. msg('Not building. The source hashes have not changed and ' +
  1180. 'the output folder "%s" already exists' % (out_src_dir))
  1181. ##
  1182. # Run CEF tests.
  1183. ##
  1184. if options.runtests:
  1185. if platform == 'windows':
  1186. test_exe = '%s.exe' % options.testtarget
  1187. elif platform == 'mac':
  1188. test_exe = '%s.app/Contents/MacOS/%s' % (options.testtarget,
  1189. options.testtarget)
  1190. elif platform == 'linux':
  1191. test_exe = options.testtarget
  1192. test_prefix = options.testprefix
  1193. if len(test_prefix) > 0:
  1194. test_prefix += ' '
  1195. test_args = options.testargs
  1196. if len(test_args) > 0:
  1197. test_args = ' ' + test_args
  1198. if not options.nodebugtests:
  1199. build_path = os.path.join(out_src_dir, get_build_directory_name(True))
  1200. test_path = os.path.join(build_path, test_exe)
  1201. if os.path.exists(test_path):
  1202. run(test_prefix + test_path + test_args, build_path, depot_tools_dir)
  1203. else:
  1204. msg('Not running debug tests. Missing executable: %s' % test_path)
  1205. if not options.noreleasetests:
  1206. build_path = os.path.join(out_src_dir, get_build_directory_name(False))
  1207. test_path = os.path.join(build_path, test_exe)
  1208. if os.path.exists(test_path):
  1209. run(test_prefix + test_path + test_args, build_path, depot_tools_dir)
  1210. else:
  1211. msg('Not running release tests. Missing executable: %s' % test_path)
  1212. ##
  1213. # Create the CEF binary distribution.
  1214. ##
  1215. if not options.nodistrib and (chromium_checkout_changed or \
  1216. cef_checkout_changed or options.forcedistrib):
  1217. if not options.forceclean and options.cleanartifacts:
  1218. # Clean the artifacts output directory.
  1219. artifacts_path = os.path.join(cef_src_dir, 'binary_distrib')
  1220. delete_directory(artifacts_path)
  1221. # Determine the requested distribution types.
  1222. distrib_types = []
  1223. if options.minimaldistribonly:
  1224. distrib_types.append('minimal')
  1225. elif options.clientdistribonly:
  1226. distrib_types.append('client')
  1227. elif options.sandboxdistribonly:
  1228. distrib_types.append('sandbox')
  1229. else:
  1230. distrib_types.append('standard')
  1231. if options.minimaldistrib:
  1232. distrib_types.append('minimal')
  1233. if options.clientdistrib:
  1234. distrib_types.append('client')
  1235. if options.sandboxdistrib:
  1236. distrib_types.append('sandbox')
  1237. cef_tools_dir = os.path.join(cef_src_dir, 'tools')
  1238. # Create the requested distribution types.
  1239. first_type = True
  1240. for type in distrib_types:
  1241. path = '%s make_distrib.py --output-dir=../binary_distrib/' % python_exe
  1242. if options.nodebugbuild or options.noreleasebuild or type != 'standard':
  1243. path += ' --allow-partial'
  1244. path = path + ' --ninja-build'
  1245. if options.x64build:
  1246. path += ' --x64-build'
  1247. elif options.armbuild:
  1248. path += ' --arm-build'
  1249. elif options.arm64build:
  1250. path += ' --arm64-build'
  1251. if type == 'minimal':
  1252. path += ' --minimal'
  1253. elif type == 'client':
  1254. path += ' --client'
  1255. elif type == 'sandbox':
  1256. path += ' --sandbox'
  1257. if first_type:
  1258. if options.nodistribdocs:
  1259. path += ' --no-docs'
  1260. if options.nodistribarchive:
  1261. path += ' --no-archive'
  1262. first_type = False
  1263. else:
  1264. # Don't create the symbol archives or documentation more than once.
  1265. path += ' --no-symbols --no-docs'
  1266. # Override the subdirectory name of binary_distrib if the caller requested.
  1267. if options.distribsubdir != '':
  1268. path += ' --distrib-subdir=' + options.distribsubdir
  1269. if options.distribsubdirsuffix != '':
  1270. path += ' --distrib-subdir-suffix=' + options.distribsubdirsuffix
  1271. # Create the distribution.
  1272. run(path, cef_tools_dir, depot_tools_dir)