Han Shen | 9144532 | 2013-03-20 13:43:31 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | __author__ = 'shenhan@google.com (Han Shen)' |
| 4 | |
| 5 | import optparse |
| 6 | import os |
| 7 | import re |
| 8 | import repo_to_repo |
| 9 | import sys |
| 10 | |
| 11 | from utils import command_executer |
| 12 | from utils import logger |
| 13 | from utils import misc |
| 14 | |
| 15 | GCC_REPO_PATH='src/third_party/gcc' |
| 16 | CHROMIUMOS_OVERLAY_PATH='src/third_party/chromiumos-overlay' |
| 17 | GCC_EBUILD_PATH='src/third_party/chromiumos-overlay/sys-devel/gcc' |
| 18 | |
| 19 | class Bootstrapper(object): |
| 20 | def __init__(self, chromeos_root, branch=None, gcc_dir=None, |
| 21 | setup_gcc_ebuild_file_only=False): |
| 22 | self._chromeos_root = chromeos_root |
| 23 | self._branch = branch |
| 24 | self._gcc_dir = gcc_dir |
| 25 | self._ce = command_executer.GetCommandExecuter() |
| 26 | self._logger = logger.GetLogger() |
| 27 | self._gcc_src_dir = None |
| 28 | self._branch_tree = None |
| 29 | self._gcc_ebuild_file = None |
| 30 | self._gcc_ebuild_file_name = None |
| 31 | self._setup_gcc_ebuild_file_only = setup_gcc_ebuild_file_only |
| 32 | |
| 33 | def SubmitToLocalBranch(self): |
| 34 | # If "_branch" is set, we just use it. |
| 35 | if self._branch: |
| 36 | return True |
| 37 | |
| 38 | # The next few steps creates an internal branch to sync with the gcc dir |
| 39 | # user provided. |
| 40 | self._branch = 'internal_testing_branch_no_use' |
| 41 | chrome_gcc_dir = os.path.join( |
| 42 | self._chromeos_root, GCC_REPO_PATH) |
| 43 | |
| 44 | # 0. Test to see if git tree is free of local changes. |
| 45 | if not misc.IsGitTreeClean(chrome_gcc_dir): |
| 46 | self._logger.LogError( |
| 47 | 'Git repository "{0}" not clean, aborted.'.format(chrome_gcc_dir)) |
| 48 | return False |
| 49 | |
| 50 | # 1. Checkout/create a (new) branch for testing. |
| 51 | command = 'cd "{0}" && git checkout -B {1} cros/master'.format( |
| 52 | chrome_gcc_dir, self._branch) |
| 53 | ret = self._ce.RunCommand(command) |
| 54 | if ret: |
| 55 | self._logger.LogError('Failed to create a temp branch for test, aborted.') |
| 56 | return False |
| 57 | |
| 58 | # 2. Sync sources from user provided gcc dir to chromiumos gcc git. |
| 59 | local_gcc_repo = repo_to_repo.FileRepo(self._gcc_dir) |
| 60 | chrome_gcc_repo = repo_to_repo.GitRepo(chrome_gcc_dir, self._branch) |
| 61 | chrome_gcc_repo._root_dir = chrome_gcc_dir |
| 62 | # Delete all stuff before start mapping. |
| 63 | self._ce.RunCommand('cd {0} && rm -rf *'.format(chrome_gcc_dir)) |
| 64 | local_gcc_repo.MapSources(chrome_gcc_repo.GetRoot()) |
| 65 | |
| 66 | # 3. Verify sync successfully. |
| 67 | diff = 'diff -r -x .git -x .svn "{0}" "{1}"'.format( |
| 68 | self._gcc_dir, chrome_gcc_dir) |
| 69 | if self._ce.RunCommand(diff): |
| 70 | self._logger.LogError('Sync not successfully, aborted.') |
| 71 | return False |
| 72 | else: |
| 73 | self._logger.LogOutput('Sync successfully done.') |
| 74 | |
| 75 | # 4. Commit all changes. |
| 76 | ret = chrome_gcc_repo.CommitLocally( |
| 77 | 'Synced with gcc source tree at - "{0}".'.format(self._gcc_dir)) |
| 78 | if ret: |
| 79 | self._logger.LogError('Commit to local branch "{0}" failed, aborted.'. |
| 80 | format(self._branch)) |
| 81 | return False |
| 82 | return True |
| 83 | |
| 84 | def CheckoutBranch(self): |
| 85 | self._gcc_src_dir = os.path.join(self._chromeos_root, GCC_REPO_PATH) |
| 86 | command = 'cd "{0}" && git checkout {1}'.format( |
| 87 | self._gcc_src_dir, self._branch) |
| 88 | if not self._ce.RunCommand(command, print_to_console=True): |
| 89 | # Get 'TREE' value of this commit |
| 90 | command = 'cd "{0}" && git cat-file -p {1} ' \ |
| 91 | '| grep -E "^tree [a-f0-9]+$" | cut -d" " -f2'.format( |
| 92 | self._gcc_src_dir, self._branch) |
| 93 | ret, stdout, stderr = self._ce.RunCommand( |
| 94 | command, return_output=True, print_to_console=False) |
| 95 | # Pipe operation always has a zero return value. So need to check if |
| 96 | # stdout is valid. |
| 97 | if not ret and stdout and \ |
| 98 | re.match('[0-9a-h]{40}', stdout.strip(), re.IGNORECASE): |
| 99 | self._branch_tree = stdout.strip() |
| 100 | self._logger.LogOutput('Find tree for branch "{0}" - "{1}"'.format( |
| 101 | self._branch, self._branch_tree)) |
| 102 | return True |
| 103 | self._logger.LogError( |
| 104 | 'Failed to checkout "{0}" or failed to get tree value, aborted.'.format( |
| 105 | self._branch)) |
| 106 | return False |
| 107 | |
| 108 | def FindGccEbuildFile(self): |
| 109 | # To get the active gcc ebuild file, we need a workable chroot first. |
| 110 | if not os.path.exists(os.path.join(self._chromeos_root, 'chroot')) and \ |
| 111 | self._ce.RunCommand('cd "{0}" && cros_sdk --create'.format( |
| 112 | self._chromeos_root)): |
| 113 | self._logger.LogError( |
| 114 | ('Failed to instal a initial chroot, aborted.\n' |
| 115 | 'If previous bootstrap failed, do a "cros_sdk --delete" to remove ' |
| 116 | 'in-complete chroot.')) |
| 117 | return False |
| 118 | |
| 119 | rv, stdout, stderr = self._ce.ChrootRunCommand(self._chromeos_root, |
| 120 | 'equery w sys-devel/gcc', return_output=True, print_to_console=True) |
| 121 | if rv: |
| 122 | self._logger.LogError('Failed to execute inside chroot ' |
| 123 | '"equery w sys-devel/gcc", aborted.') |
| 124 | return False |
| 125 | m = re.match('^.*/({0}/(.*\.ebuild))$'.format(GCC_EBUILD_PATH), stdout) |
| 126 | if not m: |
| 127 | self._logger.LogError( |
| 128 | ('Failed to find gcc ebuild file, aborted. ' |
| 129 | 'If previous bootstrap failed, do a "cros_sdk --delete" to remove ' |
| 130 | 'in-complete chroot.')) |
| 131 | return False |
| 132 | self._gcc_ebuild_file = os.path.join(self._chromeos_root, m.group(1)) |
| 133 | self._gcc_ebuild_file_name = m.group(2) |
| 134 | return True |
| 135 | |
| 136 | def InplaceModifyEbuildFile(self): |
| 137 | """Using sed to fill properly the values into the following lines - |
| 138 | CROS_WORKON_COMMIT="..." |
| 139 | CROS_WORKON_TREE="..." |
| 140 | """ |
| 141 | command = 'sed -i ' \ |
| 142 | '-e \'s!^CROS_WORKON_COMMIT=".*"$!CROS_WORKON_COMMIT="{0}"!\' ' \ |
| 143 | '-e \'s!^CROS_WORKON_TREE=".*"$!CROS_WORKON_TREE="{1}"!\' {2}'.format( |
| 144 | self._branch, self._branch_tree, self._gcc_ebuild_file) |
| 145 | rv = self._ce.RunCommand(command) |
| 146 | if rv: |
| 147 | self._logger.LogError( |
| 148 | 'Failed to modify commit and tree value for "{0}"", aborted.'.format( |
| 149 | self._gcc_ebuild_file)) |
| 150 | return False |
| 151 | return True |
| 152 | |
| 153 | def DoBootstrapping(self): |
| 154 | logfile = os.path.join(self._chromeos_root, 'bootstrap.log') |
| 155 | command = 'cd "{0}" && cros_sdk --delete --bootstrap |& tee "{1}"'. \ |
| 156 | format(self._chromeos_root, logfile) |
| 157 | rv = self._ce.RunCommand(command, \ |
| 158 | return_output=False, print_to_console=True) |
| 159 | if rv: |
| 160 | self._logger.LogError('Bootstrapping failed, log file - "{0}"\n'.format( |
| 161 | logfile)) |
| 162 | return False |
| 163 | |
| 164 | self._logger.LogOutput('Bootstrap succeeded.') |
| 165 | return True |
| 166 | |
| 167 | def Do(self): |
| 168 | if self.SubmitToLocalBranch() and \ |
| 169 | self.CheckoutBranch() and \ |
| 170 | self.FindGccEbuildFile() and \ |
| 171 | self.InplaceModifyEbuildFile() and \ |
| 172 | (self._setup_gcc_ebuild_file_only or self.DoBootstrapping()): |
| 173 | ret = True |
| 174 | else: |
| 175 | ret = False |
| 176 | ## Warn that the ebuild file is modified. |
| 177 | if self._gcc_ebuild_file: |
| 178 | self._logger.LogWarning( |
| 179 | ('Gcc ebuild file is (probably) modified, to revert the file - \n' |
| 180 | 'bootstrap_compiler.py --chromeos={0} --reset_gcc_ebuild_file').format( |
| 181 | self._chromeos_root)) |
| 182 | |
| 183 | return ret |
| 184 | |
| 185 | |
| 186 | def Main(argv): |
| 187 | parser = optparse.OptionParser() |
| 188 | parser.add_option('-c', '--chromeos_root', dest='chromeos_root', |
| 189 | help=('ChromeOs root dir.')) |
| 190 | parser.add_option('-b', '--branch', dest='branch', |
| 191 | help=('The branch to test against. ' |
| 192 | 'This branch must be a local branch ' |
| 193 | 'inside "src/third_party/gcc". ' |
| 194 | 'Notice, this must not be used with "--gcc".')) |
| 195 | parser.add_option('-g', '--gcc_dir', dest='gcc_dir', |
| 196 | help=('Use a local gcc tree to do bootstrapping. ' |
| 197 | 'Notice, this must not be used with "--branch".')) |
| 198 | parser.add_option('--fixperm', dest='fixperm', |
| 199 | default=False, action='store_true', |
| 200 | help=('Fix the (notorious) permission error ' |
| 201 | 'while trying to bootstrap the chroot. ' |
| 202 | 'Note this takes an extra 10-15 minutes ' |
| 203 | 'and is only needed once per chromiumos tree.')) |
| 204 | parser.add_option('--setup_gcc_ebuild_file_only', |
| 205 | dest='setup_gcc_ebuild_file_only', |
| 206 | default=False, action='store_true', |
| 207 | help=('Setup gcc ebuild file to pick up the ' |
| 208 | 'branch (--branch) or user gcc source (--gcc_dir) ' |
| 209 | 'and exit. Keep chroot as is.')) |
| 210 | parser.add_option('--reset_gcc_ebuild_file', dest='reset_gcc_ebuild_file', |
| 211 | default=False, action='store_true', |
| 212 | help=('Reset the modification that is done by this script.' |
| 213 | 'Note, when this script is running, it will modify ' |
| 214 | 'the active gcc ebuild file. Use this option to ' |
| 215 | 'reset (what this script has done) and exit.')) |
| 216 | options = parser.parse_args(argv)[0] |
| 217 | if not options.chromeos_root: |
| 218 | parser.error('Missing mandatory option "--chromeos".') |
| 219 | return 1 |
| 220 | |
| 221 | options.chromeos_root = os.path.abspath( |
| 222 | os.path.expanduser(options.chromeos_root)) |
| 223 | |
| 224 | if not os.path.isdir(options.chromeos_root): |
| 225 | logger.GetLogger().LogError( |
| 226 | '"{0}" does not exist.'.format(options.chromeos_root)) |
| 227 | return 1 |
| 228 | |
| 229 | if options.fixperm: |
| 230 | # Fix perm error before continuing. |
| 231 | cmd = ('sudo find "{0}" \( -name ".cache" -type d -prune \) -o ' + \ |
| 232 | '\( -name "chroot" -type d -prune \) -o ' + \ |
| 233 | '\( -type f -exec chmod a+r {{}} \; \) -o ' + \ |
| 234 | '\( -type d -exec chmod a+rx {{}} \; \)').format( |
| 235 | options.chromeos_root) |
| 236 | logger.GetLogger().LogOutput( |
| 237 | 'Fixing perm issues for chromeos root, this might take some time.') |
| 238 | command_executer.GetCommandExecuter().RunCommand(cmd) |
| 239 | |
| 240 | if options.reset_gcc_ebuild_file: |
| 241 | if options.gcc_dir or options.branch: |
| 242 | logger.GetLogger().LogWarning('Ignoring "--gcc_dir" or "--branch".') |
| 243 | if options.setup_gcc_ebuild_file_only: |
| 244 | logger.GetLogger().LogError( |
| 245 | ('Conflict options "--reset_gcc_ebuild_file" ' |
| 246 | 'and "--setup_gcc_ebuild_file_only".')) |
| 247 | return 1 |
| 248 | # Reset gcc ebuild file and exit. |
| 249 | rv = misc.GetGitChangesAsList( |
| 250 | os.path.join(options.chromeos_root,CHROMIUMOS_OVERLAY_PATH), |
| 251 | path='sys-devel/gcc/gcc-*.ebuild', |
| 252 | staged=False) |
| 253 | if rv: |
| 254 | cmd = 'cd {0} && git checkout --'.format(os.path.join( |
| 255 | options.chromeos_root, CHROMIUMOS_OVERLAY_PATH)) |
| 256 | for g in rv: |
| 257 | cmd += ' ' + g |
| 258 | rv = command_executer.GetCommandExecuter().RunCommand(cmd) |
| 259 | if rv: |
| 260 | logger.GetLogger().LogWarning('Failed to reset gcc ebuild file.') |
| 261 | return rv |
| 262 | else: |
| 263 | logger.GetLogger().LogWarning( |
| 264 | 'Did not find any modified gcc ebuild file.') |
| 265 | return 1 |
| 266 | |
| 267 | if options.gcc_dir: |
| 268 | options.gcc_dir = os.path.abspath(os.path.expanduser(options.gcc_dir)) |
| 269 | if not os.path.isdir(options.gcc_dir): |
| 270 | logger.GetLogger().LogError( |
| 271 | '"{0}" does not exist.'.format(options.gcc_dir)) |
| 272 | return 1 |
| 273 | |
| 274 | if options.branch and options.gcc_dir: |
| 275 | parser.error('Only one of "--gcc" and "--branch" can be specified.') |
| 276 | return 1 |
| 277 | if not (options.branch or options.gcc_dir): |
| 278 | parser.error('At least one of "--gcc" and "--branch" must be specified.') |
| 279 | return 1 |
| 280 | |
| 281 | if Bootstrapper( |
| 282 | options.chromeos_root, branch=options.branch, gcc_dir=options.gcc_dir, |
| 283 | setup_gcc_ebuild_file_only=options.setup_gcc_ebuild_file_only).Do(): |
| 284 | return 0 |
| 285 | return 1 |
| 286 | |
| 287 | |
| 288 | if __name__ == '__main__': |
| 289 | retval = Main(sys.argv) |
| 290 | sys.exit(retval) |