Chromium Code Reviews| 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. | |
| 54 | |
| 55 'failed' is a boolean representing if the build is in a 'failed' state. | |
| 42 """ | 56 """ |
| 43 | 57 |
| 44 import contextlib | 58 import contextlib |
| 45 import json | 59 import json |
| 46 import optparse | 60 import optparse |
| 47 import os | 61 import os |
| 48 import subprocess | 62 import subprocess |
| 49 import sys | 63 import sys |
| 50 import tempfile | 64 import tempfile |
| 51 | 65 |
| 52 from collections import namedtuple | 66 from collections import namedtuple, OrderedDict |
| 67 from itertools import islice | |
| 53 | 68 |
| 54 from common import annotator | 69 from common import annotator |
| 55 from common import chromium_utils | 70 from common import chromium_utils |
| 56 from slave import recipe_util | 71 from slave import recipe_util |
| 57 from slave import annotated_checkout | |
| 58 | 72 |
| 59 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) | 73 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) |
| 60 BUILD_ROOT = os.path.dirname(os.path.dirname(SCRIPT_PATH)) | 74 BUILD_ROOT = os.path.dirname(os.path.dirname(SCRIPT_PATH)) |
| 61 | 75 |
| 62 | 76 |
| 63 @contextlib.contextmanager | 77 @contextlib.contextmanager |
| 64 def temp_purge_path(path): | 78 def temp_purge_path(path): |
| 65 saved = sys.path | 79 saved = sys.path |
| 66 sys.path = [path] | 80 sys.path = [path] |
| 67 try: | 81 try: |
| 68 yield | 82 yield |
| 69 finally: | 83 finally: |
| 70 sys.path = saved | 84 sys.path = saved |
| 71 | 85 |
| 72 | 86 |
| 87 class StepData(object): | |
| 88 __slots__ = ['step', 'retcode', 'json_data'] | |
| 89 def __init__(self, step=None, retcode=None, json_data=None): | |
| 90 self.step = step | |
| 91 self.retcode = retcode | |
| 92 self.json_data = json_data | |
| 93 | |
| 94 | |
| 73 def expand_root_placeholder(root, lst): | 95 def expand_root_placeholder(root, lst): |
| 74 """This expands CheckoutRootPlaceholder in paths to a real path. | 96 """This expands CheckoutRootPlaceholder in paths to a real path. |
| 75 See recipe_util.checkout_path() for usage.""" | 97 See recipe_util.checkout_path() for usage.""" |
| 76 ret = [] | 98 ret = [] |
| 77 replacements = {'CheckoutRootPlaceholder': root} | 99 replacements = {'CheckoutRootPlaceholder': root} |
| 78 for item in lst: | 100 for item in lst: |
| 79 if isinstance(item, basestring): | 101 if isinstance(item, basestring): |
| 80 if '%(CheckoutRootPlaceholder)s' in item: | 102 if '%(CheckoutRootPlaceholder)s' in item: |
| 81 assert root, 'Must use "checkout" key to use checkout_path().' | 103 assert root, 'Must use "checkout" key to use checkout_path().' |
| 82 ret.append(item % replacements) | 104 ret.append(item % replacements) |
| 83 continue | 105 continue |
| 84 ret.append(item) | 106 ret.append(item) |
| 85 return ret | 107 return ret |
| 86 | 108 |
| 87 | 109 |
| 110 def fixup_seed_steps(sequence): | |
| 111 """Takes a sequence of step dict's and adds seed_steps to the first entry | |
| 112 if appropriate.""" | |
| 113 if sequence and 'seed_steps' not in sequence[0]: | |
| 114 sequence[0]['seed_steps'] = [x['name'] for x in sequence] | |
| 115 | |
| 116 | |
| 88 def get_args(argv): | 117 def get_args(argv): |
| 89 """Process command-line arguments.""" | 118 """Process command-line arguments.""" |
| 90 | 119 |
| 91 parser = optparse.OptionParser( | 120 parser = optparse.OptionParser( |
| 92 description='Entry point for annotated builds.') | 121 description='Entry point for annotated builds.') |
| 93 parser.add_option('--build-properties', | 122 parser.add_option('--build-properties', |
| 94 action='callback', callback=chromium_utils.convert_json, | 123 action='callback', callback=chromium_utils.convert_json, |
| 95 type='string', default={}, | 124 type='string', default={}, |
| 96 help='build properties in JSON format') | 125 help='build properties in JSON format') |
| 97 parser.add_option('--factory-properties', | 126 parser.add_option('--factory-properties', |
| 98 action='callback', callback=chromium_utils.convert_json, | 127 action='callback', callback=chromium_utils.convert_json, |
| 99 type='string', default={}, | 128 type='string', default={}, |
| 100 help='factory properties in JSON format') | 129 help='factory properties in JSON format') |
| 101 parser.add_option('--keep-stdin', action='store_true', default=False, | 130 parser.add_option('--keep-stdin', action='store_true', default=False, |
| 102 help='don\'t close stdin when running recipe steps') | 131 help='don\'t close stdin when running recipe steps') |
| 103 return parser.parse_args(argv) | 132 return parser.parse_args(argv) |
| 104 | 133 |
| 105 | 134 |
| 106 def main(argv=None): | 135 def main(argv=None): |
| 107 opts, _ = get_args(argv) | 136 opts, _ = get_args(argv) |
| 108 | 137 |
| 109 stream = annotator.StructuredAnnotationStream(seed_steps=['setup_build']) | 138 stream = annotator.StructuredAnnotationStream(seed_steps=['setup_build']) |
| 110 | 139 |
| 111 ret = make_steps(stream, opts.build_properties, opts.factory_properties) | 140 return run_steps(stream, opts.build_properties, opts.factory_properties)[0] |
|
Mike Stip (use stip instead)
2013/05/18 00:37:22
This is a MakeStepsRetval, right? Shouldn't we use
iannucci
2013/05/18 04:00:36
Good catch. Done.
| |
| 112 assert ret.script is None, "Unexpectedly got script from make_steps?" | |
| 113 | 141 |
| 114 if ret.status_code: | |
| 115 return ret | |
| 116 else: | |
| 117 return run_annotator(stream, ret.steps, opts.keep_stdin) | |
| 118 | 142 |
| 119 def make_steps(stream, build_properties, factory_properties, | 143 def run_steps(stream, build_properties, factory_properties, test_data=None): |
| 120 test_mode=False): | 144 """Returns a tuple of (status_code, steps_ran). |
| 121 """Returns a namedtuple of (status_code, script, steps). | |
| 122 | 145 |
| 123 Only one of these values will be set at a time. This is mainly to support the | 146 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 | 147 testing interface used by unittests/recipes_test.py. |
| 125 test_mode is set, this function should never return a value for script. | 148 |
| 149 test_data should be a dictionary of step_name -> (retcode, json_data) | |
| 126 """ | 150 """ |
| 127 MakeStepsRetval = namedtuple('MakeStepsRetval', 'status_code script steps') | 151 MakeStepsRetval = namedtuple('MakeStepsRetval', 'status_code steps_ran') |
| 128 | 152 |
| 129 # TODO(iannucci): Stop this when blamelist becomes sane data. | 153 # TODO(iannucci): Stop this when blamelist becomes sane data. |
| 130 if ('blamelist_real' in build_properties and | 154 if ('blamelist_real' in build_properties and |
| 131 'blamelist' in build_properties): | 155 'blamelist' in build_properties): |
| 132 build_properties['blamelist'] = build_properties['blamelist_real'] | 156 build_properties['blamelist'] = build_properties['blamelist_real'] |
| 133 del build_properties['blamelist_real'] | 157 del build_properties['blamelist_real'] |
| 134 | 158 |
| 135 with stream.step('setup_build') as s: | 159 with stream.step('setup_build') as s: |
| 136 assert 'recipe' in factory_properties | 160 assert 'recipe' in factory_properties |
| 137 recipe = factory_properties['recipe'] | 161 recipe = factory_properties['recipe'] |
| 138 recipe_dirs = (os.path.abspath(p) for p in ( | 162 recipe_dirs = (os.path.abspath(p) for p in ( |
| 139 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', | 163 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', |
| 140 'slave-internal', 'recipes'), | 164 'slave-internal', 'recipes'), |
| 141 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', | 165 os.path.join(SCRIPT_PATH, '..', '..', '..', 'build_internal', 'scripts', |
| 142 'slave', 'recipes'), | 166 'slave', 'recipes'), |
| 143 os.path.join(SCRIPT_PATH, 'recipes'), | 167 os.path.join(SCRIPT_PATH, 'recipes'), |
| 144 )) | 168 )) |
| 145 | 169 |
| 146 for path in recipe_dirs: | 170 for path in recipe_dirs: |
| 147 recipe_module = None | 171 recipe_module = None |
| 148 with temp_purge_path(path): | 172 with temp_purge_path(path): |
| 149 try: | 173 try: |
| 150 recipe_module = __import__(recipe, globals(), locals()) | 174 recipe_module = __import__(recipe, globals(), locals()) |
| 151 except ImportError: | 175 except ImportError: |
| 152 continue | 176 continue |
| 153 recipe_dict = recipe_module.GetFactoryProperties( | 177 steps = recipe_module.GetSteps( |
| 154 recipe_util, | 178 recipe_util, |
| 155 factory_properties.copy(), | 179 factory_properties.copy(), |
| 156 build_properties.copy()) | 180 build_properties.copy()) |
| 181 assert isinstance(steps, (list, tuple)) | |
| 157 break | 182 break |
| 158 else: | 183 else: |
| 159 s.step_text('recipe not found') | 184 s.step_text('recipe not found') |
| 160 s.step_failure() | 185 s.step_failure() |
| 161 return MakeStepsRetval(1, None, None) | 186 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 | 187 |
| 206 # Execute annotator.py with steps if specified. | 188 # Execute annotator.py with steps if specified. |
| 207 # annotator.py handles the seeding, execution, and annotation of each step. | 189 # annotator.py handles the seeding, execution, and annotation of each step. |
| 208 if 'steps' in factory_properties: | 190 factory_properties_str = json.dumps(factory_properties) |
| 209 steps = factory_properties.pop('steps') | 191 build_properties_str = json.dumps(build_properties) |
| 210 factory_properties_str = json.dumps(factory_properties) | 192 property_placeholder_lst = [ |
| 211 build_properties_str = json.dumps(build_properties) | 193 '--factory-properties', factory_properties_str, |
| 212 property_placeholder_lst = [ | 194 '--build-properties', build_properties_str] |
| 213 '--factory-properties', factory_properties_str, | 195 |
| 214 '--build-properties', build_properties_str] | 196 failed = False |
| 215 for step in steps: | 197 step_history = OrderedDict() |
| 216 new_cmd = [] | 198 step_history.last_step = lambda: step_history[next(reversed(step_history))] |
|
Mike Stip (use stip instead)
2013/05/18 00:37:22
document these
iannucci
2013/05/18 04:00:36
Done.
| |
| 217 for item in expand_root_placeholder(root, step['cmd']): | 199 step_history.nth_step = ( |
| 218 if item == recipe_util.PropertyPlaceholder: | 200 lambda n, default: next(islice(step_history.iteritems(), n, None), default)) |
| 219 new_cmd.extend(property_placeholder_lst) | 201 |
| 202 def step_generator_wrapper(): | |
|
Mike Stip (use stip instead)
2013/05/18 00:37:22
small doc saying this demuxes a single step, a lis
iannucci
2013/05/18 04:00:36
Done.
| |
| 203 for thing in steps: | |
| 204 if isinstance(thing, dict): | |
| 205 # single static step | |
| 206 yield thing | |
| 207 elif isinstance(thing, (list, tuple)): | |
| 208 # multiple static steps | |
| 209 fixup_seed_steps(thing) | |
| 210 for step in thing: | |
| 211 yield step | |
| 212 else: | |
| 213 # step generator | |
| 214 step_iter = thing(step_history, failed) | |
| 215 first = True | |
| 216 try: | |
| 217 while True: | |
| 218 # Cannot pass non-None to first generator call. | |
| 219 step_or_steps = step_iter.send(failed if not first else None) | |
| 220 first = False | |
| 221 | |
| 222 if isinstance(step_or_steps, (list, tuple)): | |
| 223 gensteps = step_or_steps | |
|
Mike Stip (use stip instead)
2013/05/18 00:37:22
is there a way you can unify this code with lines
iannucci
2013/05/18 04:00:36
Done. For bonus points, I refactored these nested
| |
| 224 fixup_seed_steps(gensteps) | |
| 225 else: | |
| 226 gensteps = [step_or_steps] | |
| 227 | |
| 228 for step in gensteps: | |
| 229 keep_going = step.pop('keep_going', False) | |
| 230 yield step | |
| 231 if failed and not keep_going: | |
| 232 raise StopIteration | |
| 233 except StopIteration: | |
| 234 pass | |
| 235 | |
| 236 root = None | |
| 237 for step in step_generator_wrapper(): | |
| 238 json_output_fd = json_output_name = None | |
| 239 new_cmd = [] | |
| 240 for item in expand_root_placeholder(root, step['cmd']): | |
| 241 if item == recipe_util.PropertyPlaceholder: | |
| 242 new_cmd.extend(property_placeholder_lst) | |
| 243 elif item == recipe_util.JsonOutputPlaceholder: | |
| 244 new_cmd.append('--output-json') | |
| 245 if test_data: | |
| 246 new_cmd.append('/path/to/tmp/json') | |
| 220 else: | 247 else: |
| 221 new_cmd.append(item) | 248 assert not json_output_name, ( |
| 222 step['cmd'] = new_cmd | 249 'Can only use json_output_file once per step' % step) |
| 223 if 'cwd' in step: | 250 json_output_fd, json_output_name = tempfile.mkstemp() |
| 224 [new_cwd] = expand_root_placeholder(root, [step['cwd']]) | 251 new_cmd.append(json_output_name) |
| 225 step['cwd'] = new_cwd | 252 else: |
| 253 new_cmd.append(item) | |
| 254 step['cmd'] = new_cmd | |
| 255 if 'cwd' in step: | |
| 256 [new_cwd] = expand_root_placeholder(root, [step['cwd']]) | |
| 257 step['cwd'] = new_cwd | |
| 226 | 258 |
| 227 return MakeStepsRetval(None, None, steps) | 259 json_data = step.pop('static_json_data', {}) |
| 260 assert not(json_data and json_output_name), ( | |
| 261 "Cannot have both static_json_data as well as dynamic json_data") | |
| 262 if test_data is None: | |
| 263 failed, [retcode] = annotator.run_steps([step], failed) | |
| 264 if json_output_name: | |
| 265 try: | |
| 266 json_data = json.load(os.fdopen(json_output_fd, 'r')) | |
| 267 except ValueError: | |
| 268 pass | |
| 269 else: | |
| 270 retcode, potential_json_data = test_data.pop(step['name'], (0, {})) | |
| 271 json_data = json_data or potential_json_data | |
| 272 failed = failed or retcode != 0 | |
| 228 | 273 |
| 229 def run_annotator(stream, steps, keep_stdin): | 274 # Support CheckoutRootPlaceholder. |
| 230 ret = 0 | 275 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 | 276 |
| 251 return ret | 277 assert step['name'] not in step_history |
| 278 step_history[step['name']] = StepData(step, retcode, json_data) | |
| 279 | |
| 280 assert test_data is None or test_data == {}, ( | |
| 281 "Unconsumed test data! %s" % (test_data,)) | |
| 282 | |
| 283 return MakeStepsRetval(retcode, step_history) | |
| 252 | 284 |
| 253 | 285 |
| 254 def UpdateScripts(): | 286 def UpdateScripts(): |
| 255 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): | 287 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): |
| 256 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') | 288 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') |
| 257 return False | 289 return False |
| 258 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts']) | 290 stream = annotator.StructuredAnnotationStream(seed_steps=['update_scripts']) |
| 259 with stream.step('update_scripts') as s: | 291 with stream.step('update_scripts') as s: |
| 260 build_root = os.path.join(SCRIPT_PATH, '..', '..') | 292 build_root = os.path.join(SCRIPT_PATH, '..', '..') |
| 261 gclient_name = 'gclient' | 293 gclient_name = 'gclient' |
| 262 if sys.platform.startswith('win'): | 294 if sys.platform.startswith('win'): |
| 263 gclient_name += '.bat' | 295 gclient_name += '.bat' |
| 264 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name) | 296 gclient_path = os.path.join(build_root, '..', 'depot_tools', gclient_name) |
| 265 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0: | 297 if subprocess.call([gclient_path, 'sync', '--force'], cwd=build_root) != 0: |
| 266 s.step_text('gclient sync failed!') | 298 s.step_text('gclient sync failed!') |
| 267 s.step_warnings() | 299 s.step_warnings() |
| 268 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1' | 300 os.environ['RUN_SLAVE_UPDATED_SCRIPTS'] = '1' |
| 269 return True | 301 return True |
| 270 | 302 |
| 271 | 303 |
| 272 if __name__ == '__main__': | 304 if __name__ == '__main__': |
| 273 if UpdateScripts(): | 305 if UpdateScripts(): |
| 274 os.execv(sys.executable, [sys.executable] + sys.argv) | 306 os.execv(sys.executable, [sys.executable] + sys.argv) |
| 275 sys.exit(main(sys.argv)) | 307 sys.exit(main(sys.argv)) |
| OLD | NEW |