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

Unified Diff: recipe_engine/run.py

Issue 2415793003: Setup basic Runtime with properties and platform.
Patch Set: Split out, more immutables, better utilization. Created 4 years, 2 months 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 side-by-side diff with in-line comments
Download patch
Index: recipe_engine/run.py
diff --git a/recipe_engine/run.py b/recipe_engine/run.py
index 4261a3cf0552ca739619ddb188de77ec47e73f94..8ead3f3268c85896db7dbb01c64f90329d7a627c 100644
--- a/recipe_engine/run.py
+++ b/recipe_engine/run.py
@@ -64,9 +64,9 @@ iterable_of_things.
"""
import collections
+import copy
import json
import os
-import sys
import traceback
from . import env
@@ -188,12 +188,11 @@ RecipeResult = collections.namedtuple('RecipeResult', 'result')
# TODO(dnj): Replace "properties" with a generic runtime instance. This instance
# will be used to seed recipe clients and expanded to include managed runtime
# entities.
-def run_steps(properties, stream_engine, step_runner, universe_view):
+def run_steps(rt, stream_engine, step_runner, universe_view):
"""Runs a recipe (given by the 'recipe' property).
Args:
- properties: a dictionary of properties to pass to the recipe. The
- 'recipe' property defines which recipe to actually run.
+ rt (Runtime): The runtime instance.
stream_engine: the StreamEngine to use to create individual step streams.
step_runner: The StepRunner to use to 'actually run' the steps.
universe_view: The RecipeUniverse to use to load the recipes & modules.
@@ -201,14 +200,14 @@ def run_steps(properties, stream_engine, step_runner, universe_view):
Returns: RecipeResult
"""
with stream_engine.make_step_stream('setup_build') as s:
- engine = RecipeEngine(step_runner, properties, universe_view)
+ engine = RecipeEngine(step_runner, rt, universe_view)
# Create all API modules and top level RunSteps function. It doesn't launch
# any recipe code yet; RunSteps needs to be called.
api = None
- assert 'recipe' in properties
- recipe = properties['recipe']
+ assert 'recipe' in rt.properties
+ recipe = rt.properties['recipe']
root_package = universe_view.universe.package_deps.root_package
run_recipe_help_lines = [
@@ -218,7 +217,7 @@ def run_steps(properties, stream_engine, step_runner, universe_view):
'%s run --properties-file - %s <<EOF' % (
os.path.join( '.', root_package.relative_recipes_dir, 'recipes.py'),
recipe),
- '%s' % json.dumps(properties),
+ '%s' % rt.properties.to_json(sort_keys=True),
'EOF',
'',
'To run on Windows, you can put the JSON in a file and redirect the',
@@ -229,12 +228,12 @@ def run_steps(properties, stream_engine, step_runner, universe_view):
for line in run_recipe_help_lines:
l.write_line(line)
- _isolate_environment()
+ os.environ = _isolate_environment(rt, os.environ)
# Find and load the recipe to run.
try:
recipe_script = universe_view.load_recipe(recipe, engine=engine)
- s.write_line('Running recipe with %s' % (properties,))
+ s.write_line('Running recipe with %s' % (rt.properties,))
api = loader.create_recipe_api(recipe_script.LOADED_DEPS,
engine,
@@ -252,23 +251,47 @@ def run_steps(properties, stream_engine, step_runner, universe_view):
# Run the steps emitted by a recipe via the engine, emitting annotations
# into |stream| along the way.
- return engine.run(recipe_script, api, properties)
+ return engine.run(recipe_script, api)
-def _isolate_environment():
+def _isolate_environment(rt, env):
"""Isolate the environment to a known subset set."""
- if sys.platform.startswith('win'):
+ if rt.platform.is_win():
whitelist = ENV_WHITELIST_WIN
- elif sys.platform in ('darwin', 'posix', 'linux2'):
+ elif rt.platform.is_posix():
whitelist = ENV_WHITELIST_POSIX
else:
- print ('WARNING: unknown platform %s, not isolating environment.' %
- sys.platform)
- return
+ print 'WARNING: unknown platform %s, not isolating environment.' % (
+ rt.platform,)
+ return env
+ return {k: v for k, v in env.iteritems() if k in whitelist}
- for k in os.environ.keys():
- if k not in whitelist:
- del os.environ[k]
+
+class Runtime(object):
+ """Container for instance-global state."""
+
+ def __init__(self, properties, platform=None):
+ # Store both the frozen and mutable properties. We will use the frozen ones
+ # internally to ensure recipe engine code doesn't modify them. The
+ # properties client may serve the original properties dict.
+ self._properties = types.freeze(properties)
+ self._properties_dict = copy.deepcopy(properties)
+
+ self._platform = platform if platform else util.Platform.probe()
+
+ @property
+ def properties(self):
+ """Returns (types.FrozenDict): The input properties."""
+ return self._properties
+
+ def mutable_properties(self):
+ """Returns (dict): A copy of the input properties."""
+ return copy.deepcopy(self._properties_dict)
+
+ @property
+ def platform(self):
+ """Returns (util.Platform): The current running Platform."""
+ return self._platform
class RecipeEngine(object):
@@ -276,23 +299,20 @@ class RecipeEngine(object):
Knows how to execute steps emitted by a recipe, holds global state such as
step history and build properties. Each recipe module API has a reference to
this object.
-
- Recipe modules that are aware of the engine:
- * properties - uses engine.properties.
- * step - uses engine.create_step(...), and previous_step_result.
"""
ActiveStep = collections.namedtuple('ActiveStep', (
'config', 'step_result', 'open_step'))
- def __init__(self, step_runner, properties, universe_view):
+ def __init__(self, step_runner, rt, universe_view):
"""See run_steps() for parameter meanings."""
self._step_runner = step_runner
- self._properties = properties
+ self._rt = rt
self._universe_view = universe_view
self._clients = {client.IDENT: client for client in (
recipe_api.StepClient(self),
- recipe_api.PropertiesClient(self),
+ recipe_api.PropertiesClient(self._rt),
+ recipe_api.PlatformClient(self._rt.platform),
recipe_api.DependencyManagerClient(self),
)}
@@ -301,21 +321,9 @@ class RecipeEngine(object):
# When we pop from this stack, we close the corresponding step stream.
self._step_stack = []
- # TODO(iannucci): come up with a more structured way to advertise/set mode
- # flags/options for the engine.
- if '$recipe_engine' in properties:
- options = properties['$recipe_engine']
- try:
- mode_flags = options.get('mode_flags')
- if mode_flags:
- if mode_flags.get('use_subprocess42'):
- print "IGNORING MODE_SUBPROCESS42"
- except Exception as e:
- print "Failed to set recipe_engine options, got: %r: %s" % (options, e)
-
@property
def properties(self):
- return self._properties
+ return self._rt.properties
@property
def universe(self):
@@ -385,7 +393,7 @@ class RecipeEngine(object):
raise exc(step_config.name, step_result)
- def run(self, recipe_script, api, properties):
+ def run(self, recipe_script, api):
"""Run a recipe represented by a recipe_script object.
This function blocks until recipe finishes.
@@ -396,7 +404,6 @@ class RecipeEngine(object):
recipe_script: The recipe to run, as represented by a RecipeScript object.
api: The api, with loaded module dependencies.
Used by the some special modules.
- properties: a dictionary of properties to pass to the recipe.
Returns:
RecipeResult which has return value or status code and exception.
@@ -406,7 +413,7 @@ class RecipeEngine(object):
with self._step_runner.run_context():
try:
try:
- recipe_result = recipe_script.run(api, properties)
+ recipe_result = recipe_script.run(api, self._rt)
result = {
"recipe_result": recipe_result,
"status_code": 0

Powered by Google App Engine
This is Rietveld 408576698