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