1#!/usr/bin/python2 2"""Script to bootstrap the chroot using new toolchain. 3 4This script allows you to build/install a customized version of gcc/binutils, 5either by specifying branch or a local directory. 6 7This script must be executed outside chroot. 8 9Below is some typical usage - 10 11## Build gcc located at /local/gcc/dir and do a bootstrap using the new 12## compiler for the chromeos root. The script tries to find a valid chromeos 13## tree all the way up from your current working directory. 14./build_tool.py --gcc_dir=/loca/gcc/dir --bootstrap 15 16## Build binutils, using remote branch "mobile_toolchain_v17" and do a 17## bootstrap using the new binutils for the chromeos root. The script tries to 18## find a valid chromeos tree all the way up from your current working 19## directory. 20./build_tool.py --binutils_branch=cros/mobile_toolchain_v17 \ 21 --chromeos_root=/chromeos/dir --bootstrap 22 23## Same as above except only do it for board daisy - no bootstrapping involved. 24./build_tool.py --binutils_branch=cros/mobile_toolchain_v16 \ 25 --chromeos_root=/chromeos/dir --board=daisy 26""" 27 28from __future__ import print_function 29 30__author__ = 'shenhan@google.com (Han Shen)' 31 32import argparse 33import os 34import re 35import sys 36 37 38from cros_utils import command_executer 39from cros_utils import logger 40from cros_utils import misc 41import repo_to_repo 42 43REPO_PATH_PATTERN = 'src/third_party/{0}' 44TEMP_BRANCH_NAME = 'internal_testing_branch_no_use' 45CHROMIUMOS_OVERLAY_PATH = 'src/third_party/chromiumos-overlay' 46EBUILD_PATH_PATTERN = 'src/third_party/chromiumos-overlay/sys-devel/{0}' 47 48 49class Bootstrapper(object): 50 """Class that handles bootstrap process.""" 51 52 def __init__(self, 53 chromeos_root, 54 ndk_dir, 55 gcc_branch=None, 56 gcc_dir=None, 57 binutils_branch=None, 58 binutils_dir=None, 59 board=None, 60 disable_2nd_bootstrap=False, 61 setup_tool_ebuild_file_only=False): 62 self._chromeos_root = chromeos_root 63 self._ndk_dir = ndk_dir 64 65 self._gcc_branch = gcc_branch 66 self._gcc_branch_tree = None 67 self._gcc_dir = gcc_dir 68 self._gcc_ebuild_file = None 69 self._gcc_ebuild_file_name = None 70 71 self._binutils_branch = binutils_branch 72 self._binutils_branch_tree = None 73 self._binutils_dir = binutils_dir 74 self._binutils_ebuild_file = None 75 self._binutils_ebuild_file_name = None 76 77 self._setup_tool_ebuild_file_only = setup_tool_ebuild_file_only 78 79 self._ce = command_executer.GetCommandExecuter() 80 self._logger = logger.GetLogger() 81 self._board = board 82 self._disable_2nd_bootstrap = disable_2nd_bootstrap 83 84 def IsTreeSame(self, t1, t2): 85 diff = 'diff -qr -x .git -x .svn "{0}" "{1}"'.format(t1, t2) 86 if self._ce.RunCommand(diff, print_to_console=False) == 0: 87 self._logger.LogOutput('"{0}" and "{1}" are the same."'.format(t1, t2)) 88 return True 89 self._logger.LogWarning('"{0}" and "{1}" are different."'.format(t1, t2)) 90 return False 91 92 def SubmitToLocalBranch(self): 93 """Copy source code to the chromium source tree and submit it locally.""" 94 if self._gcc_dir: 95 if not self.SubmitToolToLocalBranch(tool_name='gcc', 96 tool_dir=self._gcc_dir): 97 return False 98 self._gcc_branch = TEMP_BRANCH_NAME 99 100 if self._binutils_dir: 101 if not self.SubmitToolToLocalBranch(tool_name='binutils', 102 tool_dir=self._binutils_dir): 103 return False 104 self._binutils_branch = TEMP_BRANCH_NAME 105 106 return True 107 108 def SubmitToolToLocalBranch(self, tool_name, tool_dir): 109 """Copy the source code to local chromium source tree. 110 111 Args: 112 tool_name: either 'gcc' or 'binutils' 113 tool_dir: the tool source dir to be used 114 115 Returns: 116 True if all succeeded False otherwise. 117 """ 118 119 # The next few steps creates an internal branch to sync with the tool dir 120 # user provided. 121 chrome_tool_dir = self.GetChromeOsToolDir(tool_name) 122 123 # 0. Test to see if git tree is free of local changes. 124 if not misc.IsGitTreeClean(chrome_tool_dir): 125 self._logger.LogError('Git repository "{0}" not clean, aborted.'.format( 126 chrome_tool_dir)) 127 return False 128 129 # 1. Checkout/create a (new) branch for testing. 130 command = 'cd "{0}" && git checkout -B {1}'.format(chrome_tool_dir, 131 TEMP_BRANCH_NAME) 132 ret = self._ce.RunCommand(command) 133 if ret: 134 self._logger.LogError('Failed to create a temp branch for test, aborted.') 135 return False 136 137 if self.IsTreeSame(tool_dir, chrome_tool_dir): 138 self._logger.LogOutput( 139 '"{0}" and "{1}" are the same, sync skipped.'.format(tool_dir, 140 chrome_tool_dir)) 141 return True 142 143 # 2. Sync sources from user provided tool dir to chromiumos tool git. 144 local_tool_repo = repo_to_repo.FileRepo(tool_dir) 145 chrome_tool_repo = repo_to_repo.GitRepo(chrome_tool_dir, TEMP_BRANCH_NAME) 146 chrome_tool_repo.SetRoot(chrome_tool_dir) 147 # Delete all stuff except '.git' before start mapping. 148 self._ce.RunCommand( 149 'cd {0} && find . -maxdepth 1 -not -name ".git" -not -name "." ' 150 r'\( -type f -exec rm {{}} \; -o ' 151 r' -type d -exec rm -fr {{}} \; \)'.format(chrome_tool_dir)) 152 local_tool_repo.MapSources(chrome_tool_repo.GetRoot()) 153 154 # 3. Ensure after sync tree is the same. 155 if self.IsTreeSame(tool_dir, chrome_tool_dir): 156 self._logger.LogOutput('Sync successfully done.') 157 else: 158 self._logger.LogError('Sync not successful, aborted.') 159 return False 160 161 # 4. Commit all changes. 162 # 4.1 Try to get some information about the tool dir we are using. 163 cmd = 'cd {0} && git log -1 --pretty=oneline'.format(tool_dir) 164 tool_dir_extra_info = None 165 ret, tool_dir_extra_info, _ = self._ce.RunCommandWOutput( 166 cmd, 167 print_to_console=False) 168 commit_message = 'Synced with tool source tree at - "{0}".'.format(tool_dir) 169 if not ret: 170 commit_message += '\nGit log for {0}:\n{1}'.format( 171 tool_dir, tool_dir_extra_info.strip()) 172 173 if chrome_tool_repo.CommitLocally(commit_message): 174 self._logger.LogError( 175 'Commit to local branch "{0}" failed, aborted.'.format( 176 TEMP_BRANCH_NAME)) 177 return False 178 return True 179 180 def CheckoutBranch(self): 181 """Checkout working branch for the tools. 182 183 Returns: 184 True: if operation succeeds. 185 """ 186 187 if self._gcc_branch: 188 rv = self.CheckoutToolBranch('gcc', self._gcc_branch) 189 if rv: 190 self._gcc_branch_tree = rv 191 else: 192 return False 193 194 if self._binutils_branch: 195 rv = self.CheckoutToolBranch('binutils', self._binutils_branch) 196 if rv: 197 self._binutils_branch_tree = rv 198 else: 199 return False 200 201 return True 202 203 def CheckoutToolBranch(self, tool_name, tool_branch): 204 """Checkout the tool branch for a certain tool. 205 206 Args: 207 tool_name: either 'gcc' or 'binutils' 208 tool_branch: tool branch to use 209 210 Returns: 211 True: if operation succeeds. Otherwise False. 212 """ 213 214 chrome_tool_dir = self.GetChromeOsToolDir(tool_name) 215 command = 'cd "{0}" && git checkout {1}'.format(chrome_tool_dir, 216 tool_branch) 217 if not self._ce.RunCommand(command, print_to_console=True): 218 # Get 'TREE' value of this commit 219 command = ('cd "{0}" && git cat-file -p {1} ' 220 '| grep -E "^tree [a-f0-9]+$" ' 221 '| cut -d" " -f2').format(chrome_tool_dir, tool_branch) 222 ret, stdout, _ = self._ce.RunCommandWOutput(command, 223 print_to_console=False) 224 # Pipe operation always has a zero return value. So need to check if 225 # stdout is valid. 226 if not ret and stdout and re.match('[0-9a-h]{40}', stdout.strip(), 227 re.IGNORECASE): 228 tool_branch_tree = stdout.strip() 229 self._logger.LogOutput('Find tree for {0} branch "{1}" - "{2}"'.format( 230 tool_name, tool_branch, tool_branch_tree)) 231 return tool_branch_tree 232 self._logger.LogError(('Failed to checkout "{0}" or failed to ' 233 'get tree value, aborted.').format(tool_branch)) 234 return None 235 236 def FindEbuildFile(self): 237 """Find the ebuild files for the tools. 238 239 Returns: 240 True: if operation succeeds. 241 """ 242 243 if self._gcc_branch: 244 (rv, ef, efn) = self.FindToolEbuildFile('gcc') 245 if rv: 246 self._gcc_ebuild_file = ef 247 self._gcc_ebuild_file_name = efn 248 else: 249 return False 250 251 if self._binutils_branch: 252 (rv, ef, efn) = self.FindToolEbuildFile('binutils') 253 if rv: 254 self._binutils_ebuild_file = ef 255 self._binutils_ebuild_file_name = efn 256 else: 257 return False 258 259 return True 260 261 def FindToolEbuildFile(self, tool_name): 262 """Find ebuild file for a specific tool. 263 264 Args: 265 tool_name: either "gcc" or "binutils". 266 267 Returns: 268 A triplet that consisits of whether operation succeeds or not, 269 tool ebuild file full path and tool ebuild file name. 270 """ 271 272 # To get the active gcc ebuild file, we need a workable chroot first. 273 if not os.path.exists(os.path.join( 274 self._chromeos_root, 'chroot')) and self._ce.RunCommand( 275 'cd "{0}" && cros_sdk --create'.format(self._chromeos_root)): 276 self._logger.LogError(('Failed to install a initial chroot, aborted.\n' 277 'If previous bootstrap failed, do a ' 278 '"cros_sdk --delete" to remove ' 279 'in-complete chroot.')) 280 return (False, None, None) 281 282 rv, stdout, _ = self._ce.ChrootRunCommandWOutput( 283 self._chromeos_root, 284 'equery w sys-devel/{0}'.format(tool_name), 285 print_to_console=True) 286 if rv: 287 self._logger.LogError(('Failed to execute inside chroot ' 288 '"equery w sys-devel/{0}", aborted.').format( 289 tool_name)) 290 return (False, None, None) 291 m = re.match(r'^.*/({0}/(.*\.ebuild))$'.format(EBUILD_PATH_PATTERN.format( 292 tool_name)), stdout) 293 if not m: 294 self._logger.LogError( 295 ('Failed to find {0} ebuild file, aborted. ' 296 'If previous bootstrap failed, do a "cros_sdk --delete" to remove ' 297 'in-complete chroot.').format(tool_name)) 298 return (False, None, None) 299 tool_ebuild_file = os.path.join(self._chromeos_root, m.group(1)) 300 tool_ebuild_file_name = m.group(2) 301 302 return (True, tool_ebuild_file, tool_ebuild_file_name) 303 304 def InplaceModifyEbuildFile(self): 305 """Modify the ebuild file. 306 307 Returns: 308 True if operation succeeds. 309 """ 310 311 # Note we shall not use remote branch name (eg. "cros/gcc.gnu.org/...") in 312 # CROS_WORKON_COMMIT, we have to use GITHASH. So we call GitGetCommitHash on 313 # tool_branch. 314 tool = None 315 toolbranch = None 316 if self._gcc_branch: 317 tool = 'gcc' 318 toolbranch = self._gcc_branch 319 tooltree = self._gcc_branch_tree 320 toolebuild = self._gcc_ebuild_file 321 elif self._binutils_branch: 322 tool = 'binutils' 323 toolbranch = self._binutils_branch 324 tooltree = self._binutils_branch_tree 325 toolebuild = self._binutils_ebuild_file 326 327 328 assert tool 329 330 # An example for the following variables would be: 331 # tooldir = '~/android/master-ndk/toolchain/gcc/gcc-4.9' 332 # tool_branch_githash = xxxxx 333 # toolcomponents = toolchain/gcc 334 tooldir = self.GetChromeOsToolDir(tool) 335 toolgithash = misc.GitGetCommitHash(tooldir, toolbranch) 336 if not toolgithash: 337 return False 338 toolcomponents = 'toolchain/{}'.format(tool) 339 return self.InplaceModifyToolEbuildFile(toolcomponents, 340 toolgithash, 341 tooltree, 342 toolebuild) 343 344 @staticmethod 345 def ResetToolEbuildFile(chromeos_root, tool_name): 346 """Reset tool ebuild file to clean state. 347 348 Args: 349 chromeos_root: chromeos source tree 350 tool_name: either "gcc" or "binutils" 351 352 Returns: 353 True if operation succeds. 354 """ 355 rv = misc.GetGitChangesAsList( 356 os.path.join(chromeos_root, CHROMIUMOS_OVERLAY_PATH), 357 path=('sys-devel/{0}/{0}-*.ebuild'.format(tool_name)), 358 staged=False) 359 if rv: 360 cmd = 'cd {0} && git checkout --'.format(os.path.join( 361 chromeos_root, CHROMIUMOS_OVERLAY_PATH)) 362 for g in rv: 363 cmd += ' ' + g 364 rv = command_executer.GetCommandExecuter().RunCommand(cmd) 365 if rv: 366 logger.GetLogger().LogWarning( 367 'Failed to reset the ebuild file. Please refer to log above.') 368 return False 369 else: 370 logger.GetLogger().LogWarning( 371 'Note - did not find any modified {0} ebuild file.'.format(tool_name)) 372 # Fall through 373 return True 374 375 def GetChromeOsToolDir(self, tool_name): 376 """Return the chromeos git dir for a specific tool. 377 378 Note, after we unified ChromeOs and Android, the tool dir is under 379 ndk_dir/toolchain/[gcc,binutils]. 380 381 Args: 382 tool_name: either 'gcc' or 'binutils'. 383 384 Returns: 385 Absolute git path for the tool. 386 """ 387 388 tool_toppath = os.path.join(self._ndk_dir, 'toolchain', tool_name) 389 # There may be sub-directories like 'binutils-2.25', 'binutils-2.24', 390 # 'gcc-4.9', 'gcc-4.8', etc. find the newest binutils version. 391 cmd = ('find {} -maxdepth 1 -type d -name "{}-*" ' 392 '| sort -r | head -1').format(tool_toppath, tool_name) 393 rv, out, _ = self._ce.RunCommandWOutput(cmd, print_to_console=False) 394 if rv: 395 return None 396 repo = out.strip() 397 398 # cros-workon eclass expects every CROS_WORKON_PROJECT ends with ".git". 399 self._ce.RunCommand(('cd $(dirname {0}) && ' 400 'ln -sf $(basename {0}) $(basename {0}).git').format( 401 repo, print_to_console=True)) 402 return repo 403 404 405 def InplaceModifyToolEbuildFile(self, 406 tool_components, 407 tool_branch_githash, 408 tool_branch_tree, 409 tool_ebuild_file): 410 """Using sed to fill properly values into the ebuild file. 411 412 Args: 413 tool_components: either "toolchain/gcc" or "toolchain/binutils" 414 tool_branch_githash: githash for tool_branch 415 tool_branch_tree: treeish for the tool branch 416 tool_ebuild_file: tool ebuild file 417 418 Returns: 419 True: if operation succeeded. 420 """ 421 422 command = ('sed -i ' 423 '-e \'/^CROS_WORKON_REPO=".*"/i' 424 ' # The following line is modified by script.\' ' 425 '-e \'s!^CROS_WORKON_REPO=".*"$!CROS_WORKON_REPO="{0}"!\' ' 426 '-e \'/^CROS_WORKON_PROJECT=".*"/i' 427 ' # The following line is modified by script.\' ' 428 '-e \'s!^CROS_WORKON_PROJECT=.*$!CROS_WORKON_PROJECT="{1}"!\' ' 429 '-e \'/^CROS_WORKON_COMMIT=".*"/i' 430 ' # The following line is modified by script.\' ' 431 '-e \'s!^CROS_WORKON_COMMIT=".*"$!CROS_WORKON_COMMIT="{2}"!\' ' 432 '-e \'/^CROS_WORKON_TREE=".*"/i' 433 ' # The following line is modified by script.\' ' 434 '-e \'s!^CROS_WORKON_TREE=".*"$!CROS_WORKON_TREE="{3}"!\' ' 435 '{4}').format('/home/{}/ndk-root'.format(os.environ['USER']), 436 tool_components, 437 tool_branch_githash, 438 tool_branch_tree, 439 tool_ebuild_file) 440 rv = self._ce.RunCommand(command) 441 if rv: 442 self._logger.LogError( 443 'Failed to modify commit and tree value for "{0}"", aborted.'.format( 444 tool_ebuild_file)) 445 return False 446 447 # Warn that the ebuild file has been modified. 448 self._logger.LogWarning( 449 ('Ebuild file "{0}" is modified, to revert the file - \n' 450 'bootstrap_compiler.py --chromeos_root={1} ' 451 '--reset_tool_ebuild_file').format(tool_ebuild_file, 452 self._chromeos_root)) 453 return True 454 455 def DoBuildForBoard(self): 456 """Build tool for a specific board. 457 458 Returns: 459 True if operation succeeds. 460 """ 461 462 if self._gcc_branch: 463 if not self.DoBuildToolForBoard('gcc'): 464 return False 465 if self._binutils_branch: 466 if not self.DoBuildToolForBoard('binutils'): 467 return False 468 return True 469 470 def DoBuildToolForBoard(self, tool_name): 471 """Build a specific tool for a specific board. 472 473 Args: 474 tool_name: either "gcc" or "binutils" 475 476 Returns: 477 True if operation succeeds. 478 """ 479 480 chroot_ndk_root = os.path.join(self._chromeos_root, 'chroot', 481 'home', os.environ['USER'], 482 'ndk-root') 483 self._ce.RunCommand('mkdir -p {}'.format(chroot_ndk_root)) 484 if self._ce.RunCommand('sudo mount --bind {} {}'.format( 485 self._ndk_dir, chroot_ndk_root)): 486 self._logger.LogError('Failed to mount ndk dir into chroot') 487 return False 488 489 try: 490 boards_to_build = self._board.split(',') 491 target_built = set() 492 failed = [] 493 for board in boards_to_build: 494 if board == 'host': 495 command = 'sudo emerge sys-devel/{0}'.format(tool_name) 496 else: 497 target = misc.GetCtargetFromBoard(board, self._chromeos_root) 498 if not target: 499 self._logger.LogError( 500 'Unsupported board "{0}", skip.'.format(board)) 501 failed.append(board) 502 continue 503 # Skip this board if we have already built for a board that has the 504 # same target. 505 if target in target_built: 506 self._logger.LogWarning( 507 'Skipping toolchain for board "{}"'.format(board)) 508 continue 509 target_built.add(target) 510 command = 'sudo emerge cross-{0}/{1}'.format(target, tool_name) 511 512 rv = self._ce.ChrootRunCommand(self._chromeos_root, 513 command, 514 print_to_console=True) 515 if rv: 516 self._logger.LogError('Build {0} failed for {1}, aborted.'.format( 517 tool_name, board)) 518 failed.append(board) 519 else: 520 self._logger.LogOutput('Successfully built {0} for board {1}.'.format( 521 tool_name, board)) 522 finally: 523 # Make sure we un-mount ndk-root before we leave here, regardless of the 524 # build result of the tool. Otherwise we may inadvertently delete ndk-root 525 # dir, which is not part of the chroot and could be disastrous. 526 if chroot_ndk_root: 527 if self._ce.RunCommand('sudo umount {}'.format(chroot_ndk_root)): 528 self._logger.LogWarning(('Failed to umount "{}", please check ' 529 'before deleting chroot.').format( 530 chroot_ndk_root)) 531 532 # Clean up soft links created during build. 533 self._ce.RunCommand('cd {}/toolchain/{} && git clean -df'.format( 534 self._ndk_dir, tool_name)) 535 536 if failed: 537 self._logger.LogError( 538 'Failed to build {0} for the following board(s): "{1}"'.format( 539 tool_name, ' '.join(failed))) 540 return False 541 # All boards build successfully 542 return True 543 544 def DoBootstrapping(self): 545 """Do bootstrapping the chroot. 546 547 This step firstly downloads a prestine sdk, then use this sdk to build the 548 new sdk, finally use the new sdk to build every host package. 549 550 Returns: 551 True if operation succeeds. 552 """ 553 554 logfile = os.path.join(self._chromeos_root, 'bootstrap.log') 555 command = 'cd "{0}" && cros_sdk --delete --bootstrap |& tee "{1}"'.format( 556 self._chromeos_root, logfile) 557 rv = self._ce.RunCommand(command, print_to_console=True) 558 if rv: 559 self._logger.LogError('Bootstrapping failed, log file - "{0}"\n'.format( 560 logfile)) 561 return False 562 563 self._logger.LogOutput('Bootstrap succeeded.') 564 return True 565 566 def BuildAndInstallAmd64Host(self): 567 """Build amd64-host (host) packages. 568 569 Build all host packages in the newly-bootstrapped 'chroot' using *NEW* 570 toolchain. 571 572 So actually we perform 2 builds of all host packages - 573 1. build new toolchain using old toolchain and build all host packages 574 using the newly built toolchain 575 2. build the new toolchain again but using new toolchain built in step 1, 576 and build all host packages using the newly built toolchain 577 578 Returns: 579 True if operation succeeds. 580 """ 581 582 cmd = ('cd {0} && cros_sdk -- -- ./setup_board --board=amd64-host ' 583 '--accept_licenses=@CHROMEOS --skip_chroot_upgrade --nousepkg ' 584 '--reuse_pkgs_from_local_boards').format(self._chromeos_root) 585 rv = self._ce.RunCommand(cmd, print_to_console=True) 586 if rv: 587 self._logger.LogError('Build amd64-host failed.') 588 return False 589 590 # Package amd64-host into 'built-sdk.tar.xz'. 591 sdk_package = os.path.join(self._chromeos_root, 'built-sdk.tar.xz') 592 cmd = ('cd {0}/chroot/build/amd64-host && sudo XZ_OPT="-e9" ' 593 'tar --exclude="usr/lib/debug/*" --exclude="packages/*" ' 594 '--exclude="tmp/*" --exclude="usr/local/build/autotest/*" ' 595 '--sparse -I xz -vcf {1} . && sudo chmod a+r {1}').format( 596 self._chromeos_root, sdk_package) 597 rv = self._ce.RunCommand(cmd, print_to_console=True) 598 if rv: 599 self._logger.LogError('Failed to create "built-sdk.tar.xz".') 600 return False 601 602 # Install amd64-host into a new chroot. 603 cmd = ('cd {0} && cros_sdk --chroot new-sdk-chroot --download --replace ' 604 '--nousepkg --url file://{1}').format(self._chromeos_root, 605 sdk_package) 606 rv = self._ce.RunCommand(cmd, print_to_console=True) 607 if rv: 608 self._logger.LogError('Failed to install "built-sdk.tar.xz".') 609 return False 610 self._logger.LogOutput( 611 'Successfully installed built-sdk.tar.xz into a new chroot.\nAll done.') 612 613 # Rename the newly created new-sdk-chroot to chroot. 614 cmd = ('cd {0} && sudo mv chroot chroot-old && ' 615 'sudo mv new-sdk-chroot chroot').format(self._chromeos_root) 616 rv = self._ce.RunCommand(cmd, print_to_console=True) 617 return rv == 0 618 619 def Do(self): 620 """Entrance of the class. 621 622 Returns: 623 True if everything is ok. 624 """ 625 626 if (self.SubmitToLocalBranch() and self.CheckoutBranch() and 627 self.FindEbuildFile() and self.InplaceModifyEbuildFile()): 628 if self._setup_tool_ebuild_file_only: 629 # Everything is done, we are good. 630 ret = True 631 else: 632 if self._board: 633 ret = self.DoBuildForBoard() 634 else: 635 # This implies '--bootstrap'. 636 ret = (self.DoBootstrapping() and (self._disable_2nd_bootstrap or 637 self.BuildAndInstallAmd64Host())) 638 else: 639 ret = False 640 return ret 641 642 643def Main(argv): 644 parser = argparse.ArgumentParser() 645 parser.add_argument('-c', 646 '--chromeos_root', 647 dest='chromeos_root', 648 help=('Optional. ChromeOs root dir. ' 649 'When not specified, chromeos root will be deduced' 650 ' from current working directory.')) 651 parser.add_argument('--ndk_dir', 652 dest='ndk_dir', 653 help=('Topmost android ndk dir, required. ' 654 'Do not need to include the "toolchain/*" part.')) 655 parser.add_argument('--gcc_branch', 656 dest='gcc_branch', 657 help=('The branch to test against. ' 658 'This branch must be a local branch ' 659 'inside "src/third_party/gcc". ' 660 'Notice, this must not be used with "--gcc_dir".')) 661 parser.add_argument('--binutils_branch', 662 dest='binutils_branch', 663 help=('The branch to test against binutils. ' 664 'This branch must be a local branch ' 665 'inside "src/third_party/binutils". ' 666 'Notice, this must not be used with ' 667 '"--binutils_dir".')) 668 parser.add_argument('-g', 669 '--gcc_dir', 670 dest='gcc_dir', 671 help=('Use a local gcc tree to do bootstrapping. ' 672 'Notice, this must not be used with ' 673 '"--gcc_branch".')) 674 parser.add_argument('--binutils_dir', 675 dest='binutils_dir', 676 help=('Use a local binutils tree to do bootstrapping. ' 677 'Notice, this must not be used with ' 678 '"--binutils_branch".')) 679 parser.add_argument('--fixperm', 680 dest='fixperm', 681 default=False, 682 action='store_true', 683 help=('Fix the (notorious) permission error ' 684 'while trying to bootstrap the chroot. ' 685 'Note this takes an extra 10-15 minutes ' 686 'and is only needed once per chromiumos tree.')) 687 parser.add_argument('--setup_tool_ebuild_file_only', 688 dest='setup_tool_ebuild_file_only', 689 default=False, 690 action='store_true', 691 help=('Setup gcc and/or binutils ebuild file ' 692 'to pick up the branch (--gcc/binutils_branch) or ' 693 'use gcc and/or binutils source ' 694 '(--gcc/binutils_dir) and exit. Keep chroot as is.' 695 ' This should not be used with ' 696 '--gcc/binutils_dir/branch options.')) 697 parser.add_argument('--reset_tool_ebuild_file', 698 dest='reset_tool_ebuild_file', 699 default=False, 700 action='store_true', 701 help=('Reset the modification that is done by this ' 702 'script. Note, when this script is running, it ' 703 'will modify the active gcc/binutils ebuild file. ' 704 'Use this option to reset (what this script has ' 705 'done) and exit. This should not be used with -- ' 706 'gcc/binutils_dir/branch options.')) 707 parser.add_argument('--board', 708 dest='board', 709 default=None, 710 help=('Only build toolchain for specific board(s). ' 711 'Use "host" to build for host. ' 712 'Use "," to seperate multiple boards. ' 713 'This does not perform a chroot bootstrap.')) 714 parser.add_argument('--bootstrap', 715 dest='bootstrap', 716 default=False, 717 action='store_true', 718 help=('Performs a chroot bootstrap. ' 719 'Note, this will *destroy* your current chroot.')) 720 parser.add_argument('--disable-2nd-bootstrap', 721 dest='disable_2nd_bootstrap', 722 default=False, 723 action='store_true', 724 help=('Disable a second bootstrap ' 725 '(build of amd64-host stage).')) 726 727 options = parser.parse_args(argv) 728 # Trying to deduce chromeos root from current directory. 729 if not options.chromeos_root: 730 logger.GetLogger().LogOutput('Trying to deduce chromeos root ...') 731 wdir = os.getcwd() 732 while wdir and wdir != '/': 733 if misc.IsChromeOsTree(wdir): 734 logger.GetLogger().LogOutput('Find chromeos_root: {}'.format(wdir)) 735 options.chromeos_root = wdir 736 break 737 wdir = os.path.dirname(wdir) 738 739 if not options.chromeos_root: 740 parser.error('Missing or failing to deduce mandatory option "--chromeos".') 741 return 1 742 743 options.chromeos_root = os.path.abspath(os.path.expanduser( 744 options.chromeos_root)) 745 746 if not os.path.isdir(options.chromeos_root): 747 logger.GetLogger().LogError('"{0}" does not exist.'.format( 748 options.chromeos_root)) 749 return 1 750 751 options.ndk_dir = os.path.expanduser(options.ndk_dir) 752 if not options.ndk_dir: 753 parser.error('Missing mandatory option "--ndk_dir".') 754 return 1 755 756 # Some tolerance regarding user input. We only need the ndk_root part, do not 757 # include toolchain/(gcc|binutils)/ part in this option. 758 options.ndk_dir = re.sub( 759 '/toolchain(/gcc|/binutils)?/?$', '', options.ndk_dir) 760 761 if not (os.path.isdir(options.ndk_dir) and 762 os.path.isdir(os.path.join(options.ndk_dir, 'toolchain'))): 763 logger.GetLogger().LogError( 764 '"toolchain" directory not found under "{0}".'.format(options.ndk_dir)) 765 return 1 766 767 if options.fixperm: 768 # Fix perm error before continuing. 769 cmd = ( 770 r'sudo find "{0}" \( -name ".cache" -type d -prune \) -o ' 771 r'\( -name "chroot" -type d -prune \) -o ' 772 r'\( -type f -exec chmod a+r {{}} \; \) -o ' 773 r'\( -type d -exec chmod a+rx {{}} \; \)').format(options.chromeos_root) 774 logger.GetLogger().LogOutput( 775 'Fixing perm issues for chromeos root, this might take some time.') 776 command_executer.GetCommandExecuter().RunCommand(cmd) 777 778 if options.reset_tool_ebuild_file: 779 if (options.gcc_dir or options.gcc_branch or options.binutils_dir or 780 options.binutils_branch): 781 logger.GetLogger().LogWarning( 782 'Ignoring any "--gcc/binutils_dir" and/or "--gcc/binutils_branch".') 783 if options.setup_tool_ebuild_file_only: 784 logger.GetLogger().LogError( 785 ('Conflict options "--reset_tool_ebuild_file" ' 786 'and "--setup_tool_ebuild_file_only".')) 787 return 1 788 rv = Bootstrapper.ResetToolEbuildFile(options.chromeos_root, 'gcc') 789 rv1 = Bootstrapper.ResetToolEbuildFile(options.chromeos_root, 'binutils') 790 return 0 if (rv and rv1) else 1 791 792 if options.gcc_dir: 793 options.gcc_dir = os.path.abspath(os.path.expanduser(options.gcc_dir)) 794 if not os.path.isdir(options.gcc_dir): 795 logger.GetLogger().LogError('"{0}" does not exist.'.format( 796 options.gcc_dir)) 797 return 1 798 799 if options.gcc_branch and options.gcc_dir: 800 parser.error('Only one of "--gcc_dir" and "--gcc_branch" can be specified.') 801 return 1 802 803 if options.binutils_dir: 804 options.binutils_dir = os.path.abspath(os.path.expanduser( 805 options.binutils_dir)) 806 if not os.path.isdir(options.binutils_dir): 807 logger.GetLogger().LogError('"{0}" does not exist.'.format( 808 options.binutils_dir)) 809 return 1 810 811 if options.binutils_branch and options.binutils_dir: 812 parser.error('Only one of "--binutils_dir" and ' 813 '"--binutils_branch" can be specified.') 814 return 1 815 816 if (not (options.binutils_branch or options.binutils_dir or options.gcc_branch 817 or options.gcc_dir)): 818 parser.error(('At least one of "--gcc_dir", "--gcc_branch", ' 819 '"--binutils_dir" and "--binutils_branch" must ' 820 'be specified.')) 821 return 1 822 823 if not options.board and not options.bootstrap: 824 parser.error('You must specify either "--board" or "--bootstrap".') 825 return 1 826 827 if (options.board and options.bootstrap and 828 not options.setup_tool_ebuild_file_only): 829 parser.error('You must specify only one of "--board" and "--bootstrap".') 830 return 1 831 832 if not options.bootstrap and options.disable_2nd_bootstrap: 833 parser.error('"--disable-2nd-bootstrap" has no effect ' 834 'without specifying "--bootstrap".') 835 return 1 836 837 if Bootstrapper( 838 options.chromeos_root, 839 options.ndk_dir, 840 gcc_branch=options.gcc_branch, 841 gcc_dir=options.gcc_dir, 842 binutils_branch=options.binutils_branch, 843 binutils_dir=options.binutils_dir, 844 board=options.board, 845 disable_2nd_bootstrap=options.disable_2nd_bootstrap, 846 setup_tool_ebuild_file_only=options.setup_tool_ebuild_file_only).Do(): 847 return 0 848 return 1 849 850 851if __name__ == '__main__': 852 retval = Main(sys.argv[1:]) 853 sys.exit(retval) 854