OLD | NEW |
1 # Copyright (c) 2013-2015 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2013-2015 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 """Entry point for fully-annotated builds. | 5 """Entry point for fully-annotated builds. |
6 | 6 |
7 This script is part of the effort to move all builds to annotator-based | 7 This script is part of the effort to move all builds to annotator-based |
8 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() | 8 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() |
9 found in scripts/master/factory/annotator_factory.py executes a single | 9 found in scripts/master/factory/annotator_factory.py executes a single |
10 AddAnnotatedScript step. That step (found in annotator_commands.py) calls | 10 AddAnnotatedScript step. That step (found in annotator_commands.py) calls |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 | 65 |
66 import collections | 66 import collections |
67 import contextlib | 67 import contextlib |
68 import copy | 68 import copy |
69 import functools | 69 import functools |
70 import json | 70 import json |
71 import os | 71 import os |
72 import re | 72 import re |
73 import subprocess | 73 import subprocess |
74 import sys | 74 import sys |
| 75 import tempfile |
75 import threading | 76 import threading |
76 import traceback | 77 import traceback |
77 | 78 |
78 import cStringIO | 79 import cStringIO |
79 | 80 |
80 | 81 |
81 from . import loader | 82 from . import loader |
82 from . import recipe_api | 83 from . import recipe_api |
83 from . import recipe_test_api | 84 from . import recipe_test_api |
| 85 from . import types |
84 from . import util | 86 from . import util |
85 | 87 |
86 | 88 |
87 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) | 89 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) |
88 | 90 |
89 BUILDBOT_MAGIC_ENV = set([ | 91 BUILDBOT_MAGIC_ENV = set([ |
90 'BUILDBOT_BLAMELIST', | 92 'BUILDBOT_BLAMELIST', |
91 'BUILDBOT_BRANCH', | 93 'BUILDBOT_BRANCH', |
92 'BUILDBOT_BUILDBOTURL', | 94 'BUILDBOT_BUILDBOTURL', |
93 'BUILDBOT_BUILDERNAME', | 95 'BUILDBOT_BUILDERNAME', |
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
426 properties.pop('root', None) | 428 properties.pop('root', None) |
427 | 429 |
428 # TODO(iannucci): A much better way to do this would be to dynamically | 430 # TODO(iannucci): A much better way to do this would be to dynamically |
429 # detect if the mirrors are actually available during the execution of the | 431 # detect if the mirrors are actually available during the execution of the |
430 # recipe. | 432 # recipe. |
431 if ('use_mirror' not in properties and ( | 433 if ('use_mirror' not in properties and ( |
432 'TESTING_MASTERNAME' in os.environ or | 434 'TESTING_MASTERNAME' in os.environ or |
433 'TESTING_SLAVENAME' in os.environ)): | 435 'TESTING_SLAVENAME' in os.environ)): |
434 properties['use_mirror'] = False | 436 properties['use_mirror'] = False |
435 | 437 |
436 engine = RecipeEngine(stream, properties, test_data) | 438 engine = RecipeEngine(stream, properties, test_data, universe) |
437 | 439 |
438 # Create all API modules and top level RunSteps function. It doesn't launch | 440 # Create all API modules and top level RunSteps function. It doesn't launch |
439 # any recipe code yet; RunSteps needs to be called. | 441 # any recipe code yet; RunSteps needs to be called. |
440 api = None | 442 api = None |
441 with stream.step('setup_build') as s: | 443 with stream.step('setup_build') as s: |
442 assert 'recipe' in properties | 444 assert 'recipe' in properties |
443 recipe = properties['recipe'] | 445 recipe = properties['recipe'] |
444 | 446 |
445 properties_to_print = properties.copy() | 447 properties_to_print = properties.copy() |
446 if 'use_mirror' in properties: | 448 if 'use_mirror' in properties: |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
743 Knows how to execute steps emitted by a recipe, holds global state such as | 745 Knows how to execute steps emitted by a recipe, holds global state such as |
744 step history and build properties. Each recipe module API has a reference to | 746 step history and build properties. Each recipe module API has a reference to |
745 this object. | 747 this object. |
746 | 748 |
747 Recipe modules that are aware of the engine: | 749 Recipe modules that are aware of the engine: |
748 * properties - uses engine.properties. | 750 * properties - uses engine.properties. |
749 * step_history - uses engine.step_history. | 751 * step_history - uses engine.step_history. |
750 * step - uses engine.create_step(...). | 752 * step - uses engine.create_step(...). |
751 | 753 |
752 """ | 754 """ |
753 def __init__(self, stream, properties, test_data): | 755 def __init__(self, stream, properties, test_data, universe): |
754 self._stream = stream | 756 self._stream = stream |
755 self._properties = properties | 757 self._properties = properties |
756 self._test_data = test_data | 758 self._test_data = test_data |
757 self._step_history = collections.OrderedDict() | 759 self._step_history = collections.OrderedDict() |
| 760 self._universe = universe |
758 | 761 |
759 self._previous_step_annotation = None | 762 self._previous_step_annotation = None |
760 self._previous_step_result = None | 763 self._previous_step_result = None |
761 self._api = None | 764 self._api = None |
762 | 765 |
763 @property | 766 @property |
764 def properties(self): | 767 def properties(self): |
765 return self._properties | 768 return self._properties |
766 | 769 |
767 @property | 770 @property |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
854 exc = recipe_api.InfraFailure | 857 exc = recipe_api.InfraFailure |
855 | 858 |
856 step_result.presentation.status = state | 859 step_result.presentation.status = state |
857 if step_test.enabled: | 860 if step_test.enabled: |
858 # To avoid cluttering the expectations, don't emit this in testmode. | 861 # To avoid cluttering the expectations, don't emit this in testmode. |
859 self._previous_step_annotation.emit( | 862 self._previous_step_annotation.emit( |
860 'step returned non-zero exit code: %d' % step_result.retcode) | 863 'step returned non-zero exit code: %d' % step_result.retcode) |
861 | 864 |
862 raise exc(step['name'], step_result) | 865 raise exc(step['name'], step_result) |
863 | 866 |
864 | |
865 def run(self, recipe_script, api): | 867 def run(self, recipe_script, api): |
866 """Run a recipe represented by a recipe_script object. | 868 """Run a recipe represented by a recipe_script object. |
867 | 869 |
868 This function blocks until recipe finishes. | 870 This function blocks until recipe finishes. |
| 871 It mainly executes the recipe, and has some exception handling logic, and |
| 872 adds the step history to the result. |
869 | 873 |
870 Args: | 874 Args: |
871 recipe_script: The recipe to run, as represented by a RecipeScript object. | 875 recipe_script: The recipe to run, as represented by a RecipeScript object. |
872 api: The api, with loaded module dependencies. | 876 api: The api, with loaded module dependencies. |
873 Used by the some special modules. | 877 Used by the some special modules. |
874 | 878 |
875 Returns: | 879 Returns: |
876 RecipeExecutionResult which has status code, list of steps ran, | 880 RecipeExecutionResult which has status code, list of steps ran, |
877 and return value. | 881 and return value. |
878 """ | 882 """ |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
933 | 937 |
934 Args: | 938 Args: |
935 step: ConfigGroup object with information about the step, see | 939 step: ConfigGroup object with information about the step, see |
936 recipe_modules/step/config.py. | 940 recipe_modules/step/config.py. |
937 | 941 |
938 Returns: | 942 Returns: |
939 Opaque engine specific object that is understood by 'run_steps' method. | 943 Opaque engine specific object that is understood by 'run_steps' method. |
940 """ | 944 """ |
941 return step.as_jsonish() | 945 return step.as_jsonish() |
942 | 946 |
| 947 def depend_on(self, recipe, properties, distributor=None): |
| 948 return self.depend_on_multi( |
| 949 ((recipe, properties),), distributor=distributor)[0] |
943 | 950 |
| 951 def depend_on_multi(self, dependencies, distributor=None): |
| 952 results = [] |
| 953 for recipe, properties in dependencies: |
| 954 recipe_script = self._universe.load_recipe(recipe) |
| 955 |
| 956 return_schema = getattr(recipe_script, 'RETURN_SCHEMA', None) |
| 957 if not return_schema: |
| 958 raise ValueError( |
| 959 "Invalid recipe %s. Recipe must have a return schema." % recipe) |
| 960 |
| 961 # run_recipe is a function which will be called once the properties have |
| 962 # been validated by the recipe engine. The arguments being passed in are |
| 963 # simply the values being passed to the recipe, which we already know, so |
| 964 # we ignore them. We're only using this for its properties validation |
| 965 # functionality. |
| 966 run_recipe = lambda *args, **kwargs: None |
| 967 |
| 968 if self._test_data.enabled: |
| 969 run_recipe = lambda *args, **kwargs: ( |
| 970 self._test_data.depend_on_data[types.freeze((recipe, properties),)]) |
| 971 else: |
| 972 # TODO(martiniss) Add DM integration |
| 973 assert distributor is None, "Only local recipe execution supported." |
| 974 |
| 975 def run_recipe(*args, **kwargs): |
| 976 with tempfile.NamedTemporaryFile() as f: |
| 977 cmd = [sys.executable, |
| 978 self._universe.package_deps.engine_recipes_py, |
| 979 '--package=%s' % self._universe.config_file, 'run', |
| 980 '--output-result-json=%s' % f.name, recipe] |
| 981 cmd.extend(['%s=%s' % (k, v) for k, v in properties.iteritems()]) |
| 982 |
| 983 retcode = subprocess.call(cmd) |
| 984 result = json.load(f) |
| 985 if retcode != 0: |
| 986 raise recipe_api.StepFailure( |
| 987 'depend on %s with properties %r failed with %d.' |
| 988 'Recipe result: %r' % ( |
| 989 recipe, properties, retcode, result)) |
| 990 return result |
| 991 |
| 992 try: |
| 993 # This does type checking for properties |
| 994 results.append( |
| 995 loader._invoke_with_properties( |
| 996 run_recipe, properties, recipe_script.PROPERTIES, |
| 997 properties.keys())) |
| 998 except TypeError as e: |
| 999 raise TypeError( |
| 1000 "Got %r while trying to call recipe %s with properties %r" % ( |
| 1001 e, recipe, properties)) |
| 1002 |
| 1003 return results |
| 1004 |
| 1005 |
| 1006 |
OLD | NEW |