You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
853 lines
32 KiB
853 lines
32 KiB
#!/usr/bin/python2 |
|
"""Script to bootstrap the chroot using new toolchain. |
|
|
|
This script allows you to build/install a customized version of gcc/binutils, |
|
either by specifying branch or a local directory. |
|
|
|
This script must be executed outside chroot. |
|
|
|
Below is some typical usage - |
|
|
|
## Build gcc located at /local/gcc/dir and do a bootstrap using the new |
|
## compiler for the chromeos root. The script tries to find a valid chromeos |
|
## tree all the way up from your current working directory. |
|
./build_tool.py --gcc_dir=/loca/gcc/dir --bootstrap |
|
|
|
## Build binutils, using remote branch "mobile_toolchain_v17" and do a |
|
## bootstrap using the new binutils for the chromeos root. The script tries to |
|
## find a valid chromeos tree all the way up from your current working |
|
## directory. |
|
./build_tool.py --binutils_branch=cros/mobile_toolchain_v17 \ |
|
--chromeos_root=/chromeos/dir --bootstrap |
|
|
|
## Same as above except only do it for board daisy - no bootstrapping involved. |
|
./build_tool.py --binutils_branch=cros/mobile_toolchain_v16 \ |
|
--chromeos_root=/chromeos/dir --board=daisy |
|
""" |
|
|
|
from __future__ import print_function |
|
|
|
__author__ = 'shenhan@google.com (Han Shen)' |
|
|
|
import argparse |
|
import os |
|
import re |
|
import sys |
|
|
|
|
|
from cros_utils import command_executer |
|
from cros_utils import logger |
|
from cros_utils import misc |
|
import repo_to_repo |
|
|
|
REPO_PATH_PATTERN = 'src/third_party/{0}' |
|
TEMP_BRANCH_NAME = 'internal_testing_branch_no_use' |
|
CHROMIUMOS_OVERLAY_PATH = 'src/third_party/chromiumos-overlay' |
|
EBUILD_PATH_PATTERN = 'src/third_party/chromiumos-overlay/sys-devel/{0}' |
|
|
|
|
|
class Bootstrapper(object): |
|
"""Class that handles bootstrap process.""" |
|
|
|
def __init__(self, |
|
chromeos_root, |
|
ndk_dir, |
|
gcc_branch=None, |
|
gcc_dir=None, |
|
binutils_branch=None, |
|
binutils_dir=None, |
|
board=None, |
|
disable_2nd_bootstrap=False, |
|
setup_tool_ebuild_file_only=False): |
|
self._chromeos_root = chromeos_root |
|
self._ndk_dir = ndk_dir |
|
|
|
self._gcc_branch = gcc_branch |
|
self._gcc_branch_tree = None |
|
self._gcc_dir = gcc_dir |
|
self._gcc_ebuild_file = None |
|
self._gcc_ebuild_file_name = None |
|
|
|
self._binutils_branch = binutils_branch |
|
self._binutils_branch_tree = None |
|
self._binutils_dir = binutils_dir |
|
self._binutils_ebuild_file = None |
|
self._binutils_ebuild_file_name = None |
|
|
|
self._setup_tool_ebuild_file_only = setup_tool_ebuild_file_only |
|
|
|
self._ce = command_executer.GetCommandExecuter() |
|
self._logger = logger.GetLogger() |
|
self._board = board |
|
self._disable_2nd_bootstrap = disable_2nd_bootstrap |
|
|
|
def IsTreeSame(self, t1, t2): |
|
diff = 'diff -qr -x .git -x .svn "{0}" "{1}"'.format(t1, t2) |
|
if self._ce.RunCommand(diff, print_to_console=False) == 0: |
|
self._logger.LogOutput('"{0}" and "{1}" are the same."'.format(t1, t2)) |
|
return True |
|
self._logger.LogWarning('"{0}" and "{1}" are different."'.format(t1, t2)) |
|
return False |
|
|
|
def SubmitToLocalBranch(self): |
|
"""Copy source code to the chromium source tree and submit it locally.""" |
|
if self._gcc_dir: |
|
if not self.SubmitToolToLocalBranch(tool_name='gcc', |
|
tool_dir=self._gcc_dir): |
|
return False |
|
self._gcc_branch = TEMP_BRANCH_NAME |
|
|
|
if self._binutils_dir: |
|
if not self.SubmitToolToLocalBranch(tool_name='binutils', |
|
tool_dir=self._binutils_dir): |
|
return False |
|
self._binutils_branch = TEMP_BRANCH_NAME |
|
|
|
return True |
|
|
|
def SubmitToolToLocalBranch(self, tool_name, tool_dir): |
|
"""Copy the source code to local chromium source tree. |
|
|
|
Args: |
|
tool_name: either 'gcc' or 'binutils' |
|
tool_dir: the tool source dir to be used |
|
|
|
Returns: |
|
True if all succeeded False otherwise. |
|
""" |
|
|
|
# The next few steps creates an internal branch to sync with the tool dir |
|
# user provided. |
|
chrome_tool_dir = self.GetChromeOsToolDir(tool_name) |
|
|
|
# 0. Test to see if git tree is free of local changes. |
|
if not misc.IsGitTreeClean(chrome_tool_dir): |
|
self._logger.LogError('Git repository "{0}" not clean, aborted.'.format( |
|
chrome_tool_dir)) |
|
return False |
|
|
|
# 1. Checkout/create a (new) branch for testing. |
|
command = 'cd "{0}" && git checkout -B {1}'.format(chrome_tool_dir, |
|
TEMP_BRANCH_NAME) |
|
ret = self._ce.RunCommand(command) |
|
if ret: |
|
self._logger.LogError('Failed to create a temp branch for test, aborted.') |
|
return False |
|
|
|
if self.IsTreeSame(tool_dir, chrome_tool_dir): |
|
self._logger.LogOutput( |
|
'"{0}" and "{1}" are the same, sync skipped.'.format(tool_dir, |
|
chrome_tool_dir)) |
|
return True |
|
|
|
# 2. Sync sources from user provided tool dir to chromiumos tool git. |
|
local_tool_repo = repo_to_repo.FileRepo(tool_dir) |
|
chrome_tool_repo = repo_to_repo.GitRepo(chrome_tool_dir, TEMP_BRANCH_NAME) |
|
chrome_tool_repo.SetRoot(chrome_tool_dir) |
|
# Delete all stuff except '.git' before start mapping. |
|
self._ce.RunCommand( |
|
'cd {0} && find . -maxdepth 1 -not -name ".git" -not -name "." ' |
|
r'\( -type f -exec rm {{}} \; -o ' |
|
r' -type d -exec rm -fr {{}} \; \)'.format(chrome_tool_dir)) |
|
local_tool_repo.MapSources(chrome_tool_repo.GetRoot()) |
|
|
|
# 3. Ensure after sync tree is the same. |
|
if self.IsTreeSame(tool_dir, chrome_tool_dir): |
|
self._logger.LogOutput('Sync successfully done.') |
|
else: |
|
self._logger.LogError('Sync not successful, aborted.') |
|
return False |
|
|
|
# 4. Commit all changes. |
|
# 4.1 Try to get some information about the tool dir we are using. |
|
cmd = 'cd {0} && git log -1 --pretty=oneline'.format(tool_dir) |
|
tool_dir_extra_info = None |
|
ret, tool_dir_extra_info, _ = self._ce.RunCommandWOutput( |
|
cmd, |
|
print_to_console=False) |
|
commit_message = 'Synced with tool source tree at - "{0}".'.format(tool_dir) |
|
if not ret: |
|
commit_message += '\nGit log for {0}:\n{1}'.format( |
|
tool_dir, tool_dir_extra_info.strip()) |
|
|
|
if chrome_tool_repo.CommitLocally(commit_message): |
|
self._logger.LogError( |
|
'Commit to local branch "{0}" failed, aborted.'.format( |
|
TEMP_BRANCH_NAME)) |
|
return False |
|
return True |
|
|
|
def CheckoutBranch(self): |
|
"""Checkout working branch for the tools. |
|
|
|
Returns: |
|
True: if operation succeeds. |
|
""" |
|
|
|
if self._gcc_branch: |
|
rv = self.CheckoutToolBranch('gcc', self._gcc_branch) |
|
if rv: |
|
self._gcc_branch_tree = rv |
|
else: |
|
return False |
|
|
|
if self._binutils_branch: |
|
rv = self.CheckoutToolBranch('binutils', self._binutils_branch) |
|
if rv: |
|
self._binutils_branch_tree = rv |
|
else: |
|
return False |
|
|
|
return True |
|
|
|
def CheckoutToolBranch(self, tool_name, tool_branch): |
|
"""Checkout the tool branch for a certain tool. |
|
|
|
Args: |
|
tool_name: either 'gcc' or 'binutils' |
|
tool_branch: tool branch to use |
|
|
|
Returns: |
|
True: if operation succeeds. Otherwise False. |
|
""" |
|
|
|
chrome_tool_dir = self.GetChromeOsToolDir(tool_name) |
|
command = 'cd "{0}" && git checkout {1}'.format(chrome_tool_dir, |
|
tool_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(chrome_tool_dir, tool_branch) |
|
ret, stdout, _ = self._ce.RunCommandWOutput(command, |
|
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): |
|
tool_branch_tree = stdout.strip() |
|
self._logger.LogOutput('Find tree for {0} branch "{1}" - "{2}"'.format( |
|
tool_name, tool_branch, tool_branch_tree)) |
|
return tool_branch_tree |
|
self._logger.LogError(('Failed to checkout "{0}" or failed to ' |
|
'get tree value, aborted.').format(tool_branch)) |
|
return None |
|
|
|
def FindEbuildFile(self): |
|
"""Find the ebuild files for the tools. |
|
|
|
Returns: |
|
True: if operation succeeds. |
|
""" |
|
|
|
if self._gcc_branch: |
|
(rv, ef, efn) = self.FindToolEbuildFile('gcc') |
|
if rv: |
|
self._gcc_ebuild_file = ef |
|
self._gcc_ebuild_file_name = efn |
|
else: |
|
return False |
|
|
|
if self._binutils_branch: |
|
(rv, ef, efn) = self.FindToolEbuildFile('binutils') |
|
if rv: |
|
self._binutils_ebuild_file = ef |
|
self._binutils_ebuild_file_name = efn |
|
else: |
|
return False |
|
|
|
return True |
|
|
|
def FindToolEbuildFile(self, tool_name): |
|
"""Find ebuild file for a specific tool. |
|
|
|
Args: |
|
tool_name: either "gcc" or "binutils". |
|
|
|
Returns: |
|
A triplet that consisits of whether operation succeeds or not, |
|
tool ebuild file full path and tool ebuild file name. |
|
""" |
|
|
|
# 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 install a initial chroot, aborted.\n' |
|
'If previous bootstrap failed, do a ' |
|
'"cros_sdk --delete" to remove ' |
|
'in-complete chroot.')) |
|
return (False, None, None) |
|
|
|
rv, stdout, _ = self._ce.ChrootRunCommandWOutput( |
|
self._chromeos_root, |
|
'equery w sys-devel/{0}'.format(tool_name), |
|
print_to_console=True) |
|
if rv: |
|
self._logger.LogError(('Failed to execute inside chroot ' |
|
'"equery w sys-devel/{0}", aborted.').format( |
|
tool_name)) |
|
return (False, None, None) |
|
m = re.match(r'^.*/({0}/(.*\.ebuild))$'.format(EBUILD_PATH_PATTERN.format( |
|
tool_name)), stdout) |
|
if not m: |
|
self._logger.LogError( |
|
('Failed to find {0} ebuild file, aborted. ' |
|
'If previous bootstrap failed, do a "cros_sdk --delete" to remove ' |
|
'in-complete chroot.').format(tool_name)) |
|
return (False, None, None) |
|
tool_ebuild_file = os.path.join(self._chromeos_root, m.group(1)) |
|
tool_ebuild_file_name = m.group(2) |
|
|
|
return (True, tool_ebuild_file, tool_ebuild_file_name) |
|
|
|
def InplaceModifyEbuildFile(self): |
|
"""Modify the ebuild file. |
|
|
|
Returns: |
|
True if operation succeeds. |
|
""" |
|
|
|
# Note we shall not use remote branch name (eg. "cros/gcc.gnu.org/...") in |
|
# CROS_WORKON_COMMIT, we have to use GITHASH. So we call GitGetCommitHash on |
|
# tool_branch. |
|
tool = None |
|
toolbranch = None |
|
if self._gcc_branch: |
|
tool = 'gcc' |
|
toolbranch = self._gcc_branch |
|
tooltree = self._gcc_branch_tree |
|
toolebuild = self._gcc_ebuild_file |
|
elif self._binutils_branch: |
|
tool = 'binutils' |
|
toolbranch = self._binutils_branch |
|
tooltree = self._binutils_branch_tree |
|
toolebuild = self._binutils_ebuild_file |
|
|
|
|
|
assert tool |
|
|
|
# An example for the following variables would be: |
|
# tooldir = '~/android/master-ndk/toolchain/gcc/gcc-4.9' |
|
# tool_branch_githash = xxxxx |
|
# toolcomponents = toolchain/gcc |
|
tooldir = self.GetChromeOsToolDir(tool) |
|
toolgithash = misc.GitGetCommitHash(tooldir, toolbranch) |
|
if not toolgithash: |
|
return False |
|
toolcomponents = 'toolchain/{}'.format(tool) |
|
return self.InplaceModifyToolEbuildFile(toolcomponents, |
|
toolgithash, |
|
tooltree, |
|
toolebuild) |
|
|
|
@staticmethod |
|
def ResetToolEbuildFile(chromeos_root, tool_name): |
|
"""Reset tool ebuild file to clean state. |
|
|
|
Args: |
|
chromeos_root: chromeos source tree |
|
tool_name: either "gcc" or "binutils" |
|
|
|
Returns: |
|
True if operation succeds. |
|
""" |
|
rv = misc.GetGitChangesAsList( |
|
os.path.join(chromeos_root, CHROMIUMOS_OVERLAY_PATH), |
|
path=('sys-devel/{0}/{0}-*.ebuild'.format(tool_name)), |
|
staged=False) |
|
if rv: |
|
cmd = 'cd {0} && git checkout --'.format(os.path.join( |
|
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 the ebuild file. Please refer to log above.') |
|
return False |
|
else: |
|
logger.GetLogger().LogWarning( |
|
'Note - did not find any modified {0} ebuild file.'.format(tool_name)) |
|
# Fall through |
|
return True |
|
|
|
def GetChromeOsToolDir(self, tool_name): |
|
"""Return the chromeos git dir for a specific tool. |
|
|
|
Note, after we unified ChromeOs and Android, the tool dir is under |
|
ndk_dir/toolchain/[gcc,binutils]. |
|
|
|
Args: |
|
tool_name: either 'gcc' or 'binutils'. |
|
|
|
Returns: |
|
Absolute git path for the tool. |
|
""" |
|
|
|
tool_toppath = os.path.join(self._ndk_dir, 'toolchain', tool_name) |
|
# There may be sub-directories like 'binutils-2.25', 'binutils-2.24', |
|
# 'gcc-4.9', 'gcc-4.8', etc. find the newest binutils version. |
|
cmd = ('find {} -maxdepth 1 -type d -name "{}-*" ' |
|
'| sort -r | head -1').format(tool_toppath, tool_name) |
|
rv, out, _ = self._ce.RunCommandWOutput(cmd, print_to_console=False) |
|
if rv: |
|
return None |
|
repo = out.strip() |
|
|
|
# cros-workon eclass expects every CROS_WORKON_PROJECT ends with ".git". |
|
self._ce.RunCommand(('cd $(dirname {0}) && ' |
|
'ln -sf $(basename {0}) $(basename {0}).git').format( |
|
repo, print_to_console=True)) |
|
return repo |
|
|
|
|
|
def InplaceModifyToolEbuildFile(self, |
|
tool_components, |
|
tool_branch_githash, |
|
tool_branch_tree, |
|
tool_ebuild_file): |
|
"""Using sed to fill properly values into the ebuild file. |
|
|
|
Args: |
|
tool_components: either "toolchain/gcc" or "toolchain/binutils" |
|
tool_branch_githash: githash for tool_branch |
|
tool_branch_tree: treeish for the tool branch |
|
tool_ebuild_file: tool ebuild file |
|
|
|
Returns: |
|
True: if operation succeeded. |
|
""" |
|
|
|
command = ('sed -i ' |
|
'-e \'/^CROS_WORKON_REPO=".*"/i' |
|
' # The following line is modified by script.\' ' |
|
'-e \'s!^CROS_WORKON_REPO=".*"$!CROS_WORKON_REPO="{0}"!\' ' |
|
'-e \'/^CROS_WORKON_PROJECT=".*"/i' |
|
' # The following line is modified by script.\' ' |
|
'-e \'s!^CROS_WORKON_PROJECT=.*$!CROS_WORKON_PROJECT="{1}"!\' ' |
|
'-e \'/^CROS_WORKON_COMMIT=".*"/i' |
|
' # The following line is modified by script.\' ' |
|
'-e \'s!^CROS_WORKON_COMMIT=".*"$!CROS_WORKON_COMMIT="{2}"!\' ' |
|
'-e \'/^CROS_WORKON_TREE=".*"/i' |
|
' # The following line is modified by script.\' ' |
|
'-e \'s!^CROS_WORKON_TREE=".*"$!CROS_WORKON_TREE="{3}"!\' ' |
|
'{4}').format('/home/{}/ndk-root'.format(os.environ['USER']), |
|
tool_components, |
|
tool_branch_githash, |
|
tool_branch_tree, |
|
tool_ebuild_file) |
|
rv = self._ce.RunCommand(command) |
|
if rv: |
|
self._logger.LogError( |
|
'Failed to modify commit and tree value for "{0}"", aborted.'.format( |
|
tool_ebuild_file)) |
|
return False |
|
|
|
# Warn that the ebuild file has been modified. |
|
self._logger.LogWarning( |
|
('Ebuild file "{0}" is modified, to revert the file - \n' |
|
'bootstrap_compiler.py --chromeos_root={1} ' |
|
'--reset_tool_ebuild_file').format(tool_ebuild_file, |
|
self._chromeos_root)) |
|
return True |
|
|
|
def DoBuildForBoard(self): |
|
"""Build tool for a specific board. |
|
|
|
Returns: |
|
True if operation succeeds. |
|
""" |
|
|
|
if self._gcc_branch: |
|
if not self.DoBuildToolForBoard('gcc'): |
|
return False |
|
if self._binutils_branch: |
|
if not self.DoBuildToolForBoard('binutils'): |
|
return False |
|
return True |
|
|
|
def DoBuildToolForBoard(self, tool_name): |
|
"""Build a specific tool for a specific board. |
|
|
|
Args: |
|
tool_name: either "gcc" or "binutils" |
|
|
|
Returns: |
|
True if operation succeeds. |
|
""" |
|
|
|
chroot_ndk_root = os.path.join(self._chromeos_root, 'chroot', |
|
'home', os.environ['USER'], |
|
'ndk-root') |
|
self._ce.RunCommand('mkdir -p {}'.format(chroot_ndk_root)) |
|
if self._ce.RunCommand('sudo mount --bind {} {}'.format( |
|
self._ndk_dir, chroot_ndk_root)): |
|
self._logger.LogError('Failed to mount ndk dir into chroot') |
|
return False |
|
|
|
try: |
|
boards_to_build = self._board.split(',') |
|
target_built = set() |
|
failed = [] |
|
for board in boards_to_build: |
|
if board == 'host': |
|
command = 'sudo emerge sys-devel/{0}'.format(tool_name) |
|
else: |
|
target = misc.GetCtargetFromBoard(board, self._chromeos_root) |
|
if not target: |
|
self._logger.LogError( |
|
'Unsupported board "{0}", skip.'.format(board)) |
|
failed.append(board) |
|
continue |
|
# Skip this board if we have already built for a board that has the |
|
# same target. |
|
if target in target_built: |
|
self._logger.LogWarning( |
|
'Skipping toolchain for board "{}"'.format(board)) |
|
continue |
|
target_built.add(target) |
|
command = 'sudo emerge cross-{0}/{1}'.format(target, tool_name) |
|
|
|
rv = self._ce.ChrootRunCommand(self._chromeos_root, |
|
command, |
|
print_to_console=True) |
|
if rv: |
|
self._logger.LogError('Build {0} failed for {1}, aborted.'.format( |
|
tool_name, board)) |
|
failed.append(board) |
|
else: |
|
self._logger.LogOutput('Successfully built {0} for board {1}.'.format( |
|
tool_name, board)) |
|
finally: |
|
# Make sure we un-mount ndk-root before we leave here, regardless of the |
|
# build result of the tool. Otherwise we may inadvertently delete ndk-root |
|
# dir, which is not part of the chroot and could be disastrous. |
|
if chroot_ndk_root: |
|
if self._ce.RunCommand('sudo umount {}'.format(chroot_ndk_root)): |
|
self._logger.LogWarning(('Failed to umount "{}", please check ' |
|
'before deleting chroot.').format( |
|
chroot_ndk_root)) |
|
|
|
# Clean up soft links created during build. |
|
self._ce.RunCommand('cd {}/toolchain/{} && git clean -df'.format( |
|
self._ndk_dir, tool_name)) |
|
|
|
if failed: |
|
self._logger.LogError( |
|
'Failed to build {0} for the following board(s): "{1}"'.format( |
|
tool_name, ' '.join(failed))) |
|
return False |
|
# All boards build successfully |
|
return True |
|
|
|
def DoBootstrapping(self): |
|
"""Do bootstrapping the chroot. |
|
|
|
This step firstly downloads a prestine sdk, then use this sdk to build the |
|
new sdk, finally use the new sdk to build every host package. |
|
|
|
Returns: |
|
True if operation succeeds. |
|
""" |
|
|
|
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, 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 BuildAndInstallAmd64Host(self): |
|
"""Build amd64-host (host) packages. |
|
|
|
Build all host packages in the newly-bootstrapped 'chroot' using *NEW* |
|
toolchain. |
|
|
|
So actually we perform 2 builds of all host packages - |
|
1. build new toolchain using old toolchain and build all host packages |
|
using the newly built toolchain |
|
2. build the new toolchain again but using new toolchain built in step 1, |
|
and build all host packages using the newly built toolchain |
|
|
|
Returns: |
|
True if operation succeeds. |
|
""" |
|
|
|
cmd = ('cd {0} && cros_sdk -- -- ./setup_board --board=amd64-host ' |
|
'--accept_licenses=@CHROMEOS --skip_chroot_upgrade --nousepkg ' |
|
'--reuse_pkgs_from_local_boards').format(self._chromeos_root) |
|
rv = self._ce.RunCommand(cmd, print_to_console=True) |
|
if rv: |
|
self._logger.LogError('Build amd64-host failed.') |
|
return False |
|
|
|
# Package amd64-host into 'built-sdk.tar.xz'. |
|
sdk_package = os.path.join(self._chromeos_root, 'built-sdk.tar.xz') |
|
cmd = ('cd {0}/chroot/build/amd64-host && sudo XZ_OPT="-e9" ' |
|
'tar --exclude="usr/lib/debug/*" --exclude="packages/*" ' |
|
'--exclude="tmp/*" --exclude="usr/local/build/autotest/*" ' |
|
'--sparse -I xz -vcf {1} . && sudo chmod a+r {1}').format( |
|
self._chromeos_root, sdk_package) |
|
rv = self._ce.RunCommand(cmd, print_to_console=True) |
|
if rv: |
|
self._logger.LogError('Failed to create "built-sdk.tar.xz".') |
|
return False |
|
|
|
# Install amd64-host into a new chroot. |
|
cmd = ('cd {0} && cros_sdk --chroot new-sdk-chroot --download --replace ' |
|
'--nousepkg --url file://{1}').format(self._chromeos_root, |
|
sdk_package) |
|
rv = self._ce.RunCommand(cmd, print_to_console=True) |
|
if rv: |
|
self._logger.LogError('Failed to install "built-sdk.tar.xz".') |
|
return False |
|
self._logger.LogOutput( |
|
'Successfully installed built-sdk.tar.xz into a new chroot.\nAll done.') |
|
|
|
# Rename the newly created new-sdk-chroot to chroot. |
|
cmd = ('cd {0} && sudo mv chroot chroot-old && ' |
|
'sudo mv new-sdk-chroot chroot').format(self._chromeos_root) |
|
rv = self._ce.RunCommand(cmd, print_to_console=True) |
|
return rv == 0 |
|
|
|
def Do(self): |
|
"""Entrance of the class. |
|
|
|
Returns: |
|
True if everything is ok. |
|
""" |
|
|
|
if (self.SubmitToLocalBranch() and self.CheckoutBranch() and |
|
self.FindEbuildFile() and self.InplaceModifyEbuildFile()): |
|
if self._setup_tool_ebuild_file_only: |
|
# Everything is done, we are good. |
|
ret = True |
|
else: |
|
if self._board: |
|
ret = self.DoBuildForBoard() |
|
else: |
|
# This implies '--bootstrap'. |
|
ret = (self.DoBootstrapping() and (self._disable_2nd_bootstrap or |
|
self.BuildAndInstallAmd64Host())) |
|
else: |
|
ret = False |
|
return ret |
|
|
|
|
|
def Main(argv): |
|
parser = argparse.ArgumentParser() |
|
parser.add_argument('-c', |
|
'--chromeos_root', |
|
dest='chromeos_root', |
|
help=('Optional. ChromeOs root dir. ' |
|
'When not specified, chromeos root will be deduced' |
|
' from current working directory.')) |
|
parser.add_argument('--ndk_dir', |
|
dest='ndk_dir', |
|
help=('Topmost android ndk dir, required. ' |
|
'Do not need to include the "toolchain/*" part.')) |
|
parser.add_argument('--gcc_branch', |
|
dest='gcc_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_dir".')) |
|
parser.add_argument('--binutils_branch', |
|
dest='binutils_branch', |
|
help=('The branch to test against binutils. ' |
|
'This branch must be a local branch ' |
|
'inside "src/third_party/binutils". ' |
|
'Notice, this must not be used with ' |
|
'"--binutils_dir".')) |
|
parser.add_argument('-g', |
|
'--gcc_dir', |
|
dest='gcc_dir', |
|
help=('Use a local gcc tree to do bootstrapping. ' |
|
'Notice, this must not be used with ' |
|
'"--gcc_branch".')) |
|
parser.add_argument('--binutils_dir', |
|
dest='binutils_dir', |
|
help=('Use a local binutils tree to do bootstrapping. ' |
|
'Notice, this must not be used with ' |
|
'"--binutils_branch".')) |
|
parser.add_argument('--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_argument('--setup_tool_ebuild_file_only', |
|
dest='setup_tool_ebuild_file_only', |
|
default=False, |
|
action='store_true', |
|
help=('Setup gcc and/or binutils ebuild file ' |
|
'to pick up the branch (--gcc/binutils_branch) or ' |
|
'use gcc and/or binutils source ' |
|
'(--gcc/binutils_dir) and exit. Keep chroot as is.' |
|
' This should not be used with ' |
|
'--gcc/binutils_dir/branch options.')) |
|
parser.add_argument('--reset_tool_ebuild_file', |
|
dest='reset_tool_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/binutils ebuild file. ' |
|
'Use this option to reset (what this script has ' |
|
'done) and exit. This should not be used with -- ' |
|
'gcc/binutils_dir/branch options.')) |
|
parser.add_argument('--board', |
|
dest='board', |
|
default=None, |
|
help=('Only build toolchain for specific board(s). ' |
|
'Use "host" to build for host. ' |
|
'Use "," to seperate multiple boards. ' |
|
'This does not perform a chroot bootstrap.')) |
|
parser.add_argument('--bootstrap', |
|
dest='bootstrap', |
|
default=False, |
|
action='store_true', |
|
help=('Performs a chroot bootstrap. ' |
|
'Note, this will *destroy* your current chroot.')) |
|
parser.add_argument('--disable-2nd-bootstrap', |
|
dest='disable_2nd_bootstrap', |
|
default=False, |
|
action='store_true', |
|
help=('Disable a second bootstrap ' |
|
'(build of amd64-host stage).')) |
|
|
|
options = parser.parse_args(argv) |
|
# Trying to deduce chromeos root from current directory. |
|
if not options.chromeos_root: |
|
logger.GetLogger().LogOutput('Trying to deduce chromeos root ...') |
|
wdir = os.getcwd() |
|
while wdir and wdir != '/': |
|
if misc.IsChromeOsTree(wdir): |
|
logger.GetLogger().LogOutput('Find chromeos_root: {}'.format(wdir)) |
|
options.chromeos_root = wdir |
|
break |
|
wdir = os.path.dirname(wdir) |
|
|
|
if not options.chromeos_root: |
|
parser.error('Missing or failing to deduce 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 |
|
|
|
options.ndk_dir = os.path.expanduser(options.ndk_dir) |
|
if not options.ndk_dir: |
|
parser.error('Missing mandatory option "--ndk_dir".') |
|
return 1 |
|
|
|
# Some tolerance regarding user input. We only need the ndk_root part, do not |
|
# include toolchain/(gcc|binutils)/ part in this option. |
|
options.ndk_dir = re.sub( |
|
'/toolchain(/gcc|/binutils)?/?$', '', options.ndk_dir) |
|
|
|
if not (os.path.isdir(options.ndk_dir) and |
|
os.path.isdir(os.path.join(options.ndk_dir, 'toolchain'))): |
|
logger.GetLogger().LogError( |
|
'"toolchain" directory not found under "{0}".'.format(options.ndk_dir)) |
|
return 1 |
|
|
|
if options.fixperm: |
|
# Fix perm error before continuing. |
|
cmd = ( |
|
r'sudo find "{0}" \( -name ".cache" -type d -prune \) -o ' |
|
r'\( -name "chroot" -type d -prune \) -o ' |
|
r'\( -type f -exec chmod a+r {{}} \; \) -o ' |
|
r'\( -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_tool_ebuild_file: |
|
if (options.gcc_dir or options.gcc_branch or options.binutils_dir or |
|
options.binutils_branch): |
|
logger.GetLogger().LogWarning( |
|
'Ignoring any "--gcc/binutils_dir" and/or "--gcc/binutils_branch".') |
|
if options.setup_tool_ebuild_file_only: |
|
logger.GetLogger().LogError( |
|
('Conflict options "--reset_tool_ebuild_file" ' |
|
'and "--setup_tool_ebuild_file_only".')) |
|
return 1 |
|
rv = Bootstrapper.ResetToolEbuildFile(options.chromeos_root, 'gcc') |
|
rv1 = Bootstrapper.ResetToolEbuildFile(options.chromeos_root, 'binutils') |
|
return 0 if (rv and rv1) else 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.gcc_branch and options.gcc_dir: |
|
parser.error('Only one of "--gcc_dir" and "--gcc_branch" can be specified.') |
|
return 1 |
|
|
|
if options.binutils_dir: |
|
options.binutils_dir = os.path.abspath(os.path.expanduser( |
|
options.binutils_dir)) |
|
if not os.path.isdir(options.binutils_dir): |
|
logger.GetLogger().LogError('"{0}" does not exist.'.format( |
|
options.binutils_dir)) |
|
return 1 |
|
|
|
if options.binutils_branch and options.binutils_dir: |
|
parser.error('Only one of "--binutils_dir" and ' |
|
'"--binutils_branch" can be specified.') |
|
return 1 |
|
|
|
if (not (options.binutils_branch or options.binutils_dir or options.gcc_branch |
|
or options.gcc_dir)): |
|
parser.error(('At least one of "--gcc_dir", "--gcc_branch", ' |
|
'"--binutils_dir" and "--binutils_branch" must ' |
|
'be specified.')) |
|
return 1 |
|
|
|
if not options.board and not options.bootstrap: |
|
parser.error('You must specify either "--board" or "--bootstrap".') |
|
return 1 |
|
|
|
if (options.board and options.bootstrap and |
|
not options.setup_tool_ebuild_file_only): |
|
parser.error('You must specify only one of "--board" and "--bootstrap".') |
|
return 1 |
|
|
|
if not options.bootstrap and options.disable_2nd_bootstrap: |
|
parser.error('"--disable-2nd-bootstrap" has no effect ' |
|
'without specifying "--bootstrap".') |
|
return 1 |
|
|
|
if Bootstrapper( |
|
options.chromeos_root, |
|
options.ndk_dir, |
|
gcc_branch=options.gcc_branch, |
|
gcc_dir=options.gcc_dir, |
|
binutils_branch=options.binutils_branch, |
|
binutils_dir=options.binutils_dir, |
|
board=options.board, |
|
disable_2nd_bootstrap=options.disable_2nd_bootstrap, |
|
setup_tool_ebuild_file_only=options.setup_tool_ebuild_file_only).Do(): |
|
return 0 |
|
return 1 |
|
|
|
|
|
if __name__ == '__main__': |
|
retval = Main(sys.argv[1:]) |
|
sys.exit(retval)
|
|
|