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