webrtc_m130/setup_links.py

528 lines
17 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
#
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
"""Setup links to a Chromium checkout for WebRTC.
WebRTC standalone shares a lot of dependencies and build tools with Chromium.
To do this, many of the paths of a Chromium checkout is emulated by creating
symlinks to files and directories. This script handles the setup of symlinks to
achieve this.
"""
import ctypes
import errno
import logging
import optparse
import os
import shelve
import shutil
import subprocess
import sys
import textwrap
DIRECTORIES = [
'build',
'buildtools',
'mojo', # TODO(kjellander): Remove, see webrtc:5629.
'testing',
'third_party/afl',
'third_party/binutils',
'third_party/boringssl',
'third_party/colorama',
'third_party/drmemory',
'third_party/expat',
'third_party/ffmpeg',
'third_party/instrumented_libraries',
'third_party/jsoncpp',
'third_party/libc++-static',
'third_party/libFuzzer',
'third_party/libjpeg',
'third_party/libjpeg_turbo',
'third_party/libsrtp',
'third_party/libvpx',
'third_party/libyuv',
'third_party/llvm-build',
'third_party/lss',
'third_party/nss',
'third_party/ocmock',
'third_party/openh264',
'third_party/openmax_dl',
'third_party/opus',
'third_party/proguard',
'third_party/protobuf',
'third_party/sqlite',
'third_party/syzygy',
'third_party/usrsctp',
'third_party/yasm',
'third_party/zlib',
'third_party/WebKit', # TODO(kjellander): Remove, see webrtc:5629.
'tools/clang',
'tools/generate_library_loader',
'tools/generate_stubs',
'tools/gn',
'tools/gyp',
'tools/luci-go',
Reland of Add tools/mb to setup_links.py (patchset #1 id:1 of https://codereview.webrtc.org/1691723003/ ) Reason for revert: Actually, we're going to need this soon, so I'm going to add it back again. We'll use it as part of the future GN migration. Original issue's description: > Revert of Add tools/mb to setup_links.py (patchset #1 id:1 of https://codereview.webrtc.org/1692543002/ ) > > Reason for revert: > Doesn't solve the problem and MB is actually hardcoded to use gyp_chromium when running in GYP mode, so it's better to clean this up for now. > > Original issue's description: > > Add tools/mb to setup_links.py > > > > I believe this is needed in order to setup the iOS simulator bots > > using the ios recipe module. We'll also most likely use MB in the future > > when moving over from GYP to GN (since that's the sole purpose of the tool: > > to make such a migration easier). > > > > BUG=chromium:498746 > > NOTRY=True > > TBR=phoglund@webrtc.org > > > > Committed: https://crrev.com/68da769ab9579cac98fe4da7fb5da6dbb3842216 > > Cr-Commit-Position: refs/heads/master@{#11575} > > TBR=phoglund@webrtc.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > BUG=chromium:498746 > > Committed: https://crrev.com/67ca0e16501d5c9179d3003a38a7cefc7cd3e259 > Cr-Commit-Position: refs/heads/master@{#11577} TBR=phoglund@webrtc.org NOTRY=True Review URL: https://codereview.webrtc.org/1730113002 Cr-Commit-Position: refs/heads/master@{#11732}
2016-02-24 00:04:44 -08:00
'tools/mb',
'tools/memory',
'tools/protoc_wrapper',
'tools/python',
'tools/swarming_client',
'tools/valgrind',
'tools/vim',
'tools/win',
'tools/xdisplaycheck',
]
from sync_chromium import get_target_os_list
target_os = get_target_os_list()
if 'android' in target_os:
DIRECTORIES += [
'base',
'third_party/accessibility_test_framework',
'third_party/android_platform',
'third_party/android_tools',
'third_party/apache_velocity',
Roll chromium_revision 8e72e1d..271c6cc (307131:309333) This enables OpenSSL by default for Windows, see https://chromium.googlesource.com/chromium/src/+/8e72e1d..271c6cc/build/common.gypi which required libjingle_tests.gyp to be updated since the targets in third_party/nss/nss.gyp was moved into a condition in https://codereview.chromium.org/694643002. New Android dependencies are required due to being introduced in build/android/pylib/remote/device/remote_device_test_run.py of https://chromium.googlesource.com/chromium/src/+/5c49978f095340a59c62faaafe02a9527ec7728b This should also fix Android test execution that started failing after https://codereview.chromium.org/815213002 was submitted, since it's based on https://chromium.googlesource.com/chromium/src/+/e2a338fac902ff391f761c67580b8de00d4adfdf Relevant other changes: * src/buildtools: 535aff2..23a4e2f * src/third_party/android_tools: 4f723e2..8fe116f * src/third_party/boringssl/src: 00505ec..306e520 * src/third_party/icu: 53ecf0f..51c1a4c * src/third_party/libvpx: 9fbec81..d3f3dce * src/tools/swarming_client: 1d4965c..119b084 Details: https://chromium.googlesource.com/chromium/src/+/8e72e1d..271c6cc/DEPS Clang version updated 218707:223108: https://chromium.googlesource.com/chromium/src/+/8e72e1d..271c6cc/tools/clang/scripts/update.sh Due to this, we had to disable deadlock detection for TSan due to a bug in Clang (see webrtc: BUG=4106 R=pbos@webrtc.org, pthatcher@webrtc.org Review URL: https://webrtc-codereview.appspot.com/36459004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@8003 4adac7df-926f-26a2-2b94-8c16560cd09d
2015-01-06 07:24:27 +00:00
'third_party/appurify-python',
'third_party/ashmem',
'third_party/bouncycastle',
'third_party/catapult',
Roll chromium_revision 48e079d573..33f87688e4 (407510:409700) Added symlink to third_party/closure_compiler to resolve Android GN error. Change log: https://chromium.googlesource.com/chromium/src/+log/48e079d573..33f87688e4 Full diff: https://chromium.googlesource.com/chromium/src/+/48e079d573..33f87688e4 Changed dependencies: * src/buildtools: https://chromium.googlesource.com/chromium/buildtools.git/+log/60f7f9a8b4..88c6fc5bde * src/third_party/boringssl/src: https://boringssl.googlesource.com/boringssl.git/+log/5440fe0cd1..0d1b0961f9 * src/third_party/ffmpeg: https://chromium.googlesource.com/chromium/third_party/ffmpeg.git/+log/24ea727552..4e878f7f64 * src/third_party/libsrtp: https://chromium.googlesource.com/chromium/deps/libsrtp.git/+log/720780acf8..48bdd208dc * src/third_party/libvpx/source/libvpx: https://chromium.googlesource.com/webm/libvpx.git/+log/4b073bc39a..82070ae939 * src/third_party/libyuv: https://chromium.googlesource.com/libyuv/libyuv.git/+log/e84dcb43bd..68786ccd53 * src/third_party/lss: https://chromium.googlesource.com/external/linux-syscall-support/lss.git/+log/4fc942258f..3f6478ac95 * src/third_party/opus/src: https://chromium.googlesource.com/chromium/deps/opus.git/+log/655cc54c56..aa32042a50 * src/third_party/usrsctp/usrsctplib: https://chromium.googlesource.com/external/github.com/sctplab/usrsctp/+log/c60ec8b35c..9a3e5465e9 * src/tools/swarming_client: https://chromium.googlesource.com/external/swarming.client.git/+log/7f63a272f7..e4288c3040 DEPS diff: https://chromium.googlesource.com/chromium/src/+/48e079d573..33f87688e4/DEPS No update to Clang. Skipping trybots since they already ran for https://codereview.webrtc.org/2210493004/ and this CL just adds a fix for Android GN (which trybots were run). TBR=marpan@webrtc.org, BUG=webrtc:6169 NOTRY=True Review-Url: https://codereview.webrtc.org/2215633002 Cr-Commit-Position: refs/heads/master@{#13632}
2016-08-04 00:00:37 -07:00
'third_party/closure_compiler',
'third_party/guava',
'third_party/hamcrest',
'third_party/icu',
'third_party/icu4j',
'third_party/ijar',
'third_party/intellij',
'third_party/jsr-305',
'third_party/junit',
'third_party/libxml',
'third_party/mockito',
'third_party/modp_b64',
'third_party/ow2_asm',
Roll chromium_revision 8e72e1d..271c6cc (307131:309333) This enables OpenSSL by default for Windows, see https://chromium.googlesource.com/chromium/src/+/8e72e1d..271c6cc/build/common.gypi which required libjingle_tests.gyp to be updated since the targets in third_party/nss/nss.gyp was moved into a condition in https://codereview.chromium.org/694643002. New Android dependencies are required due to being introduced in build/android/pylib/remote/device/remote_device_test_run.py of https://chromium.googlesource.com/chromium/src/+/5c49978f095340a59c62faaafe02a9527ec7728b This should also fix Android test execution that started failing after https://codereview.chromium.org/815213002 was submitted, since it's based on https://chromium.googlesource.com/chromium/src/+/e2a338fac902ff391f761c67580b8de00d4adfdf Relevant other changes: * src/buildtools: 535aff2..23a4e2f * src/third_party/android_tools: 4f723e2..8fe116f * src/third_party/boringssl/src: 00505ec..306e520 * src/third_party/icu: 53ecf0f..51c1a4c * src/third_party/libvpx: 9fbec81..d3f3dce * src/tools/swarming_client: 1d4965c..119b084 Details: https://chromium.googlesource.com/chromium/src/+/8e72e1d..271c6cc/DEPS Clang version updated 218707:223108: https://chromium.googlesource.com/chromium/src/+/8e72e1d..271c6cc/tools/clang/scripts/update.sh Due to this, we had to disable deadlock detection for TSan due to a bug in Clang (see webrtc: BUG=4106 R=pbos@webrtc.org, pthatcher@webrtc.org Review URL: https://webrtc-codereview.appspot.com/36459004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@8003 4adac7df-926f-26a2-2b94-8c16560cd09d
2015-01-06 07:24:27 +00:00
'third_party/requests',
'third_party/robolectric',
'third_party/sqlite4java',
'third_party/tcmalloc',
'tools/android',
'tools/grit',
'tools/telemetry',
]
else:
DIRECTORIES += [
'base/third_party/libevent',
]
if 'ios' in target_os:
DIRECTORIES.append('third_party/class-dump')
FILES = {
'tools/isolate_driver.py': None,
'third_party/BUILD.gn': None,
}
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
CHROMIUM_CHECKOUT = os.path.join('chromium', 'src')
LINKS_DB = 'links'
# Version management to make future upgrades/downgrades easier to support.
SCHEMA_VERSION = 1
def query_yes_no(question, default=False):
"""Ask a yes/no question via raw_input() and return their answer.
Modified from http://stackoverflow.com/a/3041990.
"""
prompt = " [%s/%%s]: "
prompt = prompt % ('Y' if default is True else 'y')
prompt = prompt % ('N' if default is False else 'n')
if default is None:
default = 'INVALID'
while True:
sys.stdout.write(question + prompt)
choice = raw_input().lower()
if choice == '' and default != 'INVALID':
return default
if 'yes'.startswith(choice):
return True
elif 'no'.startswith(choice):
return False
print "Please respond with 'yes' or 'no' (or 'y' or 'n')."
# Actions
class Action(object):
def __init__(self, dangerous):
self.dangerous = dangerous
def announce(self, planning):
"""Log a description of this action.
Args:
planning - True iff we're in the planning stage, False if we're in the
doit stage.
"""
pass
def doit(self, links_db):
"""Execute the action, recording what we did to links_db, if necessary."""
pass
class Remove(Action):
def __init__(self, path, dangerous):
super(Remove, self).__init__(dangerous)
self._priority = 0
self._path = path
def announce(self, planning):
log = logging.warn
filesystem_type = 'file'
if not self.dangerous:
log = logging.info
filesystem_type = 'link'
if planning:
log('Planning to remove %s: %s', filesystem_type, self._path)
else:
log('Removing %s: %s', filesystem_type, self._path)
def doit(self, _):
os.remove(self._path)
class Rmtree(Action):
def __init__(self, path):
super(Rmtree, self).__init__(dangerous=True)
self._priority = 0
self._path = path
def announce(self, planning):
if planning:
logging.warn('Planning to remove directory: %s', self._path)
else:
logging.warn('Removing directory: %s', self._path)
def doit(self, _):
if sys.platform.startswith('win'):
# shutil.rmtree() doesn't work on Windows if any of the directories are
# read-only, which svn repositories are.
subprocess.check_call(['rd', '/q', '/s', self._path], shell=True)
else:
shutil.rmtree(self._path)
class Makedirs(Action):
def __init__(self, path):
super(Makedirs, self).__init__(dangerous=False)
self._priority = 1
self._path = path
def doit(self, _):
try:
os.makedirs(self._path)
except OSError as e:
if e.errno != errno.EEXIST:
raise
class Symlink(Action):
def __init__(self, source_path, link_path):
super(Symlink, self).__init__(dangerous=False)
self._priority = 2
self._source_path = source_path
self._link_path = link_path
def announce(self, planning):
if planning:
logging.info(
'Planning to create link from %s to %s', self._link_path,
self._source_path)
else:
logging.debug(
'Linking from %s to %s', self._link_path, self._source_path)
def doit(self, links_db):
# Files not in the root directory need relative path calculation.
# On Windows, use absolute paths instead since NTFS doesn't seem to support
# relative paths for symlinks.
if sys.platform.startswith('win'):
source_path = os.path.abspath(self._source_path)
else:
if os.path.dirname(self._link_path) != self._link_path:
source_path = os.path.relpath(self._source_path,
os.path.dirname(self._link_path))
os.symlink(source_path, os.path.abspath(self._link_path))
links_db[self._source_path] = self._link_path
class LinkError(IOError):
"""Failed to create a link."""
pass
# Use junctions instead of symlinks on the Windows platform.
if sys.platform.startswith('win'):
def symlink(source_path, link_path):
if os.path.isdir(source_path):
subprocess.check_call(['cmd.exe', '/c', 'mklink', '/J', link_path,
source_path])
else:
# Don't create symlinks to files on Windows, just copy the file instead
# (there's no way to create a link without administrator's privileges).
shutil.copy(source_path, link_path)
os.symlink = symlink
class WebRTCLinkSetup(object):
def __init__(self, links_db, force=False, dry_run=False, prompt=False):
self._force = force
self._dry_run = dry_run
self._prompt = prompt
self._links_db = links_db
def CreateLinks(self, on_bot):
logging.debug('CreateLinks')
# First, make a plan of action
actions = []
for source_path, link_path in FILES.iteritems():
actions += self._ActionForPath(
source_path, link_path, check_fn=os.path.isfile, check_msg='files')
for source_dir in DIRECTORIES:
actions += self._ActionForPath(
source_dir, None, check_fn=os.path.isdir,
check_msg='directories')
if not on_bot and self._force:
# When making the manual switch from legacy SVN checkouts to the new
# Git-based Chromium DEPS, the .gclient_entries file that contains cached
# URLs for all DEPS entries must be removed to avoid future sync problems.
entries_file = os.path.join(os.path.dirname(ROOT_DIR), '.gclient_entries')
if os.path.exists(entries_file):
actions.append(Remove(entries_file, dangerous=True))
actions.sort()
if self._dry_run:
for action in actions:
action.announce(planning=True)
logging.info('Not doing anything because dry-run was specified.')
sys.exit(0)
if any(a.dangerous for a in actions):
logging.warn('Dangerous actions:')
for action in (a for a in actions if a.dangerous):
action.announce(planning=True)
print
if not self._force:
logging.error(textwrap.dedent("""\
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
A C T I O N R E Q I R E D
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Setting up the checkout requires creating symlinks to directories in the
Chromium checkout inside chromium/src.
To avoid disrupting developers, we've chosen to not delete directories
forcibly, in case you have some work in progress in one of them :)
ACTION REQUIRED:
Before running `gclient sync|runhooks` again, you must run:
%s%s --force
Which will replace all directories which now must be symlinks, after
prompting with a summary of the work-to-be-done.
"""), 'python ' if sys.platform.startswith('win') else '', __file__)
sys.exit(1)
elif self._prompt:
if not query_yes_no('Would you like to perform the above plan?'):
sys.exit(1)
for action in actions:
action.announce(planning=False)
action.doit(self._links_db)
if not on_bot and self._force:
logging.info('Completed!\n\nNow run `gclient sync|runhooks` again to '
'let the remaining hooks (that probably were interrupted) '
'execute.')
def CleanupLinks(self):
logging.debug('CleanupLinks')
for source, link_path in self._links_db.iteritems():
if source == 'SCHEMA_VERSION':
continue
if os.path.islink(link_path) or sys.platform.startswith('win'):
# os.path.islink() always returns false on Windows
# See http://bugs.python.org/issue13143.
logging.debug('Removing link to %s at %s', source, link_path)
if not self._dry_run:
if os.path.exists(link_path):
if sys.platform.startswith('win') and os.path.isdir(link_path):
subprocess.check_call(['rmdir', '/q', '/s', link_path],
shell=True)
else:
os.remove(link_path)
del self._links_db[source]
@staticmethod
def _ActionForPath(source_path, link_path=None, check_fn=None,
check_msg=None):
"""Create zero or more Actions to link to a file or directory.
This will be a symlink on POSIX platforms. On Windows it will result in:
* a junction for directories
* a copied file for single files.
Args:
source_path: Path relative to the Chromium checkout root.
For readability, the path may contain slashes, which will
automatically be converted to the right path delimiter on Windows.
link_path: The location for the link to create. If omitted it will be the
same path as source_path.
check_fn: A function returning true if the type of filesystem object is
correct for the attempted call. Otherwise an error message with
check_msg will be printed.
check_msg: String used to inform the user of an invalid attempt to create
a file.
Returns:
A list of Action objects.
"""
def fix_separators(path):
if sys.platform.startswith('win'):
return path.replace(os.altsep, os.sep)
else:
return path
assert check_fn
assert check_msg
link_path = link_path or source_path
link_path = fix_separators(link_path)
source_path = fix_separators(source_path)
source_path = os.path.join(CHROMIUM_CHECKOUT, source_path)
if os.path.exists(source_path) and not check_fn:
raise LinkError('Can only to link to %s: tried to link to: %s' %
(check_msg, source_path))
if not os.path.exists(source_path):
logging.debug('Silently ignoring missing source: %s. This is to avoid '
'errors on platform-specific dependencies.', source_path)
return []
actions = []
if os.path.exists(link_path) or os.path.islink(link_path):
if os.path.islink(link_path):
actions.append(Remove(link_path, dangerous=False))
elif os.path.isfile(link_path):
actions.append(Remove(link_path, dangerous=True))
elif os.path.isdir(link_path):
actions.append(Rmtree(link_path))
else:
raise LinkError('Don\'t know how to plan: %s' % link_path)
# Create parent directories to the target link if needed.
target_parent_dirs = os.path.dirname(link_path)
if (target_parent_dirs and
target_parent_dirs != link_path and
not os.path.exists(target_parent_dirs)):
actions.append(Makedirs(target_parent_dirs))
actions.append(Symlink(source_path, link_path))
return actions
def _initialize_database(filename):
links_database = shelve.open(filename)
# Wipe the database if this version of the script ends up looking at a
# newer (future) version of the links db, just to be sure.
version = links_database.get('SCHEMA_VERSION')
if version and version != SCHEMA_VERSION:
logging.info('Found database with schema version %s while this script only '
'supports %s. Wiping previous database contents.', version,
SCHEMA_VERSION)
links_database.clear()
links_database['SCHEMA_VERSION'] = SCHEMA_VERSION
return links_database
def main():
on_bot = os.environ.get('CHROME_HEADLESS') == '1'
parser = optparse.OptionParser()
parser.add_option('-d', '--dry-run', action='store_true', default=False,
help='Print what would be done, but don\'t perform any '
'operations. This will automatically set logging to '
'verbose.')
parser.add_option('-c', '--clean-only', action='store_true', default=False,
help='Only clean previously created links, don\'t create '
'new ones. This will automatically set logging to '
'verbose.')
parser.add_option('-f', '--force', action='store_true', default=on_bot,
help='Force link creation. CAUTION: This deletes existing '
'folders and files in the locations where links are '
'about to be created.')
parser.add_option('-n', '--no-prompt', action='store_false', dest='prompt',
default=(not on_bot),
help='Prompt if we\'re planning to do a dangerous action')
parser.add_option('-v', '--verbose', action='store_const',
const=logging.DEBUG, default=logging.INFO,
help='Print verbose output for debugging.')
options, _ = parser.parse_args()
if options.dry_run or options.force or options.clean_only:
options.verbose = logging.DEBUG
logging.basicConfig(format='%(message)s', level=options.verbose)
# Work from the root directory of the checkout.
script_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(script_dir)
if sys.platform.startswith('win'):
def is_admin():
try:
return os.getuid() == 0
except AttributeError:
return ctypes.windll.shell32.IsUserAnAdmin() != 0
if is_admin():
logging.warning('WARNING: On Windows, you no longer need run as '
'administrator. Please run with user account privileges.')
if not os.path.exists(CHROMIUM_CHECKOUT):
logging.error('Cannot find a Chromium checkout at %s. Did you run "gclient '
'sync" before running this script?', CHROMIUM_CHECKOUT)
return 2
links_database = _initialize_database(LINKS_DB)
try:
symlink_creator = WebRTCLinkSetup(links_database, options.force,
options.dry_run, options.prompt)
symlink_creator.CleanupLinks()
if not options.clean_only:
symlink_creator.CreateLinks(on_bot)
except LinkError as e:
print >> sys.stderr, e.message
return 3
finally:
links_database.close()
return 0
if __name__ == '__main__':
sys.exit(main())