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

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: 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 os.path.exists(os.path.join(mod_path, '__init__.py')): 89 if os.path.exists(os.path.join(mod_path, '__init__.py')):
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 22 matching lines...) Expand all
197 218
198 @contextlib.contextmanager 219 @contextlib.contextmanager
199 def _preserve_path(): 220 def _preserve_path():
200 old_path = sys.path[:] 221 old_path = sys.path[:]
201 try: 222 try:
202 yield 223 yield
203 finally: 224 finally:
204 sys.path = old_path 225 sys.path = old_path
205 226
206 227
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))
212
213
214 def _find_and_load_module(fullname, modname, path): 228 def _find_and_load_module(fullname, modname, path):
215 imp.acquire_lock() 229 imp.acquire_lock()
216 try: 230 try:
217 if fullname not in sys.modules: 231 if fullname not in sys.modules:
218 fil = None 232 fil = None
219 try: 233 try:
220 fil, pathname, descr = imp.find_module(modname, 234 fil, pathname, descr = imp.find_module(modname,
221 [os.path.dirname(path)]) 235 [os.path.dirname(path)])
222 imp.load_module(fullname, fil, pathname, descr) 236 imp.load_module(fullname, fil, pathname, descr)
223 finally: 237 finally:
224 if fil: 238 if fil:
225 fil.close() 239 fil.close()
226 return sys.modules[fullname] 240 return sys.modules[fullname]
227 finally: 241 finally:
228 imp.release_lock() 242 imp.release_lock()
229 243
230 244
231 def _load_recipe_module_module(path, universe): 245 def _load_recipe_module_module(path, universe):
232 modname = os.path.splitext(os.path.basename(path))[0] 246 modname = os.path.splitext(os.path.basename(path))[0]
233 fullname = '%s.%s' % (RECIPE_MODULE_PREFIX, modname) 247 fullname = '%s.%s' % (RECIPE_MODULE_PREFIX, modname)
234 mod = _find_and_load_module(fullname, modname, path) 248 mod = _find_and_load_module(fullname, modname, path)
235 249
236 # This actually loads the dependencies. 250 # This actually loads the dependencies.
237 mod.LOADED_DEPS = universe.deps_from_mixed( 251 mod.LOADED_DEPS = universe.deps_from_spec(getattr(mod, 'DEPS', []))
238 getattr(mod, 'DEPS', []), os.path.basename(path))
239 252
240 # Prevent any modules that mess with sys.path from leaking. 253 # Prevent any modules that mess with sys.path from leaking.
241 with _preserve_path(): 254 with _preserve_path():
242 # TODO(luqui): Remove this hack once configs are cleaned. 255 # TODO(luqui): Remove this hack once configs are cleaned.
243 sys.modules['%s.DEPS' % fullname] = mod.LOADED_DEPS 256 sys.modules['%s.DEPS' % fullname] = mod.LOADED_DEPS
244 _recursive_import(path, RECIPE_MODULE_PREFIX) 257 _recursive_import(path, RECIPE_MODULE_PREFIX)
245 _patchup_module(modname, mod) 258 _patchup_module(modname, mod)
246 259
247 return mod 260 return mod
248 261
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
384 return modapi 397 return modapi
385 398
386 mapper = DependencyMapper(instantiator) 399 mapper = DependencyMapper(instantiator)
387 api = RecipeTestApi(module=None) 400 api = RecipeTestApi(module=None)
388 for k,v in toplevel_deps.iteritems(): 401 for k,v in toplevel_deps.iteritems():
389 setattr(api, k, mapper.instantiate(v)) 402 setattr(api, k, mapper.instantiate(v))
390 return api 403 return api
391 404
392 405
393 406
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698