Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """This module holds utilities which make writing recipes easier.""" | 5 """This module holds utilities which make writing recipes easier.""" |
| 6 | 6 |
| 7 import contextlib as _contextlib | 7 import contextlib as _contextlib |
| 8 import os as _os | 8 import os as _os |
| 9 | 9 |
| 10 # These imports are intended to be passed through to recipes | 10 # These imports are intended to be passed through to recipes |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 56 | 56 |
| 57 The actual checkout root is filled in by annotated_run after the recipe | 57 The actual checkout root is filled in by annotated_run after the recipe |
| 58 completes, and is dependent on the implementation of 'root()' in | 58 completes, and is dependent on the implementation of 'root()' in |
| 59 annotated_checkout for the checkout type that you've selected. | 59 annotated_checkout for the checkout type that you've selected. |
| 60 | 60 |
| 61 NOTE: In order for this function to work, your recipe MUST use the 'checkout' | 61 NOTE: In order for this function to work, your recipe MUST use the 'checkout' |
| 62 functionality provided by annotated_run. | 62 functionality provided by annotated_run. |
| 63 """ # pylint: disable=W0105 | 63 """ # pylint: disable=W0105 |
| 64 checkout_path = _path_method('checkout_path', '%(CheckoutRootPlaceholder)s') | 64 checkout_path = _path_method('checkout_path', '%(CheckoutRootPlaceholder)s') |
| 65 | 65 |
| 66 def path_exists(path): | |
| 67 return _os.path.exists(path) | |
| 66 | 68 |
| 67 @_contextlib.contextmanager | 69 @_contextlib.contextmanager |
| 68 def mock_paths(): | 70 def mock_paths(mock_path_exists=None): |
|
agable
2013/05/22 17:21:23
Thanks for adding this functionality. I find mock_
mkosiba (inactive)
2013/05/23 13:13:38
I struggled quite a bit with a good name for this
| |
| 69 """Used by unittest/recipes_test.py to temporarily override the paths | 71 """Used by unittest/recipes_test.py to temporarily override the paths |
| 70 generated by the various path functions in this module. | 72 generated by the various path functions in this module. |
| 71 | 73 |
| 72 This is necessary to give equivalent output when running the tests on any | 74 This is necessary to give equivalent output when running the tests on any |
| 73 checkout configuration. Instead of real paths, recipes which use these | 75 checkout configuration. Instead of real paths, recipes which use these |
| 74 functions will get paths like '[DEPOT_TOOLS_ROOT]'. | 76 functions will get paths like '[DEPOT_TOOLS_ROOT]'. |
| 75 """ | 77 """ |
| 76 path_base_names = ['depot_tools', 'build_internal', 'build', 'slave_build', | 78 path_base_names = ['depot_tools', 'build_internal', 'build', 'slave_build', |
| 77 'root'] | 79 'root'] |
| 78 g = globals() | 80 g = globals() |
| 79 tokens = {} | 81 tokens = {} |
| 80 path_funcs = {} | 82 path_funcs = {} |
| 83 mock_path_exists = frozenset(mock_path_exists or []) | |
| 81 try: | 84 try: |
| 82 for name in path_base_names: | 85 for name in path_base_names: |
| 83 token_name = (name + '_root').upper() | 86 token_name = (name + '_root').upper() |
| 84 token_val = '[%s]' % token_name | 87 token_val = '[%s]' % token_name |
| 85 path_func_name = (name + '_path') | 88 path_func_name = (name + '_path') |
| 86 | 89 |
| 87 if token_name in g: | 90 if token_name in g: |
| 88 tokens[token_name] = g[token_name] | 91 tokens[token_name] = g[token_name] |
| 89 g[token_name] = token_val | 92 g[token_name] = token_val |
| 90 | 93 |
| 91 if path_func_name in g: | 94 if path_func_name in g: |
| 92 path_funcs[path_func_name] = g[path_func_name] | 95 path_funcs[path_func_name] = g[path_func_name] |
| 93 g[path_func_name] = _path_method(path_func_name, token_val) | 96 g[path_func_name] = _path_method(path_func_name, token_val) |
| 97 | |
| 98 path_funcs['path_exists'] = g['path_exists'] | |
| 99 g['path_exists'] = lambda path: path in mock_path_exists | |
| 100 | |
| 94 yield | 101 yield |
| 95 finally: | 102 finally: |
| 96 g.update(tokens) | 103 g.update(tokens) |
| 97 g.update(path_funcs) | 104 g.update(path_funcs) |
| 98 | 105 |
| 99 | 106 |
| 100 def deep_set(obj, key_vals): | 107 def deep_set(obj, key_vals): |
| 101 """Take an object (a dict or list), and a list of key/value pairs to set, | 108 """Take an object (a dict or list), and a list of key/value pairs to set, |
| 102 and transform it by replacing items in obj at the key locations with the | 109 and transform it by replacing items in obj at the key locations with the |
| 103 respective values. | 110 respective values. |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 359 'recursive': recursive, | 366 'recursive': recursive, |
| 360 }, | 367 }, |
| 361 }), | 368 }), |
| 362 self.git('fetch', 'origin', *recursive_args), | 369 self.git('fetch', 'origin', *recursive_args), |
| 363 self.git('update-ref', 'refs/heads/'+branch, 'origin/'+branch), | 370 self.git('update-ref', 'refs/heads/'+branch, 'origin/'+branch), |
| 364 self.git('clean', '-f', '-d', '-X'), | 371 self.git('clean', '-f', '-d', '-X'), |
| 365 self.git('checkout', '-f', branch), | 372 self.git('checkout', '-f', branch), |
| 366 self.git('submodule', 'update', '--init', '--recursive'), | 373 self.git('submodule', 'update', '--init', '--recursive'), |
| 367 ] | 374 ] |
| 368 | 375 |
| 369 def gclient_checkout(self, common_repo_name, git_mode=False): | 376 def gclient_checkout(self, common_repo_name_or_spec, git_mode=False, |
| 377 spec_name=None): | |
| 370 """Returns a step generator function for gclient checkouts.""" | 378 """Returns a step generator function for gclient checkouts.""" |
| 371 spec = GCLIENT_COMMON_SPECS[common_repo_name](self) | 379 if isinstance(common_repo_name_or_spec, basestring): |
| 380 spec = GCLIENT_COMMON_SPECS[common_repo_name_or_spec](self) | |
| 381 else: | |
| 382 spec = common_repo_name_or_spec | |
| 372 spec_string = '' | 383 spec_string = '' |
| 384 if not spec_name: | |
| 385 step_name = lambda n: 'gclient ' + n | |
| 386 else: | |
| 387 step_name = lambda n: '[spec: %s] gclient %s' % (spec_name, n) | |
|
agable
2013/05/22 17:21:23
I'd rather have the spec_name emitted as a @@@STEP
mkosiba (inactive)
2013/05/23 13:13:38
The reason for this is not to differentiate in the
agable
2013/05/23 17:20:12
D'oh. SGTM, then.
| |
| 373 for key in spec: | 388 for key in spec: |
| 374 # We should be using json.dumps here, but gclient directly execs the dict | 389 # We should be using json.dumps here, but gclient directly execs the dict |
| 375 # that it receives as the argument to --spec, so we have to have True, | 390 # that it receives as the argument to --spec, so we have to have True, |
| 376 # False, and None instead of JSON's true, false, and null. | 391 # False, and None instead of JSON's true, false, and null. |
| 377 spec_string += '%s = %s\n' % (key, str(spec[key])) | 392 spec_string += '%s = %s\n' % (key, str(spec[key])) |
| 378 gclient = depot_tools_path('gclient') + ('.bat' if IsWindows() else '') | 393 gclient = depot_tools_path('gclient') + ('.bat' if IsWindows() else '') |
| 379 | 394 |
| 380 if not git_mode: | 395 if not git_mode: |
| 381 clean_step = self.step('gclient clean', [gclient, 'revert', '--nohooks']) | 396 clean_step = self.step(step_name('clean'), |
| 382 sync_step = self.step('gclient sync', [gclient, 'sync', '--nohooks']) | 397 [gclient, 'revert', '--nohooks']) |
| 398 sync_step = self.step(step_name('sync'), [gclient, 'sync', '--nohooks']) | |
| 383 else: | 399 else: |
| 384 # clean() isn't used because the gclient sync flags passed in checkout() | 400 # clean() isn't used because the gclient sync flags passed in checkout() |
| 385 # do much the same thing, and they're more correct than doing a separate | 401 # do much the same thing, and they're more correct than doing a separate |
| 386 # 'gclient revert' because it makes sure the other args are correct when | 402 # 'gclient revert' because it makes sure the other args are correct when |
| 387 # a repo was deleted and needs to be re-cloned (notably | 403 # a repo was deleted and needs to be re-cloned (notably |
| 388 # --with_branch_heads), whereas 'revert' uses default args for clone | 404 # --with_branch_heads), whereas 'revert' uses default args for clone |
| 389 # operations. | 405 # operations. |
| 390 # | 406 # |
| 391 # TODO(mmoss): To be like current official builders, this step could just | 407 # TODO(mmoss): To be like current official builders, this step could just |
| 392 # delete the whole <slave_name>/build/ directory and start each build | 408 # delete the whole <slave_name>/build/ directory and start each build |
| 393 # from scratch. That might be the least bad solution, at least until we | 409 # from scratch. That might be the least bad solution, at least until we |
| 394 # have a reliable gclient method to produce a pristine working dir for | 410 # have a reliable gclient method to produce a pristine working dir for |
| 395 # git-based builds (e.g. maybe some combination of 'git reset/clean -fx' | 411 # git-based builds (e.g. maybe some combination of 'git reset/clean -fx' |
| 396 # and removing the 'out' directory). | 412 # and removing the 'out' directory). |
| 397 clean_step = None | 413 clean_step = None |
| 398 sync_step = self.step('gclient sync', [ | 414 sync_step = self.step(step_name('sync'), [ |
| 399 gclient, 'sync', '--verbose', '--with_branch_heads', '--nohooks', | 415 gclient, 'sync', '--verbose', '--with_branch_heads', '--nohooks', |
| 400 '--reset', '--delete_unversioned_trees', '--force']) | 416 '--reset', '--delete_unversioned_trees', '--force']) |
| 401 steps = [ | 417 steps = [ |
| 402 self.step( | 418 self.step( |
| 403 'gclient setup', | 419 step_name('setup'), |
| 404 [gclient, 'config', '--spec', spec_string], | 420 [gclient, 'config', '--spec', spec_string], |
| 405 static_json_data={ | 421 static_json_data={ |
| 406 'CheckoutRoot': slave_build_path(spec['solutions'][0]['name']), | 422 'CheckoutRoot': slave_build_path(spec['solutions'][0]['name']), |
| 407 'CheckoutSCM': 'gclient', | 423 'CheckoutSCM': 'gclient', |
| 408 'CheckoutSpec': spec | 424 'CheckoutSpec': spec |
| 409 } | 425 } |
| 410 ), | 426 ), |
| 411 ] | 427 ] |
| 412 if clean_step: | 428 if clean_step: |
| 413 steps.append(clean_step) | 429 steps.append(clean_step) |
| 414 if sync_step: | 430 if sync_step: |
| 415 steps.append(sync_step) | 431 steps.append(sync_step) |
| 416 | 432 |
| 417 return steps | 433 return steps |
| OLD | NEW |