blob: 262f1cb99a8b029c31b530204ffdddfa4ae6cf1d [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2018 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.
"""
Script to automatically add specified chromium dependency to WebRTC repo.
If you want to add new chromium owned dependency foo to the WebRTC repo
you have to run this tool like this:
./checkin_chromium_deps.py -d foo
It will check in dependency into third_party directory and will add it into
git index. Also it will update chromium dependencies list with new dependency
to insure that it will be correctly auto updated in future.
"""
import argparse
import errno
import json
import logging
import os.path
import shutil
import subprocess
import sys
import tempfile
REMOTE_URL = 'https://chromium.googlesource.com/chromium/src/third_party'
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
CHECKOUT_SRC_DIR = os.path.realpath(os.path.join(SCRIPT_DIR, os.pardir,
os.pardir))
class DependencyAlreadyCheckedIn(Exception):
pass
class DependencyNotFound(Exception):
pass
class Config(object):
def __init__(self, src_root, remote_url, temp_dir):
self.src_root = src_root
self.dependencies_file = os.path.join(self.src_root,
'THIRD_PARTY_CHROMIUM_DEPS.json')
self.deps_file = os.path.join(self.src_root, 'DEPS')
self.third_party_dir = os.path.join(self.src_root, 'third_party')
self.remote_url = remote_url
self.temp_dir = temp_dir
def VarLookup(local_scope):
return lambda var_name: local_scope['vars'][var_name]
def ParseDepsDict(deps_content):
local_scope = {}
global_scope = {
'Var': VarLookup(local_scope),
'deps_os': {},
}
exec (deps_content, global_scope, local_scope)
return local_scope
def ParseLocalDepsFile(filename):
with open(filename, 'rb') as f:
deps_content = f.read()
return ParseDepsDict(deps_content)
def RunCommand(command, working_dir=None, ignore_exit_code=False,
extra_env=None, input_data=None):
"""Runs a command and returns the output from that command.
If the command fails (exit code != 0), the function will exit the process.
Returns:
A tuple containing the stdout and stderr outputs as strings.
"""
working_dir = working_dir or CHECKOUT_SRC_DIR
logging.debug('CMD: %s CWD: %s', ' '.join(command), working_dir)
env = os.environ.copy()
if extra_env:
assert all(type(value) == str for value in extra_env.values())
logging.debug('extra env: %s', extra_env)
env.update(extra_env)
p = subprocess.Popen(command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=env,
cwd=working_dir, universal_newlines=True)
std_output, err_output = p.communicate(input_data)
p.stdout.close()
p.stderr.close()
if not ignore_exit_code and p.returncode != 0:
logging.error('Command failed: %s\n'
'stdout:\n%s\n'
'stderr:\n%s\n', ' '.join(command), std_output, err_output)
sys.exit(p.returncode)
return std_output, err_output
def LoadThirdPartyRevision(deps_file):
logging.debug('Loading chromium third_party revision from %s', deps_file)
webrtc_deps = ParseLocalDepsFile(deps_file)
return webrtc_deps['vars']['chromium_third_party_revision']
def CheckoutRequiredDependency(dep_name, config):
third_party_revision = LoadThirdPartyRevision(config.deps_file)
logging.debug('Initializing git repo in %s...', config.temp_dir)
RunCommand(['git', 'init'], working_dir=config.temp_dir)
logging.debug('Adding remote to %s. It may take some time...',
config.remote_url)
RunCommand(['git', 'remote', 'add', '-f', 'origin', config.remote_url],
working_dir=config.temp_dir)
logging.debug('Configuring sparse checkout...')
RunCommand(['git', 'config', 'core.sparseCheckout', 'true'],
working_dir=config.temp_dir)
sparse_checkout_config_path = os.path.join(config.temp_dir, '.git', 'info',
'sparse-checkout')
with open(sparse_checkout_config_path, 'wb') as f:
f.write(dep_name)
logging.debug('Pulling changes...')
_, stderr = RunCommand(['git', 'pull', 'origin', 'master'],
working_dir=config.temp_dir,
ignore_exit_code=True)
if "Sparse checkout leaves no entry on working directory" in stderr:
# There are no such dependency in chromium third_party
raise DependencyNotFound(
"Dependency %s not found in chromium repo" % dep_name)
logging.debug('Switching to revision %s...', third_party_revision)
RunCommand(['git', 'checkout', third_party_revision],
working_dir=config.temp_dir)
return os.path.join(config.temp_dir, dep_name)
def CopyDependency(dep_name, source_path, third_party_dir):
dest_path = os.path.join(third_party_dir, dep_name)
logging.debug('Copying dependency from %s to %s...', source_path, dest_path)
shutil.copytree(source_path, dest_path)
def AppendToChromiumOwnedDependenciesList(dep_name, dep_file):
with open(dep_file, 'rb') as f:
data = json.load(f)
dep_list = data.get('dependencies', [])
dep_list.append(dep_name)
data['dependencies'] = dep_list
with open(dep_file, 'wb') as f:
json.dump(data, f, indent=2, sort_keys=True, separators=(',', ': '))
def AddToGitIndex(dep_name, config):
logging.debug('Adding required changes to git index and commit set...')
dest_path = os.path.join(config.third_party_dir, dep_name)
RunCommand(['git', 'add', dest_path], working_dir=config.src_root)
RunCommand(['git', 'add', config.dependencies_file],
working_dir=config.src_root)
def CheckinDependency(dep_name, config):
dep_path = CheckoutRequiredDependency(dep_name, config)
CopyDependency(dep_name, dep_path, config.third_party_dir)
AppendToChromiumOwnedDependenciesList(dep_name, config.dependencies_file)
AddToGitIndex(dep_name, config)
logging.info('Successfully added %s.', dep_path)
logging.info('You now have to:')
logging.info('1. Check if you need to add an entry to DEPS (check if ')
logging.info(' Chromium has %s in its DEPS file and copy that)', dep_name)
logging.info('2. Copy third_party/.gitignore for %s from Chromium to\n'
' third_party/.gitignore', dep_name)
logging.info('3. Commit locally and upload.')
def DefaultConfig(temp_dir):
return Config(CHECKOUT_SRC_DIR, REMOTE_URL, temp_dir)
def CheckinDependencyWithNewTempDir(dep_name):
temp_dir = tempfile.mkdtemp()
try:
logging.info('Using temp directory: %s', temp_dir)
config = DefaultConfig(temp_dir)
CheckinDependency(dep_name, config)
finally:
shutil.rmtree(temp_dir)
def CheckDependencyNotCheckedIn(dep_name):
config = Config(CHECKOUT_SRC_DIR, REMOTE_URL, '')
with open(config.dependencies_file, 'rb') as f:
data = json.load(f)
dep_list = data.get('dependencies', [])
if dep_name in dep_list:
raise DependencyAlreadyCheckedIn("Dependency %s has been already checked "
"into WebRTC repo" % dep_name)
if dep_name in os.listdir(config.third_party_dir):
raise DependencyAlreadyCheckedIn("Directory for dependency %s already "
"exists in third_party" % dep_name)
def main():
p = argparse.ArgumentParser()
p.add_argument('-d', '--dependency', required=True,
help=('Name of chromium dependency to check in. For instance, '
'if you want to check in third_party/xyz, then pass '
'-d xyz.'))
p.add_argument('--temp-dir',
help='Temp working directory to use. By default the one '
'provided via tempfile will be used')
p.add_argument('-v', '--verbose', action='store_true', default=False,
help='Be extra verbose in printing of log messages.')
args = p.parse_args()
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
CheckDependencyNotCheckedIn(args.dependency)
if args.temp_dir:
if not os.path.exists(args.temp_dir):
# Raise system error "No such file or directory"
raise OSError(
errno.ENOENT, os.strerror(errno.ENOENT), args.temp_dir)
config = DefaultConfig(args.temp_dir)
CheckinDependency(args.dependency, config)
else:
CheckinDependencyWithNewTempDir(args.dependency)
return 0
if __name__ == '__main__':
sys.exit(main())