| #!/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) |