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 |