| 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 |
| 11 import optparse | 11 import optparse |
| 12 import os | 12 import os |
| 13 import subprocess | |
| 14 import sys | 13 import sys |
| 15 | 14 |
| 16 import cbuildbot_comm | 15 import cbuildbot_comm |
| 17 from cbuildbot_config import config | 16 from cbuildbot_config import config |
| 18 | 17 |
| 18 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
| 19 from cros_build_lib import Die, Info, RunCommand, Warning |
| 20 |
| 19 _DEFAULT_RETRIES = 3 | 21 _DEFAULT_RETRIES = 3 |
| 20 | 22 |
| 21 # ======================== Utility functions ================================ | 23 # ======================== Utility functions ================================ |
| 22 | 24 |
| 23 def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None, | |
| 24 exit_code=False, redirect_stdout=False, redirect_stderr=False, | |
| 25 cwd=None, input=None, enter_chroot=False): | |
| 26 """Runs a shell command. | |
| 27 | |
| 28 Keyword arguments: | |
| 29 print_cmd -- prints the command before running it. | |
| 30 error_ok -- does not raise an exception on error. | |
| 31 error_message -- prints out this message when an error occurrs. | |
| 32 exit_code -- returns the return code of the shell command. | |
| 33 redirect_stdout -- returns the stdout. | |
| 34 redirect_stderr -- holds stderr output until input is communicated. | |
| 35 cwd -- the working directory to run this cmd. | |
| 36 input -- input to pipe into this command through stdin. | |
| 37 enter_chroot -- this command should be run from within the chroot. | |
| 38 | |
| 39 """ | |
| 40 # Set default for variables. | |
| 41 stdout = None | |
| 42 stderr = None | |
| 43 stdin = None | |
| 44 | |
| 45 # Modify defaults based on parameters. | |
| 46 if redirect_stdout: stdout = subprocess.PIPE | |
| 47 if redirect_stderr: stderr = subprocess.PIPE | |
| 48 if input: stdin = subprocess.PIPE | |
| 49 if enter_chroot: cmd = ['./enter_chroot.sh', '--'] + cmd | |
| 50 | |
| 51 # Print out the command before running. | |
| 52 if print_cmd: | |
| 53 print >> sys.stderr, 'CBUILDBOT -- RunCommand: ', ' '.join(cmd) | |
| 54 | |
| 55 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, | |
| 56 stdout=stdout, stderr=stderr) | |
| 57 (output, error) = proc.communicate(input) | |
| 58 if exit_code: | |
| 59 return proc.returncode | |
| 60 | |
| 61 if not error_ok and proc.returncode != 0: | |
| 62 raise Exception('Command "%s" failed.\n' % (' '.join(cmd)) + | |
| 63 (error_message or error or output or '')) | |
| 64 | |
| 65 return output | |
| 66 | |
| 67 | |
| 68 def MakeDir(path, parents=False): | 25 def MakeDir(path, parents=False): |
| 69 """Basic wrapper around os.mkdirs. | 26 """Basic wrapper around os.mkdirs. |
| 70 | 27 |
| 71 Keyword arguments: | 28 Keyword arguments: |
| 72 path -- Path to create. | 29 path -- Path to create. |
| 73 parents -- Follow mkdir -p logic. | 30 parents -- Follow mkdir -p logic. |
| 74 | 31 |
| 75 """ | 32 """ |
| 76 try: | 33 try: |
| 77 os.makedirs(path) | 34 os.makedirs(path) |
| (...skipping 19 matching lines...) Expand all Loading... |
| 97 # Always re-run in case of new git repos or repo sync | 54 # Always re-run in case of new git repos or repo sync |
| 98 # failed in a previous run because of a forced Stop Build. | 55 # failed in a previous run because of a forced Stop Build. |
| 99 RunCommand(['repo', 'forall', '-c', 'git', 'config', | 56 RunCommand(['repo', 'forall', '-c', 'git', 'config', |
| 100 'url.ssh://git@gitrw.chromium.org:9222.pushinsteadof', | 57 'url.ssh://git@gitrw.chromium.org:9222.pushinsteadof', |
| 101 'http://git.chromium.org/git'], cwd=buildroot) | 58 'http://git.chromium.org/git'], cwd=buildroot) |
| 102 | 59 |
| 103 retries = 0 | 60 retries = 0 |
| 104 except: | 61 except: |
| 105 retries -= 1 | 62 retries -= 1 |
| 106 if retries > 0: | 63 if retries > 0: |
| 107 print >> sys.stderr, 'CBUILDBOT -- Repo Sync Failed, retrying' | 64 Warning('CBUILDBOT -- Repo Sync Failed, retrying') |
| 108 else: | 65 else: |
| 109 print >> sys.stderr, 'CBUILDBOT -- Retries exhausted' | 66 Warning('CBUILDBOT -- Retries exhausted') |
| 110 raise | 67 raise |
| 111 | 68 |
| 112 # =========================== Command Helpers ================================= | 69 # =========================== Command Helpers ================================= |
| 113 | 70 |
| 114 def _GetAllGitRepos(buildroot, debug=False): | 71 def _GetAllGitRepos(buildroot, debug=False): |
| 115 """Returns a list of tuples containing [git_repo, src_path].""" | 72 """Returns a list of tuples containing [git_repo, src_path].""" |
| 116 manifest_tuples = [] | 73 manifest_tuples = [] |
| 117 # Gets all the git repos from a full repo manifest. | 74 # Gets all the git repos from a full repo manifest. |
| 118 repo_cmd = "repo manifest -o -".split() | 75 repo_cmd = "repo manifest -o -".split() |
| 119 output = RunCommand(repo_cmd, cwd=buildroot, redirect_stdout=True, | 76 output = RunCommand(repo_cmd, cwd=buildroot, redirect_stdout=True, |
| 120 redirect_stderr=True, print_cmd=debug) | 77 redirect_stderr=True, print_cmd=debug) |
| 121 | 78 |
| 122 # Extract all lines containg a project. | 79 # Extract all lines containg a project. |
| 123 extract_cmd = ["grep", "project name="] | 80 extract_cmd = ["grep", "project name="] |
| 124 output = RunCommand(extract_cmd, cwd=buildroot, input=output, | 81 output = RunCommand(extract_cmd, cwd=buildroot, input=output, |
| 125 redirect_stdout=True, print_cmd=debug) | 82 redirect_stdout=True, print_cmd=debug) |
| 126 # Parse line using re to get tuple. | 83 # Parse line using re to get tuple. |
| 127 result_array = re.findall('.+name=\"([\w-]+)\".+path=\"(\S+)".+', output) | 84 result_array = re.findall('.+name=\"([\w-]+)\".+path=\"(\S+)".+', output) |
| 128 | 85 |
| 129 # Create the array. | 86 # Create the array. |
| 130 for result in result_array: | 87 for result in result_array: |
| 131 if len(result) != 2: | 88 if len(result) != 2: |
| 132 print >> sys.stderr, 'Found in correct xml object %s', result | 89 Warning('Found incorrect xml object %s' % result) |
| 133 else: | 90 else: |
| 134 # Remove pre-pended src directory from manifest. | 91 # Remove pre-pended src directory from manifest. |
| 135 manifest_tuples.append([result[0], result[1].replace('src/', '')]) | 92 manifest_tuples.append([result[0], result[1].replace('src/', '')]) |
| 136 | 93 |
| 137 return manifest_tuples | 94 return manifest_tuples |
| 138 | 95 |
| 139 | 96 |
| 140 def _GetCrosWorkOnSrcPath(buildroot, board, package, debug=False): | 97 def _GetCrosWorkOnSrcPath(buildroot, board, package, debug=False): |
| 141 """Returns ${CROS_WORKON_SRC_PATH} for given package.""" | 98 """Returns ${CROS_WORKON_SRC_PATH} for given package.""" |
| 142 cwd = os.path.join(buildroot, 'src', 'scripts') | 99 cwd = os.path.join(buildroot, 'src', 'scripts') |
| (...skipping 11 matching lines...) Expand all Loading... |
| 154 if temp: | 111 if temp: |
| 155 return temp[0] | 112 return temp[0] |
| 156 | 113 |
| 157 return None | 114 return None |
| 158 | 115 |
| 159 | 116 |
| 160 def _CreateRepoDictionary(buildroot, board, debug=False): | 117 def _CreateRepoDictionary(buildroot, board, debug=False): |
| 161 """Returns the repo->list_of_ebuilds dictionary.""" | 118 """Returns the repo->list_of_ebuilds dictionary.""" |
| 162 repo_dictionary = {} | 119 repo_dictionary = {} |
| 163 manifest_tuples = _GetAllGitRepos(buildroot) | 120 manifest_tuples = _GetAllGitRepos(buildroot) |
| 164 print >> sys.stderr, ( | 121 Info('Creating dictionary of git repos to portage packages ...') |
| 165 'Creating dictionary of git repos to portage packages ...') | |
| 166 | 122 |
| 167 cwd = os.path.join(buildroot, 'src', 'scripts') | 123 cwd = os.path.join(buildroot, 'src', 'scripts') |
| 168 get_all_workon_pkgs_cmd = './cros_workon list --all'.split() | 124 get_all_workon_pkgs_cmd = './cros_workon list --all'.split() |
| 169 packages = RunCommand(get_all_workon_pkgs_cmd, cwd=cwd, | 125 packages = RunCommand(get_all_workon_pkgs_cmd, cwd=cwd, |
| 170 redirect_stdout=True, redirect_stderr=True, | 126 redirect_stdout=True, redirect_stderr=True, |
| 171 enter_chroot=True, print_cmd=debug) | 127 enter_chroot=True, print_cmd=debug) |
| 172 for package in packages.split(): | 128 for package in packages.split(): |
| 173 cros_workon_src_path = _GetCrosWorkOnSrcPath(buildroot, board, package) | 129 cros_workon_src_path = _GetCrosWorkOnSrcPath(buildroot, board, package) |
| 174 if cros_workon_src_path: | 130 if cros_workon_src_path: |
| 175 for tuple in manifest_tuples: | 131 for tuple in manifest_tuples: |
| 176 # This path tends to have the user's home_dir prepended to it. | 132 # This path tends to have the user's home_dir prepended to it. |
| 177 if cros_workon_src_path.endswith(tuple[1]): | 133 if cros_workon_src_path.endswith(tuple[1]): |
| 178 print >> sys.stderr, ('For %s found matching package %s' % | 134 Info('For %s found matching package %s' % (tuple[0], package)) |
| 179 (tuple[0], package)) | |
| 180 if repo_dictionary.has_key(tuple[0]): | 135 if repo_dictionary.has_key(tuple[0]): |
| 181 repo_dictionary[tuple[0]] += [package] | 136 repo_dictionary[tuple[0]] += [package] |
| 182 else: | 137 else: |
| 183 repo_dictionary[tuple[0]] = [package] | 138 repo_dictionary[tuple[0]] = [package] |
| 184 | 139 |
| 185 return repo_dictionary | 140 return repo_dictionary |
| 186 | 141 |
| 187 | 142 |
| 188 def _ParseRevisionString(revision_string, repo_dictionary): | 143 def _ParseRevisionString(revision_string, repo_dictionary): |
| 189 """Parses the given revision_string into a revision dictionary. | 144 """Parses the given revision_string into a revision dictionary. |
| 190 | 145 |
| 191 Returns a list of tuples that contain [portage_package_name, commit_id] to | 146 Returns a list of tuples that contain [portage_package_name, commit_id] to |
| 192 update. | 147 update. |
| 193 | 148 |
| 194 Keyword arguments: | 149 Keyword arguments: |
| 195 revision_string -- revision_string with format | 150 revision_string -- revision_string with format |
| 196 'repo1.git@commit_1 repo2.git@commit2 ...'. | 151 'repo1.git@commit_1 repo2.git@commit2 ...'. |
| 197 repo_dictionary -- dictionary with git repository names as keys (w/out git) | 152 repo_dictionary -- dictionary with git repository names as keys (w/out git) |
| 198 to portage package names. | 153 to portage package names. |
| 199 | 154 |
| 200 """ | 155 """ |
| 201 # Using a dictionary removes duplicates. | 156 # Using a dictionary removes duplicates. |
| 202 revisions = {} | 157 revisions = {} |
| 203 for revision in revision_string.split(): | 158 for revision in revision_string.split(): |
| 204 # Format 'package@commit-id'. | 159 # Format 'package@commit-id'. |
| 205 revision_tuple = revision.split('@') | 160 revision_tuple = revision.split('@') |
| 206 if len(revision_tuple) != 2: | 161 if len(revision_tuple) != 2: |
| 207 print >> sys.stderr, 'Incorrectly formatted revision %s' % revision | 162 Warning('Incorrectly formatted revision %s' % revision) |
| 208 | 163 |
| 209 repo_name = revision_tuple[0].replace('.git', '') | 164 repo_name = revision_tuple[0].replace('.git', '') |
| 210 # Might not have entry if no matching ebuild. | 165 # Might not have entry if no matching ebuild. |
| 211 if repo_dictionary.has_key(repo_name): | 166 if repo_dictionary.has_key(repo_name): |
| 212 # May be many corresponding packages to a given git repo e.g. kernel). | 167 # May be many corresponding packages to a given git repo e.g. kernel). |
| 213 for package in repo_dictionary[repo_name]: | 168 for package in repo_dictionary[repo_name]: |
| 214 revisions[package] = revision_tuple[1] | 169 revisions[package] = revision_tuple[1] |
| 215 | 170 |
| 216 return revisions.items() | 171 return revisions.items() |
| 217 | 172 |
| 218 | 173 |
| 219 def _UprevFromRevisionList(buildroot, revision_list): | 174 def _UprevFromRevisionList(buildroot, revision_list): |
| 220 """Uprevs based on revision list.""" | 175 """Uprevs based on revision list.""" |
| 221 if not revision_list: | 176 if not revision_list: |
| 222 print >> sys.stderr, 'No packages found to uprev' | 177 Info('No packages found to uprev') |
| 223 return | 178 return |
| 224 | 179 |
| 225 package_str = '' | 180 package_str = '' |
| 226 commit_str = '' | 181 commit_str = '' |
| 227 for package, revision in revision_list: | 182 for package, revision in revision_list: |
| 228 package_str += package + ' ' | 183 package_str += package + ' ' |
| 229 commit_str += revision + ' ' | 184 commit_str += revision + ' ' |
| 230 | 185 |
| 231 package_str = package_str.strip() | 186 package_str = package_str.strip() |
| 232 commit_str = commit_str.strip() | 187 commit_str = commit_str.strip() |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 uprev. | 257 uprev. |
| 303 """ | 258 """ |
| 304 # Purposefully set to None as it means Force Build was pressed. | 259 # Purposefully set to None as it means Force Build was pressed. |
| 305 revisions = 'None' | 260 revisions = 'None' |
| 306 if (revisionfile): | 261 if (revisionfile): |
| 307 try: | 262 try: |
| 308 rev_file = open(revisionfile) | 263 rev_file = open(revisionfile) |
| 309 revisions = rev_file.read() | 264 revisions = rev_file.read() |
| 310 rev_file.close() | 265 rev_file.close() |
| 311 except Exception, e: | 266 except Exception, e: |
| 312 print >> sys.stderr, 'Error reading %s, revving all' % revisionfile | 267 Warning('Error reading %s, revving all' % revisionfile) |
| 313 print e | |
| 314 revisions = 'None' | 268 revisions = 'None' |
| 315 | 269 |
| 316 revisions = revisions.strip() | 270 revisions = revisions.strip() |
| 317 | 271 |
| 318 # TODO(sosa): Un-comment once we close individual trees. | 272 # TODO(sosa): Un-comment once we close individual trees. |
| 319 # Revisions == "None" indicates a Force Build. | 273 # revisions == "None" indicates a Force Build. |
| 320 #if revisions != 'None': | 274 #if revisions != 'None': |
| 321 # print >> sys.stderr, 'CBUILDBOT Revision list found %s' % revisions | 275 # print >> sys.stderr, 'CBUILDBOT Revision list found %s' % revisions |
| 322 # revision_list = _ParseRevisionString(revisions, | 276 # revision_list = _ParseRevisionString(revisions, |
| 323 # _CreateRepoDictionary(buildroot, board)) | 277 # _CreateRepoDictionary(buildroot, board)) |
| 324 # _UprevFromRevisionList(buildroot, revision_list) | 278 # _UprevFromRevisionList(buildroot, revision_list) |
| 325 #else: | 279 #else: |
| 326 print >> sys.stderr, 'CBUILDBOT Revving all' | 280 Info('CBUILDBOT Revving all') |
| 327 _UprevAllPackages(buildroot) | 281 _UprevAllPackages(buildroot) |
| 328 | 282 |
| 329 | 283 |
| 330 def _UprevCleanup(buildroot): | 284 def _UprevCleanup(buildroot): |
| 331 """Clean up after a previous uprev attempt.""" | 285 """Clean up after a previous uprev attempt.""" |
| 332 cwd = os.path.join(buildroot, 'src', 'scripts') | 286 cwd = os.path.join(buildroot, 'src', 'scripts') |
| 333 RunCommand(['./cros_mark_as_stable', '--srcroot=..', | 287 RunCommand(['./cros_mark_as_stable', '--srcroot=..', |
| 334 '--tracking_branch="cros/master"', 'clean'], | 288 '--tracking_branch="cros/master"', 'clean'], |
| 335 cwd=cwd) | 289 cwd=cwd) |
| 336 | 290 |
| 337 | 291 |
| 338 def _UprevPush(buildroot): | 292 def _UprevPush(buildroot): |
| 339 """Pushes uprev changes to the main line.""" | 293 """Pushes uprev changes to the main line.""" |
| 340 cwd = os.path.join(buildroot, 'src', 'scripts') | 294 cwd = os.path.join(buildroot, 'src', 'scripts') |
| 341 RunCommand(['./cros_mark_as_stable', '--srcroot=..', | 295 RunCommand(['./cros_mark_as_stable', '--srcroot=..', |
| 342 '--tracking_branch="cros/master"', | 296 '--tracking_branch="cros/master"', |
| 343 '--push_options', '--bypass-hooks -f', 'push'], | 297 '--push_options', '--bypass-hooks -f', 'push'], |
| 344 cwd=cwd) | 298 cwd=cwd) |
| 345 | 299 |
| 346 | 300 |
| 347 def _GetConfig(config_name): | 301 def _GetConfig(config_name): |
| 348 """Gets the configuration for the build""" | 302 """Gets the configuration for the build""" |
| 349 default = config['default'] | 303 default = config['default'] |
| 350 buildconfig = {} | 304 buildconfig = {} |
| 351 if not config.has_key(config_name): | 305 if not config.has_key(config_name): |
| 352 print >> sys.stderr, 'Non-existent configuration specified.' | 306 Warning('Non-existent configuration specified.') |
| 353 print >> sys.stderr, 'Please specify one of:' | 307 Warning('Please specify one of:') |
| 354 config_names = config.keys() | 308 config_names = config.keys() |
| 355 config_names.sort() | 309 config_names.sort() |
| 356 for name in config_names: | 310 for name in config_names: |
| 357 print >> sys.stderr, ' %s' % name | 311 Warning(' %s' % name) |
| 358 sys.exit(1) | 312 sys.exit(1) |
| 359 | 313 |
| 360 buildconfig = config[config_name] | 314 buildconfig = config[config_name] |
| 361 | 315 |
| 362 for key in default.iterkeys(): | 316 for key in default.iterkeys(): |
| 363 if not buildconfig.has_key(key): | 317 if not buildconfig.has_key(key): |
| 364 buildconfig[key] = default[key] | 318 buildconfig[key] = default[key] |
| 365 | 319 |
| 366 return buildconfig | 320 return buildconfig |
| 367 | 321 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 384 buildroot = options.buildroot | 338 buildroot = options.buildroot |
| 385 revisionfile = options.revisionfile | 339 revisionfile = options.revisionfile |
| 386 | 340 |
| 387 # Passed option to clobber. | 341 # Passed option to clobber. |
| 388 if options.clobber: | 342 if options.clobber: |
| 389 RunCommand(['sudo', 'rm', '-rf', buildroot]) | 343 RunCommand(['sudo', 'rm', '-rf', buildroot]) |
| 390 | 344 |
| 391 if len(args) == 1: | 345 if len(args) == 1: |
| 392 buildconfig = _GetConfig(args[0]) | 346 buildconfig = _GetConfig(args[0]) |
| 393 else: | 347 else: |
| 394 print >> sys.stderr, "Missing configuration description" | 348 Warning('Missing configuration description') |
| 395 parser.print_usage() | 349 parser.print_usage() |
| 396 sys.exit(1) | 350 sys.exit(1) |
| 397 | 351 |
| 398 try: | 352 try: |
| 399 if not os.path.isdir(buildroot): | 353 if not os.path.isdir(buildroot): |
| 400 _FullCheckout(buildroot) | 354 _FullCheckout(buildroot) |
| 401 else: | 355 else: |
| 402 _IncrementalCheckout(buildroot) | 356 _IncrementalCheckout(buildroot) |
| 403 | 357 |
| 404 chroot_path = os.path.join(buildroot, 'chroot') | 358 chroot_path = os.path.join(buildroot, 'chroot') |
| (...skipping 14 matching lines...) Expand all Loading... |
| 419 _BuildImage(buildroot) | 373 _BuildImage(buildroot) |
| 420 if buildconfig['uprev']: | 374 if buildconfig['uprev']: |
| 421 if buildconfig['master']: | 375 if buildconfig['master']: |
| 422 # Master bot needs to check if the other slaves completed. | 376 # Master bot needs to check if the other slaves completed. |
| 423 if cbuildbot_comm.HaveSlavesCompleted(config): | 377 if cbuildbot_comm.HaveSlavesCompleted(config): |
| 424 _UprevPush(buildroot) | 378 _UprevPush(buildroot) |
| 425 _UprevCleanup(buildroot) | 379 _UprevCleanup(buildroot) |
| 426 else: | 380 else: |
| 427 # At least one of the slaves failed or we timed out. | 381 # At least one of the slaves failed or we timed out. |
| 428 _UprevCleanup(buildroot) | 382 _UprevCleanup(buildroot) |
| 429 sys.stderr('CBUILDBOT - One of the slaves has failed!!!') | 383 Die('CBUILDBOT - One of the slaves has failed!!!') |
| 430 sys.exit(1) | |
| 431 else: | 384 else: |
| 432 # Publish my status to the master if its expecting it. | 385 # Publish my status to the master if its expecting it. |
| 433 if buildconfig['important']: | 386 if buildconfig['important']: |
| 434 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_COMPLETE) | 387 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_COMPLETE) |
| 435 | 388 |
| 436 _UprevCleanup(buildroot) | 389 _UprevCleanup(buildroot) |
| 437 except: | 390 except: |
| 438 # Send failure to master bot. | 391 # Send failure to master bot. |
| 439 if not buildconfig['master'] and buildconfig['important']: | 392 if not buildconfig['master'] and buildconfig['important']: |
| 440 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_FAILED) | 393 cbuildbot_comm.PublishStatus(cbuildbot_comm.STATUS_BUILD_FAILED) |
| 441 | 394 |
| 442 raise | 395 raise |
| 443 | 396 |
| 444 | 397 |
| 445 if __name__ == '__main__': | 398 if __name__ == '__main__': |
| 446 main() | 399 main() |
| OLD | NEW |