| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Entry point for fully-annotated builds. | 6 """Entry point for fully-annotated builds. |
| 7 | 7 |
| 8 This script is part of the effort to move all builds to annotator-based | 8 This script is part of the effort to move all builds to annotator-based |
| 9 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() | 9 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() |
| 10 found in scripts/master/factory/annotator_factory.py executes a single | 10 found in scripts/master/factory/annotator_factory.py executes a single |
| 11 AddAnnotatedScript step. That step (found in annotator_commands.py) calls | 11 AddAnnotatedScript step. That step (found in annotator_commands.py) calls |
| 12 this script with the build- and factory-properties passed on the command | 12 this script with the build- and factory-properties passed on the command |
| 13 line. In general, the factory properties will include one or more other | 13 line. |
| 14 scripts for this script to delegate to. | |
| 15 | 14 |
| 16 The main mode of operation is for factory_properties to contain a single | 15 The main mode of operation is for factory_properties to contain a single |
| 17 property 'recipe' whose value is the basename (without extension) of a python | 16 property 'recipe' whose value is the basename (without extension) of a python |
| 18 script in one of the following locations (looked up in this order): | 17 script in one of the following locations (looked up in this order): |
| 19 * build_internal/scripts/slave-internal/recipes | 18 * build_internal/scripts/slave-internal/recipes |
| 20 * build_internal/scripts/slave/recipes | 19 * build_internal/scripts/slave/recipes |
| 21 * build/scripts/slave/recipes | 20 * build/scripts/slave/recipes |
| 22 | 21 |
| 23 For example, these factory_properties would run the 'run_presubmit' recipe | 22 For example, these factory_properties would run the 'run_presubmit' recipe |
| 24 located in build/scripts/slave/recipes: | 23 located in build/scripts/slave/recipes: |
| 25 { 'recipe': 'run_presubmit' } | 24 { 'recipe': 'run_presubmit' } |
| 26 | 25 |
| 27 Annotated_run.py will then import the recipe and expect to call a function whose | 26 Annotated_run.py will then import the recipe and expect to call a function whose |
| 28 signature is GetFactoryProperties(build_properties) -> factory_properties. The | 27 signature is: |
| 29 returned factory_properties will then be used to execute the following actions: | 28 GetSteps(api, factory_properties, build_properties) -> iterable_of_things. |
| 30 * optional 'checkout' | 29 |
| 31 * This checks out a gclient/git/svn spec into the slave build dir. | 30 Items in iterable_of_things must be one of: |
| 32 * The value of checkout is expected to be in ('gclient', 'git', 'svn') | 31 * A step dictionary (as accepted by annotator.py) |
| 33 * If checkout is specified, annotated_run will also expect to find a value | 32 * A sequence of step dictionaries |
| 34 for ('%s_spec' % checkout), e.g. 'gclient_spec'. The value of this spec | 33 * A step generator |
| 35 is defined by build/scripts/slave/annotated_checkout.py. | 34 |
| 36 * 'script' or 'steps' | 35 A step generator is called with the following protocol: |
| 37 * 'script' allows you to specify a single script which will be invoked with | 36 * The generator is initialized with 'step_history' and 'failed'. |
| 38 build-properties and factory-properties. | 37 * Each iteration of the generator is passed the current value of 'failed'. |
| 39 * 'steps' serves as input for build/scripts/common/annotator.py | 38 |
| 40 * You can have annotated_run pass build/factory properties to a step by | 39 On each iteration, a step generator may yield: |
| 41 using the recipe_util.step() function. | 40 * A single step dictionary |
| 41 * A sequence of step dictionaries |
| 42 * If a sequence of dictionaries is yielded, and the first step dictionary |
| 43 does not have a 'seed_steps' key, the first step will be augmented with |
| 44 a 'seed_steps' key containing the names of all the steps in the sequence. |
| 45 |
| 46 For steps yielded by the generator, if annotated_run enters the failed state, |
| 47 it will only continue to call the generator if the generator sets the |
| 48 'keep_going' key on the steps which it has produced. Otherwise annoated_run will |
| 49 cease calling the generator and move on to the next item in iterable_of_things. |
| 50 |
| 51 'step_history' is an OrderedDict of {stepname -> StepData}, always representing |
| 52 the current history of what steps have run, what they returned, and any |
| 53 json data they emitted. Additionally, the OrderedDict has the following |
| 54 convenience functions defined: |
| 55 * last_step - Returns the last step that ran or None |
| 56 * nth_step(n) - Returns the N'th step that ran or None |
| 57 |
| 58 'failed' is a boolean representing if the build is in a 'failed' state. |
| 42 """ | 59 """ |
| 43 | 60 |
| 61 import collections |
| 44 import contextlib | 62 import contextlib |
| 45 import json | 63 import json |
| 46 import optparse | 64 import optparse |
| 47 import os | 65 import os |
| 48 import subprocess | 66 import subprocess |
| 49 import sys | 67 import sys |
| 50 import tempfile | 68 import tempfile |
| 51 | 69 |
| 52 from collections import namedtuple | 70 from collections import namedtuple, OrderedDict |
| 71 from itertools import islice |
| 53 | 72 |
| 54 from common import annotator | 73 from common import annotator |
| 55 from common import chromium_utils | 74 from common import chromium_utils |
| 56 from slave import recipe_util | 75 from slave import recipe_util |
| 57 from slave import annotated_checkout | |
| 58 | 76 |
| 59 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) | 77 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) |
| 60 BUILD_ROOT = os.path.dirname(os.path.dirname(SCRIPT_PATH)) | 78 BUILD_ROOT = os.path.dirname(os.path.dirname(SCRIPT_PATH)) |
| 61 | 79 |
| 62 | 80 |
| 63 @contextlib.contextmanager | 81 @contextlib.contextmanager |
| 64 def temp_purge_path(path): | 82 def temp_purge_path(path): |
| 65 saved = sys.path | 83 saved = sys.path |
| 66 sys.path = [path] | 84 sys.path = [path] |
| 67 try: | 85 try: |
| 68 yield | 86 yield |
| 69 finally: | 87 finally: |
| 70 sys.path = saved | 88 sys.path = saved |
| 71 | 89 |
| 72 | 90 |
| 91 class StepData(object): |
| 92 __slots__ = ['step', 'retcode', 'json_data'] |
| 93 def __init__(self, step=None, retcode=None, json_data=None): |
| 94 self.step = step |
| 95 self.retcode = retcode |
| 96 self.json_data = json_data |
| 97 |
| 98 |
| 73 def expand_root_placeholder(root, lst): | 99 def expand_root_placeholder(root, lst): |
| 74 """This expands CheckoutRootPlaceholder in paths to a real path. | 100 """This expands CheckoutRootPlaceholder in paths to a real path. |
| 75 See recipe_util.checkout_path() for usage.""" | 101 See recipe_util.checkout_path() for usage.""" |
| 76 ret = [] | 102 ret = [] |
| 77 replacements = {'CheckoutRootPlaceholder': root} | 103 replacements = {'CheckoutRootPlaceholder': root} |
| 78 for item in lst: | 104 for item in lst: |
| 79 if isinstance(item, basestring): | 105 if isinstance(item, basestring): |
| 80 if '%(CheckoutRootPlaceholder)s' in item: | 106 if '%(CheckoutRootPlaceholder)s' in item: |
| 81 assert root, 'Must use "checkout" key to use checkout_path().' | 107 assert root, 'Must use "checkout" key to use checkout_path().' |
| 82 ret.append(item % replacements) | 108 ret.append(item % replacements) |
| 83 continue | 109 continue |
| 84 ret.append(item) | 110 ret.append(item) |
| 85 return ret | 111 return ret |
| 86 | 112 |
| 87 | 113 |
| 114 def fixup_seed_steps(sequence): |
| 115 """Takes a sequence of step dict's and adds seed_steps to the first entry |
| 116 if appropriate.""" |
| 117 if sequence and 'seed_steps' not in sequence[0]: |
| 118 sequence[0]['seed_steps'] = [x['name'] for x in sequence] |
| 119 |
| 120 |
| 121 def ensure_sequence_of_steps(step_or_steps): |
| 122 """Generates one or more fixed steps, given a step or a sequence of steps.""" |
| 123 if isinstance(step_or_steps, collections.Sequence): |
| 124 step_seq = step_or_steps |
| 125 fixup_seed_steps(step_seq) |
| 126 for s in step_seq: |
| 127 yield s |
| 128 else: |
| 129 yield step_or_steps |
| 130 |
| 131 |
| 132 def step_generator_wrapper(steps, step_history, is_failed): |
| 133 """Generates single steps from the non-homogeneous sequence 'steps'. |
| 134 |
| 135 Each item in steps may be: |
| 136 * A step (dict) |
| 137 * A sequence of steps |
| 138 * A generator of: |
| 139 * A step |
| 140 * A sequence of steps |
| 141 """ |
| 142 for thing in steps: |
| 143 if isinstance(thing, (collections.Sequence, dict)): |
| 144 for step in ensure_sequence_of_steps(thing): |
| 145 yield step |
| 146 else: |
| 147 # step generator |
| 148 step_iter = thing(step_history, is_failed()) |
| 149 first = True |
| 150 try: |
| 151 while True: |
| 152 # Cannot pass non-None to first generator call. |
| 153 step_or_steps = step_iter.send(is_failed() if not first else None) |
| 154 first = False |
| 155 |
| 156 for step in ensure_sequence_of_steps(step_or_steps): |
| 157 keep_going = step.pop('keep_going', False) |
| 158 yield step |
| 159 if is_failed() and not keep_going: |
| 160 raise StopIteration |
| 161 except StopIteration: |
| 162 pass |
| 163 |
| 164 |
| 165 def create_state_history(): |
| 166 """Returns an OrderedDict with some helper functions attached.""" |
| 167 step_history = OrderedDict() |
| 168 |
| 169 # Add in some helpers. |
| 170 def last_step(): |
| 171 """Returns the last item from step_history, or None.""" |
| 172 key = next(reversed(step_history), None) |
| 173 return step_history[key] if key else None |
| 174 step_history.last_step = last_step |
| 175 |
| 176 def nth_step(n): |
| 177 """Returns the N'th step from step_history, or None.""" |
| 178 return next(islice(step_history.iteritems(), n, None), None) |
| 179 step_history.nth_step = nth_step |
| 180 return step_history |
| 181 |
| 182 |
| 88 def get_args(argv): | 183 def get_args(argv): |
| 89 """Process command-line arguments.""" | 184 """Process command-line arguments.""" |
| 90 | 185 |
| 91 parser = optparse.OptionParser( | 186 parser = optparse.OptionParser( |
| 92 description='Entry point for annotated builds.') | 187 description='Entry point for annotated builds.') |
| 93 parser.add_option('--build-properties', | 188 parser.add_option('--build-properties', |
| 94 action='callback', callback=chromium_utils.convert_json, | 189 action='callback', callback=chromium_utils.convert_json, |
| 95 type='string', default={}, | 190 type='string', default={}, |
| 96 help='build properties in JSON format') | 191 help='build properties in JSON format') |
| 97 parser.add_option('--factory-properties', | 192 parser.add_option('--factory-properties', |
| 98 action='callback', callback=chromium_utils.convert_json, | 193 action='callback', callback=chromium_utils.convert_json, |
| 99 type='string', default={}, | 194 type='string', default={}, |
| 100 help='factory properties in JSON format') | 195 help='factory properties in JSON format') |
| 101 parser.add_option('--keep-stdin', action='store_true', default=False, | 196 parser.add_option('--keep-stdin', action='store_true', default=False, |
| 102 help='don\'t close stdin when running recipe steps') | 197 help='don\'t close stdin when running recipe steps') |
| 103 return parser.parse_args(argv) | 198 return parser.parse_args(argv) |
| 104 | 199 |
| 105 | 200 |
| 106 def main(argv=None): | 201 def main(argv=None): |
| 107 opts, _ = get_args(argv) | 202 opts, _ = get_args(argv) |
| 108 | 203 |
| 109 stream = annotator.StructuredAnnotationStream(seed_steps=['setup_build']) | 204 stream = annotator.StructuredAnnotationStream(seed_steps=['setup_build']) |
| 110 | 205 |
| 111 ret = make_steps(stream, opts.build_properties, opts.factory_properties) | 206 ret = run_steps(stream, opts.build_properties, opts.factory_properties) |
| 112 assert ret.script is None, "Unexpectedly got script from make_steps?" | 207 return ret.status_code |
| 113 | 208 |
| 114 if ret.status_code: | |
| 115 return ret | |
| 116 else: | |
| 117 return run_annotator(stream, ret.steps, opts.keep_stdin) | |
| 118 | 209 |
| 119 def make_steps(stream, build_properties, factory_properties, | 210 def run_steps(stream, build_properties, factory_properties, test_data=None): |
| 120 test_mode=False): | 211 """Returns a tuple of (status_code, steps_ran). |
| 121 """Returns a namedtuple of (status_code, script, steps). | |
| 122 | 212 |
| 123 Only one of these values will be set at a time. This is mainly to support the | 213 Only one of these values will be set at a time. This is mainly to support the |
| 124 testing interface used by unittests/recipes_test.py. In particular, unless | 214 testing interface used by unittests/recipes_test.py. |
| 125 test_mode is set, this function should never return a value for script. | 215 |
| 216 test_data should be a dictionary of step_name -> (retcode, json_data) |
| 126 """ | 217 """ |
| 127 MakeStepsRetval = namedtuple('MakeStepsRetval', 'status_code script steps') | 218 MakeStepsRetval = namedtuple('MakeStepsRetval', 'status_code steps_ran') |
| 128 | 219 |
| 129 # TODO(iannucci): Stop this when blamelist becomes sane data. | 220 # TODO(iannucci): Stop this when blamelist becomes sane data. |
| 130 if ('blamelist_real' in build_properties and | 221 if ('blamelist_real' in build_properties and |
| 131 'blamelist' in build_properties): | 222 'blamelist' in build_properties): |
| 132 build_properties['blamelist'] = build_properties['blamelist_real'] | 223 build_properties['blamelist'] = build_properties['blamelist_real'] |
| 133 del build_properties['blamelist_real'] | 224 del build_properties['blamelist_real'] |
| 134 | 225 |
| 135 with stream.step('setup_build') as s: | 226 with stream.step('setup_build') as s: |
| 136 assert 'recipe' in factory_properties | 227 assert 'recipe' in factory_properties |
| 137 recipe = factory_properties['recipe'] | 228 recipe = factory_properties['recipe'] |
| 138 recipe_dirs = (os.path.abspath(p) for p in ( | 229 recipe_dirs = (os.path.abspath(p) for p in ( |
| 139 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', | 230 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', |
| 140 'slave-internal', 'recipes'), | 231 'slave-internal', 'recipes'), |
| 141 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', | 232 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', |
| 142 'slave', 'recipes'), | 233 'slave', 'recipes'), |
| 143 os.path.join(SCRIPT_PATH, 'recipes'), | 234 os.path.join(SCRIPT_PATH, 'recipes'), |
| 144 )) | 235 )) |
| 145 | 236 |
| 146 for path in recipe_dirs: | 237 for path in recipe_dirs: |
| 147 recipe_module = None | 238 recipe_module = None |
| 148 with temp_purge_path(path): | 239 with temp_purge_path(path): |
| 149 try: | 240 try: |
| 150 recipe_module = __import__(recipe, globals(), locals()) | 241 recipe_module = __import__(recipe, globals(), locals()) |
| 151 except ImportError: | 242 except ImportError: |
| 152 continue | 243 continue |
| 153 recipe_dict = recipe_module.GetFactoryProperties( | 244 steps = recipe_module.GetSteps( |
| 154 recipe_util, | 245 recipe_util, |
| 155 factory_properties.copy(), | 246 factory_properties.copy(), |
| 156 build_properties.copy()) | 247 build_properties.copy()) |
| 248 assert isinstance(steps, (list, tuple)) |
| 157 break | 249 break |
| 158 else: | 250 else: |
| 159 s.step_text('recipe not found') | 251 s.step_text('recipe not found') |
| 160 s.step_failure() | 252 s.step_failure() |
| 161 return MakeStepsRetval(1, None, None) | 253 return MakeStepsRetval(1, None) |
| 162 | |
| 163 factory_properties.update(recipe_dict) | |
| 164 | |
| 165 # If a checkout is specified, get its type and spec and pass them | |
| 166 # off to annotated_checkout.py to actually fetch the repo. | |
| 167 # annotated_checkout.py handles its own StructuredAnnotationStream. | |
| 168 root = None | |
| 169 if 'checkout' in factory_properties: | |
| 170 checkout_type = factory_properties['checkout'] | |
| 171 checkout_spec = factory_properties['%s_spec' % checkout_type] | |
| 172 ret, root = annotated_checkout.run(checkout_type, checkout_spec, | |
| 173 test_mode) | |
| 174 if ret != 0: | |
| 175 return MakeStepsRetval(ret, None, None) | |
| 176 if test_mode: | |
| 177 root = '[BUILD_ROOT]'+root[len(BUILD_ROOT):] | |
| 178 | |
| 179 assert ('script' in factory_properties) ^ ('steps' in factory_properties) | |
| 180 ret = 0 | |
| 181 | |
| 182 # If a script is specified, import it, execute its GetSteps method, | |
| 183 # and pass those steps forward so they get executed by annotator.py. | |
| 184 # If we're in test_mode mode, just return the script. | |
| 185 if 'script' in factory_properties: | |
| 186 with stream.step('get_steps') as s: | |
| 187 assert isinstance(factory_properties['script'], str) | |
| 188 [script] = expand_root_placeholder(root, [factory_properties['script']]) | |
| 189 if test_mode: | |
| 190 return MakeStepsRetval(None, script, None) | |
| 191 assert os.path.abspath(script) == script | |
| 192 | |
| 193 with temp_purge_path(os.path.dirname(script)): | |
| 194 try: | |
| 195 script_name = os.path.splitext(os.path.basename(script))[0] | |
| 196 script_module = __import__(script_name, globals(), locals()) | |
| 197 except ImportError: | |
| 198 s.step_text('script not found') | |
| 199 s.step_failure() | |
| 200 return MakeStepsRetval(1, None, None) | |
| 201 steps_dict = script_module.GetSteps(recipe_util, | |
| 202 factory_properties.copy(), | |
| 203 build_properties.copy()) | |
| 204 factory_properties['steps'] = steps_dict | |
| 205 | 254 |
| 206 # Execute annotator.py with steps if specified. | 255 # Execute annotator.py with steps if specified. |
| 207 # annotator.py handles the seeding, execution, and annotation of each step. | 256 # annotator.py handles the seeding, execution, and annotation of each step. |
| 208 if 'steps' in factory_properties: | 257 factory_properties_str = json.dumps(factory_properties) |
| 209 steps = factory_properties.pop('steps') | 258 build_properties_str = json.dumps(build_properties) |
| 210 factory_properties_str = json.dumps(factory_properties) | 259 property_placeholder_lst = [ |
| 211 build_properties_str = json.dumps(build_properties) | 260 '--factory-properties', factory_properties_str, |
| 212 property_placeholder_lst = [ | 261 '--build-properties', build_properties_str] |
| 213 '--factory-properties', factory_properties_str, | 262 |
| 214 '--build-properties', build_properties_str] | 263 failed = False |
| 215 for step in steps: | 264 step_history = create_state_history() |
| 216 new_cmd = [] | 265 |
| 217 for item in expand_root_placeholder(root, step['cmd']): | 266 root = None |
| 218 if item == recipe_util.PropertyPlaceholder: | 267 for step in step_generator_wrapper(steps, step_history, lambda: failed): |
| 219 new_cmd.extend(property_placeholder_lst) | 268 json_output_fd = json_output_name = None |
| 269 new_cmd = [] |
| 270 for item in expand_root_placeholder(root, step['cmd']): |
| 271 if item == recipe_util.PropertyPlaceholder: |
| 272 new_cmd.extend(property_placeholder_lst) |
| 273 elif item == recipe_util.JsonOutputPlaceholder: |
| 274 new_cmd.append('--output-json') |
| 275 if test_data: |
| 276 new_cmd.append('/path/to/tmp/json') |
| 220 else: | 277 else: |
| 221 new_cmd.append(item) | 278 assert not json_output_name, ( |
| 222 step['cmd'] = new_cmd | 279 'Can only use json_output_file once per step' % step) |
| 223 if 'cwd' in step: | 280 json_output_fd, json_output_name = tempfile.mkstemp() |
| 224 [new_cwd] = expand_root_placeholder(root, [step['cwd']]) | 281 new_cmd.append(json_output_name) |
| 225 step['cwd'] = new_cwd | 282 else: |
| 283 new_cmd.append(item) |
| 284 step['cmd'] = new_cmd |
| 285 if 'cwd' in step: |
| 286 [new_cwd] = expand_root_placeholder(root, [step['cwd']]) |
| 287 step['cwd'] = new_cwd |
| 226 | 288 |
| 227 return MakeStepsRetval(None, None, steps) | 289 json_data = step.pop('static_json_data', {}) |
| 290 assert not(json_data and json_output_name), ( |
| 291 "Cannot have both static_json_data as well as dynamic json_data") |
| 292 if test_data is None: |
| 293 failed, [retcode] = annotator.run_steps([step], failed) |
| 294 if json_output_name: |
| 295 try: |
| 296 json_data = json.load(os.fdopen(json_output_fd, 'r')) |
| 297 except ValueError: |
| 298 pass |
| 299 else: |
| 300 retcode, potential_json_data = test_data.pop(step['name'], (0, {})) |
| 301 json_data = json_data or potential_json_data |
| 302 failed = failed or retcode != 0 |
| 228 | 303 |
| 229 def run_annotator(stream, steps, keep_stdin): | 304 # Support CheckoutRootPlaceholder. |
| 230 ret = 0 | 305 root = root or json_data.get('CheckoutRoot', None) |
| 231 annotator_path = os.path.join( | |
| 232 os.path.dirname(SCRIPT_PATH), 'common', 'annotator.py') | |
| 233 tmpfile, tmpname = tempfile.mkstemp() | |
| 234 try: | |
| 235 cmd = [sys.executable, annotator_path, tmpname] | |
| 236 step_doc = json.dumps(steps) | |
| 237 with os.fdopen(tmpfile, 'wb') as f: | |
| 238 f.write(step_doc) | |
| 239 with stream.step('annotator_preamble'): | |
| 240 print 'in %s executing: %s' % (os.getcwd(), ' '.join(cmd)) | |
| 241 print 'with: %s' % step_doc | |
| 242 if keep_stdin: | |
| 243 ret = subprocess.call(cmd) | |
| 244 else: | |
| 245 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE) | |
| 246 proc.communicate('') | |
| 247 ret = proc.returncode | |
| 248 finally: | |
| 249 os.unlink(tmpname) | |
| 250 | 306 |
| 251 return ret | 307 assert step['name'] not in step_history |
| 308 step_history[step['name']] = StepData(step, retcode, json_data) |
| 309 |
| 310 assert test_data is None or test_data == {}, ( |
| 311 "Unconsumed test data! %s" % (test_data,)) |
| 312 |
| 313 return MakeStepsRetval(retcode, step_history) |
| 252 | 314 |
| 253 | 315 |
| 254 def UpdateScripts(): | 316 def UpdateScripts(): |
| 255 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): | 317 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): |
| 256 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') | 318 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') |
| 257 return False | 319 return False |
| 258 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts']) | 320 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts']) |
| 259 with stream.step('update_scripts') as s: | 321 with stream.step('update_scripts') as s: |
| 260 build_root = os.path.join(SCRIPT_PATH, '..', '..') | 322 build_root = os.path.join(SCRIPT_PATH, '..', '..') |
| 261 gclient_name = 'gclient' | 323 gclient_name = 'gclient' |
| 262 if sys.platform.startswith('win'): | 324 if sys.platform.startswith('win'): |
| 263 gclient_name += '.bat' | 325 gclient_name += '.bat' |
| 264 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name) | 326 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name) |
| 265 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0: | 327 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0: |
| 266 s.step_text('gclient sync failed!') | 328 s.step_text('gclient sync failed!') |
| 267 s.step_warnings() | 329 s.step_warnings() |
| 268 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1' | 330 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1' |
| 269 return True | 331 return True |
| 270 | 332 |
| 271 | 333 |
| 272 if __name__ == '__main__': | 334 if __name__ == '__main__': |
| 273 if UpdateScripts(): | 335 if UpdateScripts(): |
| 274 os.execv(sys.executable, [sys.executable] + sys.argv) | 336 os.execv(sys.executable, [sys.executable] + sys.argv) |
| 275 sys.exit(main(sys.argv)) | 337 sys.exit(main(sys.argv)) |
| OLD | NEW |