blob: b00464fc31a5ac40e887ae03634ca389bad11915 [file] [log] [blame]
#!/usr/bin/python
__author__ = 'shenhan@google.com (Han Shen)'
import optparse
import os
import re
import repo_to_repo
import sys
from utils import command_executer
from utils import logger
from utils import misc
GCC_REPO_PATH='src/third_party/gcc'
CHROMIUMOS_OVERLAY_PATH='src/third_party/chromiumos-overlay'
GCC_EBUILD_PATH='src/third_party/chromiumos-overlay/sys-devel/gcc'
class Bootstrapper(object):
def __init__(self, chromeos_root, branch=None, gcc_dir=None,
setup_gcc_ebuild_file_only=False):
self._chromeos_root = chromeos_root
self._branch = branch
self._gcc_dir = gcc_dir
self._ce = command_executer.GetCommandExecuter()
self._logger = logger.GetLogger()
self._gcc_src_dir = None
self._branch_tree = None
self._gcc_ebuild_file = None
self._gcc_ebuild_file_name = None
self._setup_gcc_ebuild_file_only = setup_gcc_ebuild_file_only
def SubmitToLocalBranch(self):
# If "_branch" is set, we just use it.
if self._branch:
return True
# The next few steps creates an internal branch to sync with the gcc dir
# user provided.
self._branch = 'internal_testing_branch_no_use'
chrome_gcc_dir = os.path.join(
self._chromeos_root, GCC_REPO_PATH)
# 0. Test to see if git tree is free of local changes.
if not misc.IsGitTreeClean(chrome_gcc_dir):
self._logger.LogError(
'Git repository "{0}" not clean, aborted.'.format(chrome_gcc_dir))
return False
# 1. Checkout/create a (new) branch for testing.
command = 'cd "{0}" && git checkout -B {1} cros/master'.format(
chrome_gcc_dir, self._branch)
ret = self._ce.RunCommand(command)
if ret:
self._logger.LogError('Failed to create a temp branch for test, aborted.')
return False
# 2. Sync sources from user provided gcc dir to chromiumos gcc git.
local_gcc_repo = repo_to_repo.FileRepo(self._gcc_dir)
chrome_gcc_repo = repo_to_repo.GitRepo(chrome_gcc_dir, self._branch)
chrome_gcc_repo._root_dir = chrome_gcc_dir
# Delete all stuff before start mapping.
self._ce.RunCommand('cd {0} && rm -rf *'.format(chrome_gcc_dir))
local_gcc_repo.MapSources(chrome_gcc_repo.GetRoot())
# 3. Verify sync successfully.
diff = 'diff -r -x .git -x .svn "{0}" "{1}"'.format(
self._gcc_dir, chrome_gcc_dir)
if self._ce.RunCommand(diff):
self._logger.LogError('Sync not successfully, aborted.')
return False
else:
self._logger.LogOutput('Sync successfully done.')
# 4. Commit all changes.
ret = chrome_gcc_repo.CommitLocally(
'Synced with gcc source tree at - "{0}".'.format(self._gcc_dir))
if ret:
self._logger.LogError('Commit to local branch "{0}" failed, aborted.'.
format(self._branch))
return False
return True
def CheckoutBranch(self):
self._gcc_src_dir = os.path.join(self._chromeos_root, GCC_REPO_PATH)
command = 'cd "{0}" && git checkout {1}'.format(
self._gcc_src_dir, self._branch)
if not self._ce.RunCommand(command, print_to_console=True):
# Get 'TREE' value of this commit
command = 'cd "{0}" && git cat-file -p {1} ' \
'| grep -E "^tree [a-f0-9]+$" | cut -d" " -f2'.format(
self._gcc_src_dir, self._branch)
ret, stdout, stderr = self._ce.RunCommand(
command, return_output=True, print_to_console=False)
# Pipe operation always has a zero return value. So need to check if
# stdout is valid.
if not ret and stdout and \
re.match('[0-9a-h]{40}', stdout.strip(), re.IGNORECASE):
self._branch_tree = stdout.strip()
self._logger.LogOutput('Find tree for branch "{0}" - "{1}"'.format(
self._branch, self._branch_tree))
return True
self._logger.LogError(
'Failed to checkout "{0}" or failed to get tree value, aborted.'.format(
self._branch))
return False
def FindGccEbuildFile(self):
# To get the active gcc ebuild file, we need a workable chroot first.
if not os.path.exists(os.path.join(self._chromeos_root, 'chroot')) and \
self._ce.RunCommand('cd "{0}" && cros_sdk --create'.format(
self._chromeos_root)):
self._logger.LogError(
('Failed to instal a initial chroot, aborted.\n'
'If previous bootstrap failed, do a "cros_sdk --delete" to remove '
'in-complete chroot.'))
return False
rv, stdout, stderr = self._ce.ChrootRunCommand(self._chromeos_root,
'equery w sys-devel/gcc', return_output=True, print_to_console=True)
if rv:
self._logger.LogError('Failed to execute inside chroot '
'"equery w sys-devel/gcc", aborted.')
return False
m = re.match('^.*/({0}/(.*\.ebuild))$'.format(GCC_EBUILD_PATH), stdout)
if not m:
self._logger.LogError(
('Failed to find gcc ebuild file, aborted. '
'If previous bootstrap failed, do a "cros_sdk --delete" to remove '
'in-complete chroot.'))
return False
self._gcc_ebuild_file = os.path.join(self._chromeos_root, m.group(1))
self._gcc_ebuild_file_name = m.group(2)
return True
def InplaceModifyEbuildFile(self):
"""Using sed to fill properly the values into the following lines -
CROS_WORKON_COMMIT="..."
CROS_WORKON_TREE="..."
"""
command = 'sed -i ' \
'-e \'s!^CROS_WORKON_COMMIT=".*"$!CROS_WORKON_COMMIT="{0}"!\' ' \
'-e \'s!^CROS_WORKON_TREE=".*"$!CROS_WORKON_TREE="{1}"!\' {2}'.format(
self._branch, self._branch_tree, self._gcc_ebuild_file)
rv = self._ce.RunCommand(command)
if rv:
self._logger.LogError(
'Failed to modify commit and tree value for "{0}"", aborted.'.format(
self._gcc_ebuild_file))
return False
return True
def DoBootstrapping(self):
logfile = os.path.join(self._chromeos_root, 'bootstrap.log')
command = 'cd "{0}" && cros_sdk --delete --bootstrap |& tee "{1}"'. \
format(self._chromeos_root, logfile)
rv = self._ce.RunCommand(command, \
return_output=False, print_to_console=True)
if rv:
self._logger.LogError('Bootstrapping failed, log file - "{0}"\n'.format(
logfile))
return False
self._logger.LogOutput('Bootstrap succeeded.')
return True
def Do(self):
if self.SubmitToLocalBranch() and \
self.CheckoutBranch() and \
self.FindGccEbuildFile() and \
self.InplaceModifyEbuildFile() and \
(self._setup_gcc_ebuild_file_only or self.DoBootstrapping()):
ret = True
else:
ret = False
## Warn that the ebuild file is modified.
if self._gcc_ebuild_file:
self._logger.LogWarning(
('Gcc ebuild file is (probably) modified, to revert the file - \n'
'bootstrap_compiler.py --chromeos={0} --reset_gcc_ebuild_file').format(
self._chromeos_root))
return ret
def Main(argv):
parser = optparse.OptionParser()
parser.add_option('-c', '--chromeos_root', dest='chromeos_root',
help=('ChromeOs root dir.'))
parser.add_option('-b', '--branch', dest='branch',
help=('The branch to test against. '
'This branch must be a local branch '
'inside "src/third_party/gcc". '
'Notice, this must not be used with "--gcc".'))
parser.add_option('-g', '--gcc_dir', dest='gcc_dir',
help=('Use a local gcc tree to do bootstrapping. '
'Notice, this must not be used with "--branch".'))
parser.add_option('--fixperm', dest='fixperm',
default=False, action='store_true',
help=('Fix the (notorious) permission error '
'while trying to bootstrap the chroot. '
'Note this takes an extra 10-15 minutes '
'and is only needed once per chromiumos tree.'))
parser.add_option('--setup_gcc_ebuild_file_only',
dest='setup_gcc_ebuild_file_only',
default=False, action='store_true',
help=('Setup gcc ebuild file to pick up the '
'branch (--branch) or user gcc source (--gcc_dir) '
'and exit. Keep chroot as is.'))
parser.add_option('--reset_gcc_ebuild_file', dest='reset_gcc_ebuild_file',
default=False, action='store_true',
help=('Reset the modification that is done by this script.'
'Note, when this script is running, it will modify '
'the active gcc ebuild file. Use this option to '
'reset (what this script has done) and exit.'))
options = parser.parse_args(argv)[0]
if not options.chromeos_root:
parser.error('Missing mandatory option "--chromeos".')
return 1
options.chromeos_root = os.path.abspath(
os.path.expanduser(options.chromeos_root))
if not os.path.isdir(options.chromeos_root):
logger.GetLogger().LogError(
'"{0}" does not exist.'.format(options.chromeos_root))
return 1
if options.fixperm:
# Fix perm error before continuing.
cmd = ('sudo find "{0}" \( -name ".cache" -type d -prune \) -o ' + \
'\( -name "chroot" -type d -prune \) -o ' + \
'\( -type f -exec chmod a+r {{}} \; \) -o ' + \
'\( -type d -exec chmod a+rx {{}} \; \)').format(
options.chromeos_root)
logger.GetLogger().LogOutput(
'Fixing perm issues for chromeos root, this might take some time.')
command_executer.GetCommandExecuter().RunCommand(cmd)
if options.reset_gcc_ebuild_file:
if options.gcc_dir or options.branch:
logger.GetLogger().LogWarning('Ignoring "--gcc_dir" or "--branch".')
if options.setup_gcc_ebuild_file_only:
logger.GetLogger().LogError(
('Conflict options "--reset_gcc_ebuild_file" '
'and "--setup_gcc_ebuild_file_only".'))
return 1
# Reset gcc ebuild file and exit.
rv = misc.GetGitChangesAsList(
os.path.join(options.chromeos_root,CHROMIUMOS_OVERLAY_PATH),
path='sys-devel/gcc/gcc-*.ebuild',
staged=False)
if rv:
cmd = 'cd {0} && git checkout --'.format(os.path.join(
options.chromeos_root, CHROMIUMOS_OVERLAY_PATH))
for g in rv:
cmd += ' ' + g
rv = command_executer.GetCommandExecuter().RunCommand(cmd)
if rv:
logger.GetLogger().LogWarning('Failed to reset gcc ebuild file.')
return rv
else:
logger.GetLogger().LogWarning(
'Did not find any modified gcc ebuild file.')
return 1
if options.gcc_dir:
options.gcc_dir = os.path.abspath(os.path.expanduser(options.gcc_dir))
if not os.path.isdir(options.gcc_dir):
logger.GetLogger().LogError(
'"{0}" does not exist.'.format(options.gcc_dir))
return 1
if options.branch and options.gcc_dir:
parser.error('Only one of "--gcc" and "--branch" can be specified.')
return 1
if not (options.branch or options.gcc_dir):
parser.error('At least one of "--gcc" and "--branch" must be specified.')
return 1
if Bootstrapper(
options.chromeos_root, branch=options.branch, gcc_dir=options.gcc_dir,
setup_gcc_ebuild_file_only=options.setup_gcc_ebuild_file_only).Do():
return 0
return 1
if __name__ == '__main__':
retval = Main(sys.argv)
sys.exit(retval)