Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: recipe_engine/run.py

Issue 1421843006: Add simple depends_on API. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/recipes-py@master
Patch Set: Fix small broken tests, add invoke tests, respond to nits. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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)
iannucci 2015/11/20 02:00:19 probably also something for the 'advanced' API, bu
martiniss 2015/11/23 22:05:07 True. Will require additional thinking. But we can
955 # run_recipe is a function which will be called once the properties have
956 # been validated by the recipe engine. The arguments being passed in are
957 # simply the values being passed to the recipe, which we already know, so
958 # we ignore them. We're only using this for its properties validation
959 # functionality.
960 run_recipe = lambda *args, **kwargs: None
961
962 if self._test_data.enabled:
963 run_recipe = lambda *args, **kwargs: (
964 self._test_data.depend_on_data[types.freeze((recipe, properties),)])
965 else:
966 # TODO(martiniss) Add DM integration
967 # For now, we just run the recipe locally.
iannucci 2015/11/20 02:00:19 assert distributor == None?
martiniss 2015/11/23 22:05:07 Good idea.
968 def run_recipe(*args, **kwargs):
969 with tempfile.NamedTemporaryFile() as f:
970 cmd = [sys.executable,
971 self._universe.package_deps.engine_recipes_py,
972 '--package=%s' % self._universe.config_file, 'run',
973 '--output-result-json=%s' % f.name, recipe]
974 cmd.extend(['%s=%s' % (k, v) for k, v in properties.iteritems()])
975
976 retcode = subprocess.call(cmd)
977 result = json.load(f)
978 if retcode != 0:
979 raise recipe_api.StepFailure(
980 'depend on %s with properties %r failed with %d.'
981 'Recipe result: %r' % (
982 recipe, properties, retcode, result))
983 return result
984
985 try:
986 # This does type checking for properties
iannucci 2015/11/20 02:00:19 presumably distributor calculation would happen he
martiniss 2015/11/23 22:05:07 What do you mean calculation? I believe the dist
987 results.append(loader._invoke_with_properties(
988 run_recipe, properties, recipe_script.PROPERTIES, properties.keys()) )
989 except TypeError as e:
990 raise TypeError(
991 "Got %r while trying to call recipe %s with properties %r" % (
992 e, recipe, properties))
993
994 return results
995
996
997
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698