| OLD | NEW |
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import collections | 5 import collections |
| 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 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 244 'chromiuminternal': 'build_internal/chromium', | 244 'chromiuminternal': 'build_internal/chromium', |
| 245 } | 245 } |
| 246 """ | 246 """ |
| 247 return { | 247 return { |
| 248 local_name: self.universe.load(pkg, name) | 248 local_name: self.universe.load(pkg, name) |
| 249 for local_name, (pkg, name) in self.normalize_deps_spec(spec).iteritems() | 249 for local_name, (pkg, name) in self.normalize_deps_spec(spec).iteritems() |
| 250 } | 250 } |
| 251 | 251 |
| 252 | 252 |
| 253 def find_recipe(self, recipe): | 253 def find_recipe(self, recipe): |
| 254 """Finds the given recipe. |
| 255 |
| 256 Args: |
| 257 recipe (str) - the name of the recipe as you would specify to |
| 258 `recipes.py run`. This may have the form: |
| 259 recipe, path/to/recipe - a recipe under the `recipes folder` |
| 260 module:tests/recipe - a test recipe under a recipe_module. |
| 261 module:examples/recipe - an example recipe under a recipe_module. |
| 262 module:example - a legacy example example recipe under a recipe_module |
| 263 |
| 264 Return (str) - The absolute path to the recipe .py file. |
| 265 |
| 266 Raises NoSuchRecipe if this cannot find the recipe. |
| 267 """ |
| 268 # If the recipe is specified as "module:recipe", then it is an recipe |
| 269 # contained in a recipe_module as an example, or a test. Look for it in the |
| 270 # modules imported by load_recipe_modules instead of the normal search |
| 271 # paths. |
| 272 # |
| 273 # TODO(martiniss) change "infra/example" to ["infra", "example"], |
| 274 # and handle appropriately, because of windows. |
| 254 if ':' in recipe: | 275 if ':' in recipe: |
| 255 module_name, recipe_name = recipe.split(':') | 276 module_name, recipe_name = recipe.split(':') |
| 256 module_dir = os.path.join(self.package.module_dir, module_name) | 277 module_dir = os.path.join(self.package.module_dir, module_name) |
| 257 if _is_recipe_module_dir(module_dir): | 278 if _is_recipe_module_dir(module_dir): |
| 258 if recipe_name.endswith('example'): | 279 # TODO(iannucci): remove legacy example. |
| 259 # TODO(martinis) change to example == 'example' ? Technically a bug... | 280 if (recipe_name == 'example' or |
| 260 recipe_path = os.path.join(module_dir, 'example.py') | 281 recipe_name.startswith(('tests/', 'examples/'))): |
| 261 elif recipe_name.startswith('tests/'): | 282 recipe_path = os.path.join(module_dir, recipe_name + '.py') |
| 262 recipe_path = os.path.join( | |
| 263 module_dir, 'tests', recipe_name[len('tests/'):] + '.py') | |
| 264 else: | 283 else: |
| 265 raise NoSuchRecipe(recipe) | 284 raise NoSuchRecipe(recipe) |
| 266 else: | 285 else: |
| 267 recipe_path = os.path.join(self.package.recipe_dir, recipe)+".py" | 286 recipe_path = os.path.join(self.package.recipe_dir, recipe)+".py" |
| 268 | 287 |
| 269 if os.path.exists(recipe_path): | 288 if os.path.exists(recipe_path): |
| 270 return recipe_path | 289 return recipe_path |
| 271 | 290 |
| 272 raise NoSuchRecipe(recipe) | 291 raise NoSuchRecipe(recipe) |
| 273 | 292 |
| 274 def load_recipe(self, recipe, engine=None): | 293 def load_recipe(self, recipe, engine=None): |
| 275 """Given name of a recipe, loads and returns it as RecipeScript instance. | 294 """Given name of a recipe, loads and returns it as RecipeScript instance. |
| 276 | 295 |
| 277 Args: | 296 Args: |
| 278 recipe (str): name of a recipe, can be in form '<module>:<recipe>'. | 297 recipe (str): name of a recipe, can be in form '<module>:<recipe>'. |
| 279 engine (RecipeEngine): If not None, resolve requirements with this recipe | 298 engine (RecipeEngine): If not None, resolve requirements with this recipe |
| 280 engine. Otherwise, requirements will be unresolved. | 299 engine. Otherwise, requirements will be unresolved. |
| 281 | 300 |
| 282 Returns: | 301 Returns: |
| 283 RecipeScript instance. | 302 RecipeScript instance. |
| 284 | 303 |
| 285 Raises: | 304 Raises: |
| 286 NoSuchRecipe: recipe is not found. | 305 NoSuchRecipe: recipe is not found. |
| 287 """ | 306 """ |
| 288 # If the recipe is specified as "module:recipe", then it is an recipe | |
| 289 # contained in a recipe_module as an example. Look for it in the modules | |
| 290 # imported by load_recipe_modules instead of the normal search paths. | |
| 291 # TODO(martiniss) change "infra/example" to ["infra", "example"], and handle | |
| 292 # appropriately, because of windows. | |
| 293 recipe_path = self.find_recipe(recipe) | 307 recipe_path = self.find_recipe(recipe) |
| 294 try: | 308 try: |
| 295 script = RecipeScript.from_script_path(recipe, recipe_path, self) | 309 script = RecipeScript.from_script_path(recipe, recipe_path, self) |
| 296 except (LoaderError,AssertionError,ImportError) as e: | 310 except (LoaderError,AssertionError,ImportError) as e: |
| 297 _amend_exception(e, 'while loading recipe %s' % recipe) | 311 _amend_exception(e, 'while loading recipe %s' % recipe) |
| 298 | 312 |
| 299 if engine is not None: | 313 if engine is not None: |
| 300 script.globals.update({k: _resolve_requirement(v, engine) | 314 script.globals.update({k: _resolve_requirement(v, engine) |
| 301 for k, v in script.globals.iteritems() | 315 for k, v in script.globals.iteritems() |
| 302 if isinstance(v, _UnresolvedRequirement)}) | 316 if isinstance(v, _UnresolvedRequirement)}) |
| (...skipping 27 matching lines...) Expand all Loading... |
| 330 yield file_path | 344 yield file_path |
| 331 | 345 |
| 332 path = self.package.recipe_dir | 346 path = self.package.recipe_dir |
| 333 for recipe in scan_directory( | 347 for recipe in scan_directory( |
| 334 path, lambda f: f.endswith('.py') and f[0] != '_'): | 348 path, lambda f: f.endswith('.py') and f[0] != '_'): |
| 335 yield recipe, recipe[len(path)+1:-len('.py')] | 349 yield recipe, recipe[len(path)+1:-len('.py')] |
| 336 | 350 |
| 337 for module_name in self.loop_over_recipe_modules(): | 351 for module_name in self.loop_over_recipe_modules(): |
| 338 module_dir = os.path.join(self.package.module_dir, module_name) | 352 module_dir = os.path.join(self.package.module_dir, module_name) |
| 339 | 353 |
| 354 # TODO(iannucci): remove unscoped example.py |
| 340 example_path = os.path.join(module_dir, 'example.py') | 355 example_path = os.path.join(module_dir, 'example.py') |
| 341 if os.path.exists(example_path): | 356 if os.path.exists(example_path): |
| 342 yield example_path, '%s:example' % module_name | 357 yield example_path, '%s:example' % module_name |
| 343 | 358 |
| 344 test_dir = os.path.join(module_dir, 'tests') | 359 for subdir_name in ('tests', 'examples'): |
| 345 if os.path.exists(test_dir): | 360 subdir = os.path.join(module_dir, subdir_name) |
| 346 for recipe in scan_directory(test_dir, lambda f: f.endswith('.py')): | 361 if os.path.exists(subdir): |
| 347 yield recipe, '%s:tests/%s' % ( | 362 for recipe in scan_directory(subdir, lambda f: f.endswith('.py')): |
| 348 module_name, recipe[len(test_dir)+1:-len('.py')]) | 363 yield recipe, '%s:%s/%s' % ( |
| 364 module_name, subdir_name, recipe[len(subdir)+1:-len('.py')]) |
| 349 | 365 |
| 350 def loop_over_recipe_modules(self): | 366 def loop_over_recipe_modules(self): |
| 351 """Yields the paths to all the modules that this view can see.""" | 367 """Yields the paths to all the modules that this view can see.""" |
| 352 path = self.package.module_dir | 368 path = self.package.module_dir |
| 353 if os.path.isdir(path): | 369 if os.path.isdir(path): |
| 354 for item in os.listdir(path): | 370 for item in os.listdir(path): |
| 355 subpath = os.path.join(path, item) | 371 subpath = os.path.join(path, item) |
| 356 if _is_recipe_module_dir(subpath): | 372 if _is_recipe_module_dir(subpath): |
| 357 yield os.path.basename(subpath) | 373 yield os.path.basename(subpath) |
| 358 | 374 |
| (...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 for k,v in toplevel_deps.iteritems(): | 713 for k,v in toplevel_deps.iteritems(): |
| 698 setattr(api, k, mapper.instantiate(v)) | 714 setattr(api, k, mapper.instantiate(v)) |
| 699 return api | 715 return api |
| 700 | 716 |
| 701 | 717 |
| 702 def _resolve_requirement(req, engine): | 718 def _resolve_requirement(req, engine): |
| 703 if req._typ == 'client': | 719 if req._typ == 'client': |
| 704 return engine._get_client(req._name) | 720 return engine._get_client(req._name) |
| 705 else: | 721 else: |
| 706 raise ValueError('Unknown requirement type [%s]' % (req._typ,)) | 722 raise ValueError('Unknown requirement type [%s]' % (req._typ,)) |
| OLD | NEW |