| 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
|
|
|