OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """CBuildbot is wrapper around the build process used by the pre-flight queue""" | 7 """CBuildbot is wrapper around the build process used by the pre-flight queue""" |
8 | 8 |
9 import errno | 9 import errno |
10 import re | 10 import re |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 | 50 |
51 # Print out the command before running. | 51 # Print out the command before running. |
52 if print_cmd: | 52 if print_cmd: |
53 print >> sys.stderr, 'CBUILDBOT -- RunCommand: ', ' '.join(cmd) | 53 print >> sys.stderr, 'CBUILDBOT -- RunCommand: ', ' '.join(cmd) |
54 | 54 |
55 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, | 55 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, |
56 stdout=stdout, stderr=stderr) | 56 stdout=stdout, stderr=stderr) |
57 (output, error) = proc.communicate(input) | 57 (output, error) = proc.communicate(input) |
58 if exit_code: | 58 if exit_code: |
59 return proc.returncode | 59 return proc.returncode |
| 60 |
60 if not error_ok and proc.returncode != 0: | 61 if not error_ok and proc.returncode != 0: |
61 raise Exception('Command "%s" failed.\n' % (' '.join(cmd)) + | 62 raise Exception('Command "%s" failed.\n' % (' '.join(cmd)) + |
62 (error_message or error or output or '')) | 63 (error_message or error or output or '')) |
| 64 |
63 return output | 65 return output |
64 | 66 |
65 | 67 |
66 def MakeDir(path, parents=False): | 68 def MakeDir(path, parents=False): |
67 """Basic wrapper around os.mkdirs. | 69 """Basic wrapper around os.mkdirs. |
68 | 70 |
69 Keyword arguments: | 71 Keyword arguments: |
70 path -- Path to create. | 72 path -- Path to create. |
71 parents -- Follow mkdir -p logic. | 73 parents -- Follow mkdir -p logic. |
72 | 74 |
(...skipping 17 matching lines...) Expand all Loading... |
90 """ | 92 """ |
91 while retries > 0: | 93 while retries > 0: |
92 try: | 94 try: |
93 RunCommand(['repo', 'sync'], cwd=buildroot) | 95 RunCommand(['repo', 'sync'], cwd=buildroot) |
94 if rw_checkout: | 96 if rw_checkout: |
95 # Always re-run in case of new git repos or repo sync | 97 # Always re-run in case of new git repos or repo sync |
96 # failed in a previous run because of a forced Stop Build. | 98 # failed in a previous run because of a forced Stop Build. |
97 RunCommand(['repo', 'forall', '-c', 'git', 'config', | 99 RunCommand(['repo', 'forall', '-c', 'git', 'config', |
98 'url.ssh://git@gitrw.chromium.org:9222.pushinsteadof', | 100 'url.ssh://git@gitrw.chromium.org:9222.pushinsteadof', |
99 'http://git.chromium.org/git'], cwd=buildroot) | 101 'http://git.chromium.org/git'], cwd=buildroot) |
| 102 |
100 retries = 0 | 103 retries = 0 |
101 except: | 104 except: |
102 retries -= 1 | 105 retries -= 1 |
103 if retries > 0: | 106 if retries > 0: |
104 print >> sys.stderr, 'CBUILDBOT -- Repo Sync Failed, retrying' | 107 print >> sys.stderr, 'CBUILDBOT -- Repo Sync Failed, retrying' |
105 else: | 108 else: |
106 print >> sys.stderr, 'CBUILDBOT -- Retries exhausted' | 109 print >> sys.stderr, 'CBUILDBOT -- Retries exhausted' |
107 raise | 110 raise |
108 | 111 |
109 # =========================== Command Helpers ================================= | 112 # =========================== Command Helpers ================================= |
(...skipping 13 matching lines...) Expand all Loading... |
123 # Parse line using re to get tuple. | 126 # Parse line using re to get tuple. |
124 result_array = re.findall('.+name=\"([\w-]+)\".+path=\"(\S+)".+', output) | 127 result_array = re.findall('.+name=\"([\w-]+)\".+path=\"(\S+)".+', output) |
125 | 128 |
126 # Create the array. | 129 # Create the array. |
127 for result in result_array: | 130 for result in result_array: |
128 if len(result) != 2: | 131 if len(result) != 2: |
129 print >> sys.stderr, 'Found in correct xml object %s', result | 132 print >> sys.stderr, 'Found in correct xml object %s', result |
130 else: | 133 else: |
131 # Remove pre-pended src directory from manifest. | 134 # Remove pre-pended src directory from manifest. |
132 manifest_tuples.append([result[0], result[1].replace('src/', '')]) | 135 manifest_tuples.append([result[0], result[1].replace('src/', '')]) |
| 136 |
133 return manifest_tuples | 137 return manifest_tuples |
134 | 138 |
135 | 139 |
136 def _GetCrosWorkOnSrcPath(buildroot, board, package, debug=False): | 140 def _GetCrosWorkOnSrcPath(buildroot, board, package, debug=False): |
137 """Returns ${CROS_WORKON_SRC_PATH} for given package.""" | 141 """Returns ${CROS_WORKON_SRC_PATH} for given package.""" |
138 cwd = os.path.join(buildroot, 'src', 'scripts') | 142 cwd = os.path.join(buildroot, 'src', 'scripts') |
139 equery_cmd = ('equery-%s which %s' % (board, package)).split() | 143 equery_cmd = ('equery-%s which %s' % (board, package)).split() |
140 ebuild_path = RunCommand(equery_cmd, cwd=cwd, redirect_stdout=True, | 144 ebuild_path = RunCommand(equery_cmd, cwd=cwd, redirect_stdout=True, |
141 redirect_stderr=True, enter_chroot=True, | 145 redirect_stderr=True, enter_chroot=True, |
142 error_ok=True, print_cmd=debug) | 146 error_ok=True, print_cmd=debug) |
143 if ebuild_path: | 147 if ebuild_path: |
144 ebuild_cmd = ('ebuild-%s %s info' % (board, ebuild_path)).split() | 148 ebuild_cmd = ('ebuild-%s %s info' % (board, ebuild_path)).split() |
145 cros_workon_output = RunCommand(ebuild_cmd, cwd=cwd, | 149 cros_workon_output = RunCommand(ebuild_cmd, cwd=cwd, |
146 redirect_stdout=True, redirect_stderr=True, | 150 redirect_stdout=True, redirect_stderr=True, |
147 enter_chroot=True, print_cmd=debug) | 151 enter_chroot=True, print_cmd=debug) |
148 | 152 |
149 temp = re.findall('CROS_WORKON_SRCDIR="(\S+)"', cros_workon_output) | 153 temp = re.findall('CROS_WORKON_SRCDIR="(\S+)"', cros_workon_output) |
150 if temp: | 154 if temp: |
151 return temp[0] | 155 return temp[0] |
| 156 |
152 return None | 157 return None |
153 | 158 |
154 | 159 |
155 def _CreateRepoDictionary(buildroot, board, debug=False): | 160 def _CreateRepoDictionary(buildroot, board, debug=False): |
156 """Returns the repo->list_of_ebuilds dictionary.""" | 161 """Returns the repo->list_of_ebuilds dictionary.""" |
157 repo_dictionary = {} | 162 repo_dictionary = {} |
158 manifest_tuples = _GetAllGitRepos(buildroot) | 163 manifest_tuples = _GetAllGitRepos(buildroot) |
159 print >> sys.stderr, 'Creating dictionary of git repos to portage packages ...
' | 164 print >> sys.stderr, ( |
| 165 'Creating dictionary of git repos to portage packages ...') |
160 | 166 |
161 cwd = os.path.join(buildroot, 'src', 'scripts') | 167 cwd = os.path.join(buildroot, 'src', 'scripts') |
162 get_all_workon_pkgs_cmd = './cros_workon list --all'.split() | 168 get_all_workon_pkgs_cmd = './cros_workon list --all'.split() |
163 packages = RunCommand(get_all_workon_pkgs_cmd, cwd=cwd, | 169 packages = RunCommand(get_all_workon_pkgs_cmd, cwd=cwd, |
164 redirect_stdout=True, redirect_stderr=True, | 170 redirect_stdout=True, redirect_stderr=True, |
165 enter_chroot=True, print_cmd=debug) | 171 enter_chroot=True, print_cmd=debug) |
166 for package in packages.split(): | 172 for package in packages.split(): |
167 cros_workon_src_path = _GetCrosWorkOnSrcPath(buildroot, board, package) | 173 cros_workon_src_path = _GetCrosWorkOnSrcPath(buildroot, board, package) |
168 if cros_workon_src_path: | 174 if cros_workon_src_path: |
169 for tuple in manifest_tuples: | 175 for tuple in manifest_tuples: |
170 # This path tends to have the user's home_dir prepended to it. | 176 # This path tends to have the user's home_dir prepended to it. |
171 if cros_workon_src_path.endswith(tuple[1]): | 177 if cros_workon_src_path.endswith(tuple[1]): |
172 print >> sys.stderr, ('For %s found matching package %s' % | 178 print >> sys.stderr, ('For %s found matching package %s' % |
173 (tuple[0], package)) | 179 (tuple[0], package)) |
174 if repo_dictionary.has_key(tuple[0]): | 180 if repo_dictionary.has_key(tuple[0]): |
175 repo_dictionary[tuple[0]] += [package] | 181 repo_dictionary[tuple[0]] += [package] |
176 else: | 182 else: |
177 repo_dictionary[tuple[0]] = [package] | 183 repo_dictionary[tuple[0]] = [package] |
| 184 |
178 return repo_dictionary | 185 return repo_dictionary |
179 | 186 |
180 | 187 |
181 def _ParseRevisionString(revision_string, repo_dictionary): | 188 def _ParseRevisionString(revision_string, repo_dictionary): |
182 """Parses the given revision_string into a revision dictionary. | 189 """Parses the given revision_string into a revision dictionary. |
183 | 190 |
184 Returns a list of tuples that contain [portage_package_name, commit_id] to | 191 Returns a list of tuples that contain [portage_package_name, commit_id] to |
185 update. | 192 update. |
186 | 193 |
187 Keyword arguments: | 194 Keyword arguments: |
188 revision_string -- revision_string with format | 195 revision_string -- revision_string with format |
189 'repo1.git@commit_1 repo2.git@commit2 ...'. | 196 'repo1.git@commit_1 repo2.git@commit2 ...'. |
190 repo_dictionary -- dictionary with git repository names as keys (w/out git) | 197 repo_dictionary -- dictionary with git repository names as keys (w/out git) |
191 to portage package names. | 198 to portage package names. |
192 | 199 |
193 """ | 200 """ |
194 # Using a dictionary removes duplicates. | 201 # Using a dictionary removes duplicates. |
195 revisions = {} | 202 revisions = {} |
196 for revision in revision_string.split(): | 203 for revision in revision_string.split(): |
197 # Format 'package@commit-id'. | 204 # Format 'package@commit-id'. |
198 revision_tuple = revision.split('@') | 205 revision_tuple = revision.split('@') |
199 if len(revision_tuple) != 2: | 206 if len(revision_tuple) != 2: |
200 print >> sys.stderr, 'Incorrectly formatted revision %s' % revision | 207 print >> sys.stderr, 'Incorrectly formatted revision %s' % revision |
| 208 |
201 repo_name = revision_tuple[0].replace('.git', '') | 209 repo_name = revision_tuple[0].replace('.git', '') |
202 # Might not have entry if no matching ebuild. | 210 # Might not have entry if no matching ebuild. |
203 if repo_dictionary.has_key(repo_name): | 211 if repo_dictionary.has_key(repo_name): |
204 # May be many corresponding packages to a given git repo e.g. kernel). | 212 # May be many corresponding packages to a given git repo e.g. kernel). |
205 for package in repo_dictionary[repo_name]: | 213 for package in repo_dictionary[repo_name]: |
206 revisions[package] = revision_tuple[1] | 214 revisions[package] = revision_tuple[1] |
| 215 |
207 return revisions.items() | 216 return revisions.items() |
208 | 217 |
209 | 218 |
210 def _UprevFromRevisionList(buildroot, revision_list): | 219 def _UprevFromRevisionList(buildroot, revision_list): |
211 """Uprevs based on revision list.""" | 220 """Uprevs based on revision list.""" |
212 if not revision_list: | 221 if not revision_list: |
213 print >> sys.stderr, 'No packages found to uprev' | 222 print >> sys.stderr, 'No packages found to uprev' |
214 return | 223 return |
| 224 |
215 package_str = '' | 225 package_str = '' |
216 commit_str = '' | 226 commit_str = '' |
217 for package, revision in revision_list: | 227 for package, revision in revision_list: |
218 package_str += package + ' ' | 228 package_str += package + ' ' |
219 commit_str += revision + ' ' | 229 commit_str += revision + ' ' |
| 230 |
220 package_str = package_str.strip() | 231 package_str = package_str.strip() |
221 commit_str = commit_str.strip() | 232 commit_str = commit_str.strip() |
222 | 233 |
223 cwd = os.path.join(buildroot, 'src', 'scripts') | 234 cwd = os.path.join(buildroot, 'src', 'scripts') |
224 RunCommand(['./cros_mark_as_stable', | 235 RunCommand(['./cros_mark_as_stable', |
225 '--tracking_branch="cros/master"', | 236 '--tracking_branch="cros/master"', |
226 '--packages="%s"' % package_str, | 237 '--packages="%s"' % package_str, |
227 '--commit_ids="%s"' % commit_str, | 238 '--commit_ids="%s"' % commit_str, |
228 'commit'], | 239 'commit'], |
229 cwd=cwd, enter_chroot=True) | 240 cwd=cwd, enter_chroot=True) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
262 def _SetupBoard(buildroot, board='x86-generic'): | 273 def _SetupBoard(buildroot, board='x86-generic'): |
263 """Wrapper around setup_board.""" | 274 """Wrapper around setup_board.""" |
264 cwd = os.path.join(buildroot, 'src', 'scripts') | 275 cwd = os.path.join(buildroot, 'src', 'scripts') |
265 RunCommand(['./setup_board', '--fast', '--default', '--board=%s' % board], | 276 RunCommand(['./setup_board', '--fast', '--default', '--board=%s' % board], |
266 cwd=cwd) | 277 cwd=cwd) |
267 | 278 |
268 | 279 |
269 def _Build(buildroot): | 280 def _Build(buildroot): |
270 """Wrapper around build_packages.""" | 281 """Wrapper around build_packages.""" |
271 cwd = os.path.join(buildroot, 'src', 'scripts') | 282 cwd = os.path.join(buildroot, 'src', 'scripts') |
272 RunCommand(['./build_packages'], cwd=cwd) | 283 RunCommand(['./build_packages'], cwd=cwd, enter_chroot=True) |
| 284 |
| 285 def _BuildImage(buildroot): |
| 286 cwd = os.path.join(buildroot, 'src', 'scripts') |
| 287 RunCommand(['./build_image'], cwd=cwd) |
| 288 |
| 289 def _RunUnitTests(buildroot): |
| 290 cwd = os.path.join(buildroot, 'src', 'scripts') |
| 291 RunCommand(['./cros_run_unit_tests'], cwd=cwd) |
273 | 292 |
274 | 293 |
275 def _UprevPackages(buildroot, revisionfile, board): | 294 def _UprevPackages(buildroot, revisionfile, board): |
276 """Uprevs a package based on given revisionfile. | 295 """Uprevs a package based on given revisionfile. |
277 | 296 |
278 If revisionfile is set to None or does not resolve to an actual file, this | 297 If revisionfile is set to None or does not resolve to an actual file, this |
279 function will uprev all packages. | 298 function will uprev all packages. |
280 | 299 |
281 Keyword arguments: | 300 Keyword arguments: |
282 revisionfile -- string specifying a file that contains a list of revisions to | 301 revisionfile -- string specifying a file that contains a list of revisions to |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 RunCommand(['./cros_mark_as_stable', '--srcroot=..', | 340 RunCommand(['./cros_mark_as_stable', '--srcroot=..', |
322 '--tracking_branch="cros/master"', | 341 '--tracking_branch="cros/master"', |
323 '--push_options', '--bypass-hooks -f', 'push'], | 342 '--push_options', '--bypass-hooks -f', 'push'], |
324 cwd=cwd) | 343 cwd=cwd) |
325 | 344 |
326 | 345 |
327 def _GetConfig(config_name): | 346 def _GetConfig(config_name): |
328 """Gets the configuration for the build""" | 347 """Gets the configuration for the build""" |
329 default = config['default'] | 348 default = config['default'] |
330 buildconfig = {} | 349 buildconfig = {} |
331 if config.has_key(config_name): | 350 if not config.has_key(config_name): |
332 buildconfig = config[config_name] | 351 print >> sys.stderr, 'Non-existent configuration specified.' |
| 352 print >> sys.stderr, 'Please specify one of:' |
| 353 config_names = config.keys() |
| 354 config_names.sort() |
| 355 for name in config_names: |
| 356 print >> sys.stderr, ' %s' % name |
| 357 sys.exit(1) |
| 358 |
| 359 buildconfig = config[config_name] |
| 360 |
333 for key in default.iterkeys(): | 361 for key in default.iterkeys(): |
334 if not buildconfig.has_key(key): | 362 if not buildconfig.has_key(key): |
335 buildconfig[key] = default[key] | 363 buildconfig[key] = default[key] |
| 364 |
336 return buildconfig | 365 return buildconfig |
337 | 366 |
338 | 367 |
339 def main(): | 368 def main(): |
340 # Parse options | 369 # Parse options |
341 usage = "usage: %prog [options] cbuildbot_config" | 370 usage = "usage: %prog [options] cbuildbot_config" |
342 parser = optparse.OptionParser(usage=usage) | 371 parser = optparse.OptionParser(usage=usage) |
343 parser.add_option('-r', '--buildroot', | 372 parser.add_option('-r', '--buildroot', |
344 help='root directory where build occurs', default=".") | 373 help='root directory where build occurs', default=".") |
345 parser.add_option('-n', '--buildnumber', | 374 parser.add_option('-n', '--buildnumber', |
346 help='build number', type='int', default=0) | 375 help='build number', type='int', default=0) |
347 parser.add_option('-f', '--revisionfile', | 376 parser.add_option('-f', '--revisionfile', |
348 help='file where new revisions are stored') | 377 help='file where new revisions are stored') |
349 parser.add_option('--noclobber', action='store_false', dest='clobber', | 378 parser.add_option('--noclobber', action='store_false', dest='clobber', |
350 default=True, | 379 default=True, |
351 help='Disables clobbering the buildroot on failure') | 380 help='Disables clobbering the buildroot on failure') |
352 (options, args) = parser.parse_args() | 381 (options, args) = parser.parse_args() |
353 | 382 |
354 buildroot = options.buildroot | 383 buildroot = options.buildroot |
355 revisionfile = options.revisionfile | 384 revisionfile = options.revisionfile |
356 clobber = options.clobber | 385 clobber = options.clobber |
357 | 386 |
358 if len(args) == 1: | 387 if len(args) == 1: |
359 buildconfig = _GetConfig(args[0]) | 388 buildconfig = _GetConfig(args[0]) |
360 else: | 389 else: |
361 print >> sys.stderr, "Missing configuration description" | 390 print >> sys.stderr, "Missing configuration description" |
362 parser.print_usage() | 391 parser.print_usage() |
363 sys.exit(1) | 392 sys.exit(1) |
| 393 |
364 try: | 394 try: |
365 if not os.path.isdir(buildroot): | 395 if not os.path.isdir(buildroot): |
366 _FullCheckout(buildroot) | 396 _FullCheckout(buildroot) |
367 else: | 397 else: |
368 _IncrementalCheckout(buildroot) | 398 _IncrementalCheckout(buildroot) |
| 399 |
369 chroot_path = os.path.join(buildroot, 'chroot') | 400 chroot_path = os.path.join(buildroot, 'chroot') |
370 if not os.path.isdir(chroot_path): | 401 if not os.path.isdir(chroot_path): |
371 _MakeChroot(buildroot) | 402 _MakeChroot(buildroot) |
| 403 |
372 boardpath = os.path.join(chroot_path, 'build', buildconfig['board']) | 404 boardpath = os.path.join(chroot_path, 'build', buildconfig['board']) |
373 if not os.path.isdir(boardpath): | 405 if not os.path.isdir(boardpath): |
374 _SetupBoard(buildroot, board=buildconfig['board']) | 406 _SetupBoard(buildroot, board=buildconfig['board']) |
| 407 |
375 if buildconfig['uprev']: | 408 if buildconfig['uprev']: |
376 _UprevPackages(buildroot, revisionfile, board=buildconfig['board']) | 409 _UprevPackages(buildroot, revisionfile, board=buildconfig['board']) |
| 410 |
377 _Build(buildroot) | 411 _Build(buildroot) |
| 412 if buildconfig['unittests']: |
| 413 _RunUnitTests(buildroot) |
| 414 |
| 415 _BuildImage(buildroot) |
378 if buildconfig['uprev']: | 416 if buildconfig['uprev']: |
379 if buildconfig['master']: | 417 if buildconfig['master']: |
380 # Master bot needs to check if the other slaves completed. | 418 # Master bot needs to check if the other slaves completed. |
381 if cbuildbot_comm.HaveSlavesCompleted(config): | 419 if cbuildbot_comm.HaveSlavesCompleted(config): |
382 _UprevPush(buildroot) | 420 _UprevPush(buildroot) |
383 _UprevCleanup(buildroot) | 421 _UprevCleanup(buildroot) |
384 else: | 422 else: |
385 # At least one of the slaves failed or we timed out. | 423 # At least one of the slaves failed or we timed out. |
386 _UprevCleanup(buildroot) | 424 _UprevCleanup(buildroot) |
387 sys.stderr('CBUILDBOT - One of the slaves has failed!!!') | 425 sys.stderr('CBUILDBOT - One of the slaves has failed!!!') |
388 sys.exit(1) | 426 sys.exit(1) |
389 else: | 427 else: |
390 # Publish my status to the master. | 428 # Publish my status to the master if its expecting it. |
391 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_COMPLETE) | 429 if buildconfig['important']: |
| 430 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_COMPLETE) |
| 431 |
392 _UprevCleanup(buildroot) | 432 _UprevCleanup(buildroot) |
393 except: | 433 except: |
394 # Something went wrong, cleanup (being paranoid) for next build. | 434 # Something went wrong, cleanup (being paranoid) for next build. |
395 if clobber: | 435 if clobber: |
396 RunCommand(['sudo', 'rm', '-rf', buildroot], print_cmd=False) | 436 RunCommand(['sudo', 'rm', '-rf', buildroot], print_cmd=False) |
| 437 |
397 # Send failure to master bot. | 438 # Send failure to master bot. |
398 if not buildconfig['master']: | 439 if not buildconfig['master'] and buildconfig['important']: |
399 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_FAILED) | 440 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_FAILED) |
| 441 |
400 raise | 442 raise |
401 | 443 |
402 | 444 |
403 if __name__ == '__main__': | 445 if __name__ == '__main__': |
404 main() | 446 main() |
OLD | NEW |