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

Side by Side Diff: third_party/recipe_engine/recipe_loader.py

Issue 1151423002: Move recipe engine to third_party/recipe_engine. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Copyright notices Created 5 years, 7 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 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 import contextlib
5 import imp 6 import imp
6 import inspect 7 import inspect
7 import os 8 import os
8 import sys 9 import sys
9 10
10 from .recipe_util import (ROOT_PATH, RECIPE_DIRS, MODULE_DIRS, 11 from .recipe_util import scan_directory
11 cached_unary, scan_directory)
12 from .recipe_api import RecipeApi, RecipeApiPlain 12 from .recipe_api import RecipeApi, RecipeApiPlain
13 from .recipe_config import ConfigContext 13 from .recipe_config import ConfigContext
14 from .recipe_config_types import Path, ModuleBasePath, RECIPE_MODULE_PREFIX 14 from .recipe_config_types import Path, ModuleBasePath, RECIPE_MODULE_PREFIX
15 from .recipe_test_api import RecipeTestApi, DisabledTestData 15 from .recipe_test_api import RecipeTestApi, DisabledTestData
16 16
17 17
18 class NoSuchRecipe(Exception): 18 class NoSuchRecipe(Exception):
19 """Raised by load_recipe is recipe is not found.""" 19 """Raised by load_recipe is recipe is not found."""
20 20
21 21
22 class RecipeScript(object): 22 class RecipeScript(object):
23 """Holds dict of an evaluated recipe script.""" 23 """Holds dict of an evaluated recipe script."""
24 24
25 def __init__(self, recipe_dict): 25 def __init__(self, recipe_dict):
26 for k, v in recipe_dict.iteritems(): 26 for k, v in recipe_dict.iteritems():
27 setattr(self, k, v) 27 setattr(self, k, v)
28 28
29 @classmethod 29 @classmethod
30 def from_script_path(cls, script_path, universe): 30 def from_script_path(cls, script_path, universe):
31 """Evaluates a script and returns RecipeScript instance.""" 31 """Evaluates a script and returns RecipeScript instance."""
32
32 script_vars = {} 33 script_vars = {}
33 execfile(script_path, script_vars)
34 script_vars['__file__'] = script_path 34 script_vars['__file__'] = script_path
35
36 with _preserve_path():
37 execfile(script_path, script_vars)
38
35 script_vars['LOADED_DEPS'] = universe.deps_from_mixed( 39 script_vars['LOADED_DEPS'] = universe.deps_from_mixed(
36 script_vars.get('DEPS', []), os.path.basename(script_path)) 40 script_vars.get('DEPS', []), os.path.basename(script_path))
37 return cls(script_vars) 41 return cls(script_vars)
38 42
39 43
40 class Dependency(object): 44 class Dependency(object):
41 def load(self, universe): 45 def load(self, universe):
42 raise NotImplementedError() 46 raise NotImplementedError()
43 47
44 @property 48 @property
45 def local_name(self): 49 def local_name(self):
46 raise NotImplementedError() 50 raise NotImplementedError()
47 51
48 @property 52 @property
49 def unique_name(self): 53 def unique_name(self):
50 """A unique identifier for the module that this dependency refers to. 54 """A unique identifier for the module that this dependency refers to.
51 This must be generated without loading the module.""" 55 This must be generated without loading the module."""
52 raise NotImplementedError() 56 raise NotImplementedError()
53 57
54 58
55 class PathDependency(Dependency): 59 class PathDependency(Dependency):
56 def __init__(self, path, local_name, base_path=None): 60 def __init__(self, path, local_name, universe, base_path=None):
57 self._path = _normalize_path(base_path, path) 61 self._path = _normalize_path(base_path, path)
58 self._local_name = local_name 62 self._local_name = local_name
59 63
60 # We forbid modules from living outside our main paths to keep clients 64 # We forbid modules from living outside our main paths to keep clients
61 # from going crazy before we have standardized recipe locations. 65 # from going crazy before we have standardized recipe locations.
62 mod_dir = os.path.dirname(path) 66 mod_dir = os.path.dirname(path)
63 assert mod_dir in MODULE_DIRS(), ( 67 assert mod_dir in universe.module_dirs, (
64 'Modules living outside of approved directories are forbidden: ' 68 'Modules living outside of approved directories are forbidden: '
65 '%s is not in %s' % (mod_dir, MODULE_DIRS())) 69 '%s is not in %s' % (mod_dir, universe.module_dirs))
66 70
67 def load(self, universe): 71 def load(self, universe):
68 return _load_recipe_module_module(self._path, universe) 72 return _load_recipe_module_module(self._path, universe)
69 73
70 @property 74 @property
71 def local_name(self): 75 def local_name(self):
72 return self._local_name 76 return self._local_name
73 77
74 @property 78 @property
75 def unique_name(self): 79 def unique_name(self):
76 return self._path 80 return self._path
77 81
78 82
79 class NamedDependency(PathDependency): 83 class NamedDependency(PathDependency):
80 def __init__(self, name): 84 def __init__(self, name, universe):
81 for path in MODULE_DIRS(): 85 for path in universe.module_dirs:
82 mod_path = os.path.join(path, name) 86 mod_path = os.path.join(path, name)
83 if os.path.exists(os.path.join(mod_path, '__init__.py')): 87 if os.path.exists(os.path.join(mod_path, '__init__.py')):
84 super(NamedDependency, self).__init__(mod_path, name) 88 super(NamedDependency, self).__init__(mod_path, name, universe=universe)
85 return 89 return
86 raise NoSuchRecipe('Recipe module named %s does not exist' % name) 90 raise NoSuchRecipe('Recipe module named %s does not exist' % name)
87 91
88 92
89 class RecipeUniverse(object): 93 class RecipeUniverse(object):
90 def __init__(self): 94 def __init__(self, module_dirs, recipe_dirs):
91 self._loaded = {} 95 self._loaded = {}
96 self._module_dirs = module_dirs[:]
97 self._recipe_dirs = recipe_dirs[:]
98
99 @property
100 def module_dirs(self):
101 return self._module_dirs
102
103 @property
104 def recipe_dirs(self):
105 return self._recipe_dirs
92 106
93 def load(self, dep): 107 def load(self, dep):
94 """Load a Dependency.""" 108 """Load a Dependency."""
95 name = dep.unique_name 109 name = dep.unique_name
96 if name in self._loaded: 110 if name in self._loaded:
97 mod = self._loaded[name] 111 mod = self._loaded[name]
98 assert mod is not None, ( 112 assert mod is not None, (
99 'Cyclic dependency when trying to load %s' % name) 113 'Cyclic dependency when trying to load %s' % name)
100 return mod 114 return mod
101 else: 115 else:
102 self._loaded[name] = None 116 self._loaded[name] = None
103 mod = dep.load(self) 117 mod = dep.load(self)
104 self._loaded[name] = mod 118 self._loaded[name] = mod
105 return mod 119 return mod
106 120
107 def deps_from_names(self, deps): 121 def deps_from_names(self, deps):
108 """Load dependencies given a list simple module names (old style).""" 122 """Load dependencies given a list simple module names (old style)."""
109 return { dep: self.load(NamedDependency(dep)) for dep in deps } 123 return { dep: self.load(NamedDependency(dep, universe=self))
124 for dep in deps }
110 125
111 def deps_from_paths(self, deps, base_path): 126 def deps_from_paths(self, deps, base_path):
112 """Load dependencies given a dictionary of local names to module paths 127 """Load dependencies given a dictionary of local names to module paths
113 (new style).""" 128 (new style)."""
114 return { name: self.load(PathDependency(path, name, base_path)) 129 return { name: self.load(PathDependency(path, name,
130 universe=self, base_path=base_path))
115 for name, path in deps.iteritems() } 131 for name, path in deps.iteritems() }
116 132
117 def deps_from_mixed(self, deps, base_path): 133 def deps_from_mixed(self, deps, base_path):
118 """Load dependencies given either a new style or old style deps spec.""" 134 """Load dependencies given either a new style or old style deps spec."""
119 if isinstance(deps, (list, tuple)): 135 if isinstance(deps, (list, tuple)):
120 return self.deps_from_names(deps) 136 return self.deps_from_names(deps)
121 elif isinstance(deps, dict): 137 elif isinstance(deps, dict):
122 return self.deps_from_paths(deps, base_path) 138 return self.deps_from_paths(deps, base_path)
123 else: 139 else:
124 raise ValueError('%s is not a valid or known deps structure' % deps) 140 raise ValueError('%s is not a valid or known deps structure' % deps)
125 141
126 def load_recipe(self, recipe): 142 def load_recipe(self, recipe):
127 """Given name of a recipe, loads and returns it as RecipeScript instance. 143 """Given name of a recipe, loads and returns it as RecipeScript instance.
128 144
129 Args: 145 Args:
130 recipe (str): name of a recipe, can be in form '<module>:<recipe>'. 146 recipe (str): name of a recipe, can be in form '<module>:<recipe>'.
131 147
132 Returns: 148 Returns:
133 RecipeScript instance. 149 RecipeScript instance.
134 150
135 Raises: 151 Raises:
136 NoSuchRecipe: recipe is not found. 152 NoSuchRecipe: recipe is not found.
137 """ 153 """
138 # If the recipe is specified as "module:recipe", then it is an recipe 154 # If the recipe is specified as "module:recipe", then it is an recipe
139 # contained in a recipe_module as an example. Look for it in the modules 155 # contained in a recipe_module as an example. Look for it in the modules
140 # imported by load_recipe_modules instead of the normal search paths. 156 # imported by load_recipe_modules instead of the normal search paths.
141 if ':' in recipe: 157 if ':' in recipe:
142 module_name, example = recipe.split(':') 158 module_name, example = recipe.split(':')
143 assert example.endswith('example') 159 assert example.endswith('example')
144 for module_dir in MODULE_DIRS(): 160 for module_dir in self.module_dirs:
145 for subitem in os.listdir(module_dir): 161 for subitem in os.listdir(module_dir):
146 if module_name == subitem: 162 if module_name == subitem:
147 return RecipeScript.from_script_path( 163 return RecipeScript.from_script_path(
148 os.path.join(module_dir, subitem, 'example.py'), self) 164 os.path.join(module_dir, subitem, 'example.py'), self)
149 raise NoSuchRecipe(recipe, 165 raise NoSuchRecipe(recipe,
150 'Recipe example %s:%s does not exist' % 166 'Recipe example %s:%s does not exist' %
151 (module_name, example)) 167 (module_name, example))
152 else: 168 else:
153 for recipe_path in (os.path.join(p, recipe) for p in RECIPE_DIRS()): 169 for recipe_path in (os.path.join(p, recipe) for p in self.recipe_dirs):
154 if os.path.exists(recipe_path + '.py'): 170 if os.path.exists(recipe_path + '.py'):
155 return RecipeScript.from_script_path(recipe_path + '.py', self) 171 return RecipeScript.from_script_path(recipe_path + '.py', self)
156 raise NoSuchRecipe(recipe) 172 raise NoSuchRecipe(recipe)
157 173
174 def loop_over_recipe_modules(self):
175 for path in self.module_dirs:
176 if os.path.isdir(path):
177 for item in os.listdir(path):
178 subpath = os.path.join(path, item)
179 if os.path.isdir(subpath):
180 yield subpath
158 181
182 def loop_over_recipes(self):
183 """Yields pairs (path to recipe, recipe name).
184
185 Enumerates real recipes in recipes/* as well as examples in recipe_modules/* .
186 """
187 for path in self.recipe_dirs:
188 for recipe in scan_directory(
189 path, lambda f: f.endswith('.py') and f[0] != '_'):
190 yield recipe, recipe[len(path)+1:-len('.py')]
191 for path in self.module_dirs:
192 for recipe in scan_directory(
193 path, lambda f: f.endswith('example.py')):
194 module_name = os.path.dirname(recipe)[len(path)+1:]
195 yield recipe, '%s:example' % module_name
196
197
198 @contextlib.contextmanager
199 def _preserve_path():
200 old_path = sys.path[:]
201 try:
202 yield
203 finally:
204 sys.path = old_path
205
206
207 def _normalize_path(base_path, path):
208 if base_path is None or os.path.isabs(path):
209 return os.path.realpath(path)
210 else:
211 return os.path.realpath(os.path.join(base_path, path))
159 212
160 213
161 def _find_and_load_module(fullname, modname, path): 214 def _find_and_load_module(fullname, modname, path):
162 imp.acquire_lock() 215 imp.acquire_lock()
163 try: 216 try:
164 if fullname not in sys.modules: 217 if fullname not in sys.modules:
165 fil = None 218 fil = None
166 try: 219 try:
167 fil, pathname, descr = imp.find_module(modname, 220 fil, pathname, descr = imp.find_module(modname,
168 [os.path.dirname(path)]) 221 [os.path.dirname(path)])
169 imp.load_module(fullname, fil, pathname, descr) 222 imp.load_module(fullname, fil, pathname, descr)
170 finally: 223 finally:
171 if fil: 224 if fil:
172 fil.close() 225 fil.close()
173 return sys.modules[fullname] 226 return sys.modules[fullname]
174 finally: 227 finally:
175 imp.release_lock() 228 imp.release_lock()
176 229
177 230
178 def _load_recipe_module_module(path, universe): 231 def _load_recipe_module_module(path, universe):
179 modname = os.path.splitext(os.path.basename(path))[0] 232 modname = os.path.splitext(os.path.basename(path))[0]
180 fullname = '%s.%s' % (RECIPE_MODULE_PREFIX, modname) 233 fullname = '%s.%s' % (RECIPE_MODULE_PREFIX, modname)
181 mod = _find_and_load_module(fullname, modname, path) 234 mod = _find_and_load_module(fullname, modname, path)
182 235
183 # This actually loads the dependencies. 236 # This actually loads the dependencies.
184 mod.LOADED_DEPS = universe.deps_from_mixed( 237 mod.LOADED_DEPS = universe.deps_from_mixed(
185 getattr(mod, 'DEPS', []), os.path.basename(path)) 238 getattr(mod, 'DEPS', []), os.path.basename(path))
186 239
187 # TODO(luqui): Remove this hack once configs are cleaned. 240 # Prevent any modules that mess with sys.path from leaking.
188 sys.modules['%s.DEPS' % fullname] = mod.LOADED_DEPS 241 with _preserve_path():
189 _recursive_import(path, RECIPE_MODULE_PREFIX) 242 # TODO(luqui): Remove this hack once configs are cleaned.
190 _patchup_module(modname, mod) 243 sys.modules['%s.DEPS' % fullname] = mod.LOADED_DEPS
244 _recursive_import(path, RECIPE_MODULE_PREFIX)
245 _patchup_module(modname, mod)
191 246
192 return mod 247 return mod
193 248
194 249
195 def _recursive_import(path, prefix): 250 def _recursive_import(path, prefix):
196 modname = os.path.splitext(os.path.basename(path))[0] 251 modname = os.path.splitext(os.path.basename(path))[0]
197 fullname = '%s.%s' % (prefix, modname) 252 fullname = '%s.%s' % (prefix, modname)
198 mod = _find_and_load_module(fullname, modname, path) 253 mod = _find_and_load_module(fullname, modname, path)
199 if not os.path.isdir(path): 254 if not os.path.isdir(path):
200 return mod 255 return mod
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 setattr(modapi.m, k, v) 383 setattr(modapi.m, k, v)
329 return modapi 384 return modapi
330 385
331 mapper = DependencyMapper(instantiator) 386 mapper = DependencyMapper(instantiator)
332 api = RecipeTestApi(module=None) 387 api = RecipeTestApi(module=None)
333 for k,v in toplevel_deps.iteritems(): 388 for k,v in toplevel_deps.iteritems():
334 setattr(api, k, mapper.instantiate(v)) 389 setattr(api, k, mapper.instantiate(v))
335 return api 390 return api
336 391
337 392
338 def loop_over_recipe_modules():
339 for path in MODULE_DIRS():
340 if os.path.isdir(path):
341 for item in os.listdir(path):
342 subpath = os.path.join(path, item)
343 if os.path.isdir(subpath):
344 yield subpath
345 393
346
347 def loop_over_recipes():
348 """Yields pairs (path to recipe, recipe name).
349
350 Enumerates real recipes in recipes/* as well as examples in recipe_modules/*.
351 """
352 for path in RECIPE_DIRS():
353 for recipe in scan_directory(
354 path, lambda f: f.endswith('.py') and f[0] != '_'):
355 yield recipe, recipe[len(path)+1:-len('.py')]
356 for path in MODULE_DIRS():
357 for recipe in scan_directory(
358 path, lambda f: f.endswith('example.py')):
359 module_name = os.path.dirname(recipe)[len(path)+1:]
360 yield recipe, '%s:example' % module_name
361
362
363 def _normalize_path(base_path, path):
364 if base_path is None or os.path.isabs(path):
365 return os.path.realpath(path)
366 else:
367 return os.path.realpath(os.path.join(base_path, path))
368
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698