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