blob: 1df4695259c391d943ddb55e44129062be64d81d [file] [log] [blame]
Caroline Tice88272d42016-01-13 09:48:29 -08001#!/usr/bin/python2
Han Shen819f8622014-04-08 16:57:38 -07002"""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
Caroline Tice88272d42016-01-13 09:48:29 -080011## 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.
Han Shen819f8622014-04-08 16:57:38 -070014./build_tool.py --gcc_dir=/loca/gcc/dir --bootstrap
15
Caroline Tice88272d42016-01-13 09:48:29 -080016## 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.
Han Shen819f8622014-04-08 16:57:38 -070020./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"""
Han Shen91445322013-03-20 13:43:31 -070027
Caroline Tice88272d42016-01-13 09:48:29 -080028from __future__ import print_function
29
Han Shen91445322013-03-20 13:43:31 -070030__author__ = 'shenhan@google.com (Han Shen)'
31
Caroline Tice88272d42016-01-13 09:48:29 -080032import argparse
Han Shen91445322013-03-20 13:43:31 -070033import os
34import re
Han Shen91445322013-03-20 13:43:31 -070035import sys
36
Han Shend1c31b22016-04-18 14:58:57 -070037
Caroline Tice88272d42016-01-13 09:48:29 -080038from cros_utils import command_executer
39from cros_utils import logger
40from cros_utils import misc
Han Shend1c31b22016-04-18 14:58:57 -070041import repo_to_repo
Han Shen91445322013-03-20 13:43:31 -070042
Han Shen819f8622014-04-08 16:57:38 -070043REPO_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
Han Shen91445322013-03-20 13:43:31 -070048
49class Bootstrapper(object):
Caroline Tice88272d42016-01-13 09:48:29 -080050 """Class that handles bootstrap process."""
Han Shen819f8622014-04-08 16:57:38 -070051
Luis Lozanof2a3ef42015-12-15 13:49:30 -080052 def __init__(self,
53 chromeos_root,
Han Shend1c31b22016-04-18 14:58:57 -070054 ndk_dir,
Luis Lozanof2a3ef42015-12-15 13:49:30 -080055 gcc_branch=None,
56 gcc_dir=None,
57 binutils_branch=None,
58 binutils_dir=None,
59 board=None,
60 disable_2nd_bootstrap=False,
Han Shen03d30982014-06-12 11:22:29 -070061 setup_tool_ebuild_file_only=False):
Han Shen91445322013-03-20 13:43:31 -070062 self._chromeos_root = chromeos_root
Han Shend1c31b22016-04-18 14:58:57 -070063 self._ndk_dir = ndk_dir
Han Shen819f8622014-04-08 16:57:38 -070064
65 self._gcc_branch = gcc_branch
66 self._gcc_branch_tree = None
Han Shen91445322013-03-20 13:43:31 -070067 self._gcc_dir = gcc_dir
Han Shen91445322013-03-20 13:43:31 -070068 self._gcc_ebuild_file = None
69 self._gcc_ebuild_file_name = None
Han Shen819f8622014-04-08 16:57:38 -070070
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
Han Shen03d30982014-06-12 11:22:29 -070082 self._disable_2nd_bootstrap = disable_2nd_bootstrap
Han Shen819f8622014-04-08 16:57:38 -070083
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
Han Shen91445322013-03-20 13:43:31 -070091
92 def SubmitToLocalBranch(self):
Han Shen819f8622014-04-08 16:57:38 -070093 """Copy source code to the chromium source tree and submit it locally."""
94 if self._gcc_dir:
Luis Lozanof2a3ef42015-12-15 13:49:30 -080095 if not self.SubmitToolToLocalBranch(tool_name='gcc',
96 tool_dir=self._gcc_dir):
Han Shen819f8622014-04-08 16:57:38 -070097 return False
98 self._gcc_branch = TEMP_BRANCH_NAME
Han Shen91445322013-03-20 13:43:31 -070099
Han Shen819f8622014-04-08 16:57:38 -0700100 if self._binutils_dir:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800101 if not self.SubmitToolToLocalBranch(tool_name='binutils',
102 tool_dir=self._binutils_dir):
Han Shen819f8622014-04-08 16:57:38 -0700103 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
Caroline Tice88272d42016-01-13 09:48:29 -0800114
Han Shen819f8622014-04-08 16:57:38 -0700115 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
Han Shen91445322013-03-20 13:43:31 -0700120 # user provided.
Han Shen819f8622014-04-08 16:57:38 -0700121 chrome_tool_dir = self.GetChromeOsToolDir(tool_name)
Han Shen91445322013-03-20 13:43:31 -0700122
123 # 0. Test to see if git tree is free of local changes.
Han Shen819f8622014-04-08 16:57:38 -0700124 if not misc.IsGitTreeClean(chrome_tool_dir):
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800125 self._logger.LogError('Git repository "{0}" not clean, aborted.'.format(
126 chrome_tool_dir))
Han Shen91445322013-03-20 13:43:31 -0700127 return False
128
129 # 1. Checkout/create a (new) branch for testing.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800130 command = 'cd "{0}" && git checkout -B {1}'.format(chrome_tool_dir,
131 TEMP_BRANCH_NAME)
Han Shen91445322013-03-20 13:43:31 -0700132 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
Han Shen819f8622014-04-08 16:57:38 -0700137 if self.IsTreeSame(tool_dir, chrome_tool_dir):
138 self._logger.LogOutput(
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800139 '"{0}" and "{1}" are the same, sync skipped.'.format(tool_dir,
140 chrome_tool_dir))
Han Shen819f8622014-04-08 16:57:38 -0700141 return True
Han Shen91445322013-03-20 13:43:31 -0700142
Han Shen819f8622014-04-08 16:57:38 -0700143 # 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)
Caroline Tice88272d42016-01-13 09:48:29 -0800146 chrome_tool_repo.SetRoot(chrome_tool_dir)
Han Shen03d30982014-06-12 11:22:29 -0700147 # 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 '
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800151 r' -type d -exec rm -fr {{}} \; \)'.format(chrome_tool_dir))
Han Shen819f8622014-04-08 16:57:38 -0700152 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):
Han Shen91445322013-03-20 13:43:31 -0700156 self._logger.LogOutput('Sync successfully done.')
Han Shen819f8622014-04-08 16:57:38 -0700157 else:
158 self._logger.LogError('Sync not successful, aborted.')
159 return False
Han Shen91445322013-03-20 13:43:31 -0700160
161 # 4. Commit all changes.
Han Shen03d30982014-06-12 11:22:29 -0700162 # 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
Luis Lozano036c9232015-12-10 10:47:01 -0800165 ret, tool_dir_extra_info, _ = self._ce.RunCommandWOutput(
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800166 cmd,
167 print_to_console=False)
Han Shen03d30982014-06-12 11:22:29 -0700168 commit_message = 'Synced with tool source tree at - "{0}".'.format(tool_dir)
169 if not ret:
Han Shend1c31b22016-04-18 14:58:57 -0700170 commit_message += '\nGit log for {0}:\n{1}'.format(
171 tool_dir, tool_dir_extra_info.strip())
Han Shen03d30982014-06-12 11:22:29 -0700172
173 if chrome_tool_repo.CommitLocally(commit_message):
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800174 self._logger.LogError(
175 'Commit to local branch "{0}" failed, aborted.'.format(
176 TEMP_BRANCH_NAME))
Han Shen91445322013-03-20 13:43:31 -0700177 return False
178 return True
179
180 def CheckoutBranch(self):
Han Shen819f8622014-04-08 16:57:38 -0700181 """Checkout working branch for the tools.
Han Shen91445322013-03-20 13:43:31 -0700182
Han Shen819f8622014-04-08 16:57:38 -0700183 Returns:
184 True: if operation succeeds.
185 """
Han Shen91445322013-03-20 13:43:31 -0700186
Han Shen819f8622014-04-08 16:57:38 -0700187 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
Han Shen91445322013-03-20 13:43:31 -0700201 return True
202
Han Shen819f8622014-04-08 16:57:38 -0700203 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
Caroline Tice88272d42016-01-13 09:48:29 -0800209
Han Shen819f8622014-04-08 16:57:38 -0700210 Returns:
211 True: if operation succeeds. Otherwise False.
Han Shen91445322013-03-20 13:43:31 -0700212 """
Han Shen819f8622014-04-08 16:57:38 -0700213
214 chrome_tool_dir = self.GetChromeOsToolDir(tool_name)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800215 command = 'cd "{0}" && git checkout {1}'.format(chrome_tool_dir,
216 tool_branch)
Han Shen819f8622014-04-08 16:57:38 -0700217 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)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800222 ret, stdout, _ = self._ce.RunCommandWOutput(command,
223 print_to_console=False)
Han Shen819f8622014-04-08 16:57:38 -0700224 # Pipe operation always has a zero return value. So need to check if
225 # stdout is valid.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800226 if not ret and stdout and re.match('[0-9a-h]{40}', stdout.strip(),
227 re.IGNORECASE):
Han Shen819f8622014-04-08 16:57:38 -0700228 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".
Caroline Tice88272d42016-01-13 09:48:29 -0800266
Han Shen819f8622014-04-08 16:57:38 -0700267 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.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800273 if not os.path.exists(os.path.join(
274 self._chromeos_root, 'chroot')) and self._ce.RunCommand(
Han Shen819f8622014-04-08 16:57:38 -0700275 '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
Luis Lozano036c9232015-12-10 10:47:01 -0800282 rv, stdout, _ = self._ce.ChrootRunCommandWOutput(
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800283 self._chromeos_root,
284 'equery w sys-devel/{0}'.format(tool_name),
Luis Lozano036c9232015-12-10 10:47:01 -0800285 print_to_console=True)
Han Shen819f8622014-04-08 16:57:38 -0700286 if rv:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800287 self._logger.LogError(('Failed to execute inside chroot '
288 '"equery w sys-devel/{0}", aborted.').format(
289 tool_name))
Han Shen819f8622014-04-08 16:57:38 -0700290 return (False, None, None)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800291 m = re.match(r'^.*/({0}/(.*\.ebuild))$'.format(EBUILD_PATH_PATTERN.format(
292 tool_name)), stdout)
Han Shen819f8622014-04-08 16:57:38 -0700293 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.
Han Shend1c31b22016-04-18 14:58:57 -0700314 tool = None
315 toolbranch = None
Han Shen819f8622014-04-08 16:57:38 -0700316 if self._gcc_branch:
Han Shend1c31b22016-04-18 14:58:57 -0700317 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
Han Shen819f8622014-04-08 16:57:38 -0700326
Han Shend1c31b22016-04-18 14:58:57 -0700327
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)
Han Shen819f8622014-04-08 16:57:38 -0700343
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"
Caroline Tice88272d42016-01-13 09:48:29 -0800351
Han Shen819f8622014-04-08 16:57:38 -0700352 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
Han Shend1c31b22016-04-18 14:58:57 -0700378 Note, after we unified ChromeOs and Android, the tool dir is under
379 ndk_dir/toolchain/[gcc,binutils].
380
Han Shen819f8622014-04-08 16:57:38 -0700381 Args:
382 tool_name: either 'gcc' or 'binutils'.
Caroline Tice88272d42016-01-13 09:48:29 -0800383
Han Shen819f8622014-04-08 16:57:38 -0700384 Returns:
385 Absolute git path for the tool.
386 """
387
Han Shend1c31b22016-04-18 14:58:57 -0700388 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()
Han Shen819f8622014-04-08 16:57:38 -0700397
Han Shend1c31b22016-04-18 14:58:57 -0700398 # 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,
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800409 tool_ebuild_file):
Han Shen819f8622014-04-08 16:57:38 -0700410 """Using sed to fill properly values into the ebuild file.
411
412 Args:
Han Shend1c31b22016-04-18 14:58:57 -0700413 tool_components: either "toolchain/gcc" or "toolchain/binutils"
Han Shen819f8622014-04-08 16:57:38 -0700414 tool_branch_githash: githash for tool_branch
415 tool_branch_tree: treeish for the tool branch
416 tool_ebuild_file: tool ebuild file
Caroline Tice88272d42016-01-13 09:48:29 -0800417
Han Shen819f8622014-04-08 16:57:38 -0700418 Returns:
419 True: if operation succeeded.
420 """
421
422 command = ('sed -i '
Han Shend1c31b22016-04-18 14:58:57 -0700423 '-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}"!\' '
Han Shen819f8622014-04-08 16:57:38 -0700429 '-e \'/^CROS_WORKON_COMMIT=".*"/i'
430 ' # The following line is modified by script.\' '
Han Shend1c31b22016-04-18 14:58:57 -0700431 '-e \'s!^CROS_WORKON_COMMIT=".*"$!CROS_WORKON_COMMIT="{2}"!\' '
Han Shen819f8622014-04-08 16:57:38 -0700432 '-e \'/^CROS_WORKON_TREE=".*"/i'
433 ' # The following line is modified by script.\' '
Han Shend1c31b22016-04-18 14:58:57 -0700434 '-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,
Han Shen819f8622014-04-08 16:57:38 -0700439 tool_ebuild_file)
Han Shen91445322013-03-20 13:43:31 -0700440 rv = self._ce.RunCommand(command)
441 if rv:
442 self._logger.LogError(
Han Shen819f8622014-04-08 16:57:38 -0700443 '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} '
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800451 '--reset_tool_ebuild_file').format(tool_ebuild_file,
452 self._chromeos_root))
Han Shen819f8622014-04-08 16:57:38 -0700453 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"
Caroline Tice88272d42016-01-13 09:48:29 -0800475
Han Shen819f8622014-04-08 16:57:38 -0700476 Returns:
477 True if operation succeeds.
478 """
479
Han Shend1c31b22016-04-18 14:58:57 -0700480 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
Han Shen819f8622014-04-08 16:57:38 -0700488
Han Shend1c31b22016-04-18 14:58:57 -0700489 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))
Han Shenfe3001c2014-04-28 16:36:28 -0700518 failed.append(board)
Han Shend1c31b22016-04-18 14:58:57 -0700519 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))
Han Shenfe3001c2014-04-28 16:36:28 -0700531
Han Shend1c31b22016-04-18 14:58:57 -0700532 # Clean up soft links created during build.
533 self._ce.RunCommand('cd {}/toolchain/{} && git clean -df'.format(
534 self._ndk_dir, tool_name))
Han Shenfe3001c2014-04-28 16:36:28 -0700535
Han Shen8dfdabd2014-05-08 10:15:09 -0700536 if failed:
Han Shen819f8622014-04-08 16:57:38 -0700537 self._logger.LogError(
Han Shenfe3001c2014-04-28 16:36:28 -0700538 'Failed to build {0} for the following board(s): "{1}"'.format(
539 tool_name, ' '.join(failed)))
Han Shen91445322013-03-20 13:43:31 -0700540 return False
Han Shenfe3001c2014-04-28 16:36:28 -0700541 # All boards build successfully
Han Shen91445322013-03-20 13:43:31 -0700542 return True
543
544 def DoBootstrapping(self):
Han Shen819f8622014-04-08 16:57:38 -0700545 """Do bootstrapping the chroot.
546
Han Shen03d30982014-06-12 11:22:29 -0700547 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
Han Shen819f8622014-04-08 16:57:38 -0700550 Returns:
551 True if operation succeeds.
552 """
553
Han Shen91445322013-03-20 13:43:31 -0700554 logfile = os.path.join(self._chromeos_root, 'bootstrap.log')
Han Shen819f8622014-04-08 16:57:38 -0700555 command = 'cd "{0}" && cros_sdk --delete --bootstrap |& tee "{1}"'.format(
556 self._chromeos_root, logfile)
Luis Lozano036c9232015-12-10 10:47:01 -0800557 rv = self._ce.RunCommand(command, print_to_console=True)
Han Shen91445322013-03-20 13:43:31 -0700558 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
Han Shen03d30982014-06-12 11:22:29 -0700566 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)
Luis Lozano036c9232015-12-10 10:47:01 -0800585 rv = self._ce.RunCommand(cmd, print_to_console=True)
Han Shen03d30982014-06-12 11:22:29 -0700586 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)
Luis Lozano036c9232015-12-10 10:47:01 -0800597 rv = self._ce.RunCommand(cmd, print_to_console=True)
Han Shen03d30982014-06-12 11:22:29 -0700598 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 '
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800604 '--nousepkg --url file://{1}').format(self._chromeos_root,
605 sdk_package)
Luis Lozano036c9232015-12-10 10:47:01 -0800606 rv = self._ce.RunCommand(cmd, print_to_console=True)
Han Shen03d30982014-06-12 11:22:29 -0700607 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)
Luis Lozano036c9232015-12-10 10:47:01 -0800616 rv = self._ce.RunCommand(cmd, print_to_console=True)
Han Shen03d30982014-06-12 11:22:29 -0700617 return rv == 0
618
Han Shen91445322013-03-20 13:43:31 -0700619 def Do(self):
Han Shen819f8622014-04-08 16:57:38 -0700620 """Entrance of the class.
621
622 Returns:
623 True if everything is ok.
624 """
625
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800626 if (self.SubmitToLocalBranch() and self.CheckoutBranch() and
627 self.FindEbuildFile() and self.InplaceModifyEbuildFile()):
Han Shen819f8622014-04-08 16:57:38 -0700628 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'.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800636 ret = (self.DoBootstrapping() and (self._disable_2nd_bootstrap or
637 self.BuildAndInstallAmd64Host()))
Han Shen91445322013-03-20 13:43:31 -0700638 else:
639 ret = False
Han Shen91445322013-03-20 13:43:31 -0700640 return ret
641
642
643def Main(argv):
Caroline Tice88272d42016-01-13 09:48:29 -0800644 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.'))
Han Shend1c31b22016-04-18 14:58:57 -0700651 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.'))
Caroline Tice88272d42016-01-13 09:48:29 -0800655 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).'))
Han Shen819f8622014-04-08 16:57:38 -0700726
Caroline Tice88272d42016-01-13 09:48:29 -0800727 options = parser.parse_args(argv)
Han Shen819f8622014-04-08 16:57:38 -0700728 # Trying to deduce chromeos root from current directory.
Han Shen91445322013-03-20 13:43:31 -0700729 if not options.chromeos_root:
Han Shen819f8622014-04-08 16:57:38 -0700730 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".')
Han Shen91445322013-03-20 13:43:31 -0700741 return 1
742
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800743 options.chromeos_root = os.path.abspath(os.path.expanduser(
744 options.chromeos_root))
Han Shen91445322013-03-20 13:43:31 -0700745
746 if not os.path.isdir(options.chromeos_root):
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800747 logger.GetLogger().LogError('"{0}" does not exist.'.format(
748 options.chromeos_root))
Han Shen91445322013-03-20 13:43:31 -0700749 return 1
750
Han Shend1c31b22016-04-18 14:58:57 -0700751 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
Han Shen91445322013-03-20 13:43:31 -0700767 if options.fixperm:
768 # Fix perm error before continuing.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800769 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)
Han Shen91445322013-03-20 13:43:31 -0700774 logger.GetLogger().LogOutput(
Han Shen819f8622014-04-08 16:57:38 -0700775 'Fixing perm issues for chromeos root, this might take some time.')
Han Shen91445322013-03-20 13:43:31 -0700776 command_executer.GetCommandExecuter().RunCommand(cmd)
777
Han Shen819f8622014-04-08 16:57:38 -0700778 if options.reset_tool_ebuild_file:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800779 if (options.gcc_dir or options.gcc_branch or options.binutils_dir or
780 options.binutils_branch):
Han Shen91445322013-03-20 13:43:31 -0700781 logger.GetLogger().LogWarning(
Han Shen819f8622014-04-08 16:57:38 -0700782 '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".'))
Han Shen91445322013-03-20 13:43:31 -0700787 return 1
Han Shen819f8622014-04-08 16:57:38 -0700788 rv = Bootstrapper.ResetToolEbuildFile(options.chromeos_root, 'gcc')
789 rv1 = Bootstrapper.ResetToolEbuildFile(options.chromeos_root, 'binutils')
790 return 0 if (rv and rv1) else 1
Han Shen91445322013-03-20 13:43:31 -0700791
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):
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800795 logger.GetLogger().LogError('"{0}" does not exist.'.format(
796 options.gcc_dir))
Han Shen91445322013-03-20 13:43:31 -0700797 return 1
798
Han Shen819f8622014-04-08 16:57:38 -0700799 if options.gcc_branch and options.gcc_dir:
800 parser.error('Only one of "--gcc_dir" and "--gcc_branch" can be specified.')
Han Shen91445322013-03-20 13:43:31 -0700801 return 1
Han Shen819f8622014-04-08 16:57:38 -0700802
803 if options.binutils_dir:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800804 options.binutils_dir = os.path.abspath(os.path.expanduser(
805 options.binutils_dir))
Han Shen819f8622014-04-08 16:57:38 -0700806 if not os.path.isdir(options.binutils_dir):
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800807 logger.GetLogger().LogError('"{0}" does not exist.'.format(
808 options.binutils_dir))
Han Shen819f8622014-04-08 16:57:38 -0700809 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
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800816 if (not (options.binutils_branch or options.binutils_dir or options.gcc_branch
817 or options.gcc_dir)):
Han Shen819f8622014-04-08 16:57:38 -0700818 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
Han Shen03d30982014-06-12 11:22:29 -0700827 if (options.board and options.bootstrap and
828 not options.setup_tool_ebuild_file_only):
Han Shen819f8622014-04-08 16:57:38 -0700829 parser.error('You must specify only one of "--board" and "--bootstrap".')
Han Shen91445322013-03-20 13:43:31 -0700830 return 1
831
Han Shen03d30982014-06-12 11:22:29 -0700832 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
Han Shen91445322013-03-20 13:43:31 -0700837 if Bootstrapper(
Han Shen819f8622014-04-08 16:57:38 -0700838 options.chromeos_root,
Han Shend1c31b22016-04-18 14:58:57 -0700839 options.ndk_dir,
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800840 gcc_branch=options.gcc_branch,
841 gcc_dir=options.gcc_dir,
Han Shen819f8622014-04-08 16:57:38 -0700842 binutils_branch=options.binutils_branch,
843 binutils_dir=options.binutils_dir,
844 board=options.board,
Han Shen03d30982014-06-12 11:22:29 -0700845 disable_2nd_bootstrap=options.disable_2nd_bootstrap,
Han Shen819f8622014-04-08 16:57:38 -0700846 setup_tool_ebuild_file_only=options.setup_tool_ebuild_file_only).Do():
Han Shen91445322013-03-20 13:43:31 -0700847 return 0
848 return 1
849
850
851if __name__ == '__main__':
Caroline Tice88272d42016-01-13 09:48:29 -0800852 retval = Main(sys.argv[1:])
Han Shen91445322013-03-20 13:43:31 -0700853 sys.exit(retval)