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/loader.py

Issue 1241323004: Cross-repo recipe package system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Moved show_me_the_modules into recipe_engine Created 5 years, 4 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-2015 The Chromium Authors. All rights reserved. 1 # Copyright 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 import contextlib 5 import contextlib
6 import imp 6 import imp
7 import inspect 7 import inspect
8 import os 8 import os
9 import sys 9 import sys
10 10
(...skipping 18 matching lines...) Expand all
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
33 script_vars = {} 33 script_vars = {}
34 script_vars['__file__'] = script_path 34 script_vars['__file__'] = script_path
35 35
36 with _preserve_path(): 36 with _preserve_path():
37 execfile(script_path, script_vars) 37 execfile(script_path, script_vars)
38 38
39 script_vars['LOADED_DEPS'] = universe.deps_from_mixed( 39 script_vars['LOADED_DEPS'] = universe.deps_from_spec(
40 script_vars.get('DEPS', []), os.path.basename(script_path)) 40 script_vars.get('DEPS', []))
41 return cls(script_vars) 41 return cls(script_vars)
42 42
43 43
44 class Dependency(object): 44 class Dependency(object):
45 def load(self, universe): 45 def load(self, universe):
46 raise NotImplementedError() 46 raise NotImplementedError()
47 47
48 @property 48 @property
49 def local_name(self): 49 def local_name(self):
50 raise NotImplementedError() 50 raise NotImplementedError()
51 51
52 @property 52 @property
53 def unique_name(self): 53 def unique_name(self):
54 """A unique identifier for the module that this dependency refers to. 54 """A unique identifier for the module that this dependency refers to.
55 This must be generated without loading the module.""" 55 This must be generated without loading the module."""
56 raise NotImplementedError() 56 raise NotImplementedError()
57 57
58 58
59 class PathDependency(Dependency): 59 class PathDependency(Dependency):
60 def __init__(self, path, local_name, universe, base_path=None): 60 def __init__(self, path, local_name, universe):
61 self._path = _normalize_path(base_path, path) 61 assert os.path.isabs(path), (
62 'Path dependencies must be absolute, but %s is not' % path)
63 self._path = path
62 self._local_name = local_name 64 self._local_name = local_name
63 65
64 # We forbid modules from living outside our main paths to keep clients 66 # We forbid modules from living outside our main paths to keep clients
65 # from going crazy before we have standardized recipe locations. 67 # from going crazy before we have standardized recipe locations.
66 mod_dir = os.path.dirname(path) 68 mod_dir = os.path.dirname(path)
67 assert mod_dir in universe.module_dirs, ( 69 assert mod_dir in universe.module_dirs, (
68 'Modules living outside of approved directories are forbidden: ' 70 'Modules living outside of approved directories are forbidden: '
69 '%s is not in %s' % (mod_dir, universe.module_dirs)) 71 '%s is not in %s' % (mod_dir, universe.module_dirs))
70 72
71 def load(self, universe): 73 def load(self, universe):
(...skipping 11 matching lines...) Expand all
83 class NamedDependency(PathDependency): 85 class NamedDependency(PathDependency):
84 def __init__(self, name, universe): 86 def __init__(self, name, universe):
85 for path in universe.module_dirs: 87 for path in universe.module_dirs:
86 mod_path = os.path.join(path, name) 88 mod_path = os.path.join(path, name)
87 if _is_recipe_module_dir(mod_path): 89 if _is_recipe_module_dir(mod_path):
88 super(NamedDependency, self).__init__(mod_path, name, universe=universe) 90 super(NamedDependency, self).__init__(mod_path, name, universe=universe)
89 return 91 return
90 raise NoSuchRecipe('Recipe module named %s does not exist' % name) 92 raise NoSuchRecipe('Recipe module named %s does not exist' % name)
91 93
92 94
95 class PackageDependency(PathDependency):
96 # TODO(luqui): Forbid depending on a module from a (locally) undeclared
97 # dependency.
98 def __init__(self, package, module, local_name, universe):
99 mod_path = (
100 universe.package_deps.get_package(package).module_path(module))
101 super(PackageDependency, self).__init__(
102 mod_path, local_name, universe=universe)
103
104
93 class RecipeUniverse(object): 105 class RecipeUniverse(object):
94 def __init__(self, module_dirs, recipe_dirs): 106 def __init__(self, package_deps):
95 self._loaded = {} 107 self._loaded = {}
96 self._module_dirs = module_dirs[:] 108 self._package_deps = package_deps
97 self._recipe_dirs = recipe_dirs[:]
98 109
99 @property 110 @property
100 def module_dirs(self): 111 def module_dirs(self):
101 return self._module_dirs 112 return self._package_deps.all_module_dirs
102 113
103 @property 114 @property
104 def recipe_dirs(self): 115 def recipe_dirs(self):
105 return self._recipe_dirs 116 return self._package_deps.all_recipe_dirs
117
118 @property
119 def package_deps(self):
120 return self._package_deps
106 121
107 def load(self, dep): 122 def load(self, dep):
108 """Load a Dependency.""" 123 """Load a Dependency."""
109 name = dep.unique_name 124 name = dep.unique_name
110 if name in self._loaded: 125 if name in self._loaded:
111 mod = self._loaded[name] 126 mod = self._loaded[name]
112 assert mod is not None, ( 127 assert mod is not None, (
113 'Cyclic dependency when trying to load %s' % name) 128 'Cyclic dependency when trying to load %s' % name)
114 return mod 129 return mod
115 else: 130 else:
116 self._loaded[name] = None 131 self._loaded[name] = None
117 mod = dep.load(self) 132 mod = dep.load(self)
118 self._loaded[name] = mod 133 self._loaded[name] = mod
119 return mod 134 return mod
120 135
121 def deps_from_names(self, deps): 136 def _dep_from_name(self, name):
122 """Load dependencies given a list simple module names (old style).""" 137 if '/' in name:
123 return { dep: self.load(NamedDependency(dep, universe=self)) 138 [package,module] = name.split('/')
124 for dep in deps } 139 dep = PackageDependency(package, module, module, universe=self)
140 else:
141 # Old style: bare module name, search paths to find it.
142 module = name
143 dep = NamedDependency(name, universe=self)
125 144
126 def deps_from_paths(self, deps, base_path): 145 return module, dep
127 """Load dependencies given a dictionary of local names to module paths
128 (new style)."""
129 return { name: self.load(PathDependency(path, name,
130 universe=self, base_path=base_path))
131 for name, path in deps.iteritems() }
132 146
133 def deps_from_mixed(self, deps, base_path): 147 def deps_from_spec(self, spec):
134 """Load dependencies given either a new style or old style deps spec.""" 148 # Automatic local names.
135 if isinstance(deps, (list, tuple)): 149 if isinstance(spec, (list, tuple)):
136 return self.deps_from_names(deps) 150 deps = {}
137 elif isinstance(deps, dict): 151 for item in spec:
138 return self.deps_from_paths(deps, base_path) 152 name, dep = self._dep_from_name(item)
139 else: 153 deps[name] = self.load(dep)
140 raise ValueError('%s is not a valid or known deps structure' % deps) 154 # Explicit local names.
155 elif isinstance(spec, dict):
156 deps = {}
157 for name, item in spec.iteritems():
158 _, dep = self._dep_from_name(item)
159 deps[name] = self.load(dep)
160 return deps
141 161
142 def load_recipe(self, recipe): 162 def load_recipe(self, recipe):
143 """Given name of a recipe, loads and returns it as RecipeScript instance. 163 """Given name of a recipe, loads and returns it as RecipeScript instance.
144 164
145 Args: 165 Args:
146 recipe (str): name of a recipe, can be in form '<module>:<recipe>'. 166 recipe (str): name of a recipe, can be in form '<module>:<recipe>'.
147 167
148 Returns: 168 Returns:
149 RecipeScript instance. 169 RecipeScript instance.
150 170
151 Raises: 171 Raises:
152 NoSuchRecipe: recipe is not found. 172 NoSuchRecipe: recipe is not found.
153 """ 173 """
154 # If the recipe is specified as "module:recipe", then it is an recipe 174 # If the recipe is specified as "module:recipe", then it is an recipe
155 # contained in a recipe_module as an example. Look for it in the modules 175 # contained in a recipe_module as an example. Look for it in the modules
156 # imported by load_recipe_modules instead of the normal search paths. 176 # imported by load_recipe_modules instead of the normal search paths.
157 if ':' in recipe: 177 if ':' in recipe:
158 module_name, example = recipe.split(':') 178 module_name, example = recipe.split(':')
159 assert example.endswith('example') 179 assert example.endswith('example')
160 for module_dir in self.module_dirs: 180 for module_dir in self.module_dirs:
161 for subitem in os.listdir(module_dir): 181 if os.path.isdir(module_dir):
162 if module_name == subitem: 182 for subitem in os.listdir(module_dir):
163 return RecipeScript.from_script_path( 183 if module_name == subitem:
164 os.path.join(module_dir, subitem, 'example.py'), self) 184 return RecipeScript.from_script_path(
185 os.path.join(module_dir, subitem, 'example.py'), self)
165 raise NoSuchRecipe(recipe, 186 raise NoSuchRecipe(recipe,
166 'Recipe example %s:%s does not exist' % 187 'Recipe example %s:%s does not exist' %
167 (module_name, example)) 188 (module_name, example))
168 else: 189 else:
169 for recipe_path in (os.path.join(p, recipe) for p in self.recipe_dirs): 190 for recipe_path in (os.path.join(p, recipe) for p in self.recipe_dirs):
170 if os.path.exists(recipe_path + '.py'): 191 if os.path.exists(recipe_path + '.py'):
171 return RecipeScript.from_script_path(recipe_path + '.py', self) 192 return RecipeScript.from_script_path(recipe_path + '.py', self)
172 raise NoSuchRecipe(recipe) 193 raise NoSuchRecipe(recipe)
173 194
174 def loop_over_recipe_modules(self): 195 def loop_over_recipe_modules(self):
(...skipping 27 matching lines...) Expand all
202 223
203 @contextlib.contextmanager 224 @contextlib.contextmanager
204 def _preserve_path(): 225 def _preserve_path():
205 old_path = sys.path[:] 226 old_path = sys.path[:]
206 try: 227 try:
207 yield 228 yield
208 finally: 229 finally:
209 sys.path = old_path 230 sys.path = old_path
210 231
211 232
212 def _normalize_path(base_path, path):
213 if base_path is None or os.path.isabs(path):
214 return os.path.realpath(path)
215 else:
216 return os.path.realpath(os.path.join(base_path, path))
217
218
219 def _find_and_load_module(fullname, modname, path): 233 def _find_and_load_module(fullname, modname, path):
220 imp.acquire_lock() 234 imp.acquire_lock()
221 try: 235 try:
222 if fullname not in sys.modules: 236 if fullname not in sys.modules:
223 fil = None 237 fil = None
224 try: 238 try:
225 fil, pathname, descr = imp.find_module(modname, 239 fil, pathname, descr = imp.find_module(modname,
226 [os.path.dirname(path)]) 240 [os.path.dirname(path)])
227 imp.load_module(fullname, fil, pathname, descr) 241 imp.load_module(fullname, fil, pathname, descr)
228 finally: 242 finally:
229 if fil: 243 if fil:
230 fil.close() 244 fil.close()
231 return sys.modules[fullname] 245 return sys.modules[fullname]
232 finally: 246 finally:
233 imp.release_lock() 247 imp.release_lock()
234 248
235 249
236 def _load_recipe_module_module(path, universe): 250 def _load_recipe_module_module(path, universe):
237 modname = os.path.splitext(os.path.basename(path))[0] 251 modname = os.path.splitext(os.path.basename(path))[0]
238 fullname = '%s.%s' % (RECIPE_MODULE_PREFIX, modname) 252 fullname = '%s.%s' % (RECIPE_MODULE_PREFIX, modname)
239 mod = _find_and_load_module(fullname, modname, path) 253 mod = _find_and_load_module(fullname, modname, path)
240 254
241 # This actually loads the dependencies. 255 # This actually loads the dependencies.
242 mod.LOADED_DEPS = universe.deps_from_mixed( 256 mod.LOADED_DEPS = universe.deps_from_spec(getattr(mod, 'DEPS', []))
243 getattr(mod, 'DEPS', []), os.path.basename(path))
244 257
245 # Prevent any modules that mess with sys.path from leaking. 258 # Prevent any modules that mess with sys.path from leaking.
246 with _preserve_path(): 259 with _preserve_path():
247 # TODO(luqui): Remove this hack once configs are cleaned. 260 # TODO(luqui): Remove this hack once configs are cleaned.
248 sys.modules['%s.DEPS' % fullname] = mod.LOADED_DEPS 261 sys.modules['%s.DEPS' % fullname] = mod.LOADED_DEPS
249 _recursive_import(path, RECIPE_MODULE_PREFIX) 262 _recursive_import(path, RECIPE_MODULE_PREFIX)
250 _patchup_module(modname, mod) 263 _patchup_module(modname, mod)
251 264
252 return mod 265 return mod
253 266
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
389 return modapi 402 return modapi
390 403
391 mapper = DependencyMapper(instantiator) 404 mapper = DependencyMapper(instantiator)
392 api = RecipeTestApi(module=None) 405 api = RecipeTestApi(module=None)
393 for k,v in toplevel_deps.iteritems(): 406 for k,v in toplevel_deps.iteritems():
394 setattr(api, k, mapper.instantiate(v)) 407 setattr(api, k, mapper.instantiate(v))
395 return api 408 return api
396 409
397 410
398 411
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698