update_layer.py: Save and show recipe dependencies
v2 cover update_layer.py: Save and show recipe dependencies
In v2, added the migration file to update the database.
The following changes since commit a64bfed81b3827503ff825090f1fb4b94e1cd9bd:
recipes.html: Require keyword for recipe search (2017-12-05 10:01:10 +1300)
are available in the git repository at:
git://git.yoctoproject.org/layerindex-web abrindle/dependencies
http://git.yoctoproject.org/cgit.cgi/layerindex-web/log/?h=abrindle/dependencies
Amanda Brindle (1):
update_layer.py: Save and show recipe dependencies
layerindex/admin.py | 20 ++++++++++- layerindex/migrations/0010_add_dependencies.py | 50 ++++++++++++++++++++++++++ layerindex/models.py | 24 +++++++++++++ layerindex/update_layer.py | 38 ++++++++++++++++++++ layerindex/views.py | 11 +++++- templates/layerindex/recipedetail.html | 33 +++++++++++++++++ 6 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 layerindex/migrations/0010_add_dependencies.py
Signature · 1 line
2.7.4
PATCH v2 1/1 update_layer.py: Save and show recipe dependencies
Added a model for the PACKAGECONFIG variable, which has a one to many relationship with the Recipe model.
Added models for static build dependencies and dynamic build dependenices, both of which have a many to many relationship with the Recipe model.
These objects are created in update_layer.py and are displayed on the Recipe detail page.
Added a depends search option for recipes, allowing users to search for recipes based on the recipe's build dependencies.
Fixes [YOCTO #12129]
Fixes [YOCTO #11415]
--- layerindex/admin.py | 20 ++++++++++- layerindex/migrations/0010_add_dependencies.py | 50 ++++++++++++++++++++++++++ layerindex/models.py | 24 +++++++++++++ layerindex/update_layer.py | 38 ++++++++++++++++++++ layerindex/views.py | 11 +++++- templates/layerindex/recipedetail.html | 33 +++++++++++++++++ 6 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 layerindex/migrations/0010_add_dependencies.py
layerindex/admin.py +19 -1
@@ -89,12 +89,27 @@ class LayerUpdateAdmin(admin.ModelAdmin): class RecipeAdmin(admin.ModelAdmin): search_fields = ['filename', 'pn'] list_filter = ['layerbranch__layer__name', 'layerbranch__branch__name'] - readonly_fields = [fieldname for fieldname in Recipe._meta.get_all_field_names() if fieldname not in ['recipefiledependency', 'classicrecipe']] + readonly_fields = [fieldname for fieldname in Recipe._meta.get_all_field_names() if fieldname not in ['recipefiledependency', 'classicrecipe', 'packageconfig']] def has_add_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False +class PackageConfigAdmin(admin.ModelAdmin): + search_fields = ['feature'] + list_display = ('feature',) + ordering = ('feature',) + +class StaticBuildDepAdmin(admin.ModelAdmin): + search_fields = ['name'] + list_display = ('name',) + filter_horizontal = ('recipes',) + +class DynamicBuildDepAdmin(admin.ModelAdmin): + search_fields = ['name'] + list_display = ('name',) + filter_horizontal = ('package_configs',) + class ClassicRecipeAdmin(admin.ModelAdmin): search_fields = ['filename', 'pn'] list_filter = ['layerbranch__layer__name', 'layerbranch__branch__name']
@@ -159,6 +174,9 @@ admin.site.register(LayerDependency, LayerDependencyAdmin) admin.site.register(LayerNote, LayerNoteAdmin) admin.site.register(Update, UpdateAdmin) admin.site.register(LayerUpdate, LayerUpdateAdmin) +admin.site.register(PackageConfig, PackageConfigAdmin) +admin.site.register(StaticBuildDep, StaticBuildDepAdmin) +admin.site.register(DynamicBuildDep, DynamicBuildDepAdmin) admin.site.register(Recipe, RecipeAdmin) admin.site.register(RecipeFileDependency) admin.site.register(Machine, MachineAdmin)
layerindex/migrations/0010_add_dependencies.py new file +50 -0
50 lines not on this page · raw message
layerindex/models.py +24 -0
@@ -357,6 +357,30 @@ class Recipe(models.Model): def __str__(self): return os.path.join(self.filepath, self.filename) +class PackageConfig(models.Model): + recipe = models.ForeignKey(Recipe) + feature = models.CharField(max_length=255) + with_option = models.CharField(max_length=255, blank=True) + without_option = models.CharField(max_length=255, blank=True) + build_deps = models.CharField(max_length=255, blank=True) + + def __str__(self): + return "%s - %s" % (self.recipe, self.feature) + +class StaticBuildDep(models.Model): + recipes = models.ManyToManyField(Recipe) + name = models.CharField(max_length=255) + + def __str__(self): + return self.name + +class DynamicBuildDep(models.Model): + package_configs = models.ManyToManyField(PackageConfig) + recipes = models.ManyToManyField(Recipe) + name = models.CharField(max_length=255) + + def __str__(self): + return self.name class RecipeFileDependency(models.Model): recipe = models.ForeignKey(Recipe)
layerindex/update_layer.py +38 -0
@@ -57,6 +57,7 @@ def split_recipe_fn(path): def update_recipe_file(tinfoil, data, path, recipe, layerdir_start, repodir): fn = str(os.path.join(path, recipe.filename)) + from layerindex.models import PackageConfig, StaticBuildDep, DynamicBuildDep try: logger.debug('Updating recipe %s' % fn) if hasattr(tinfoil, 'parse_recipe_file'):
@@ -81,6 +82,43 @@ def update_recipe_file(tinfoil, data, path, recipe, layerdir_start, repodir): recipe.blacklisted = envdata.getVarFlag('PNBLACKLIST', recipe.pn, True) or "" recipe.save() + # Handle static build dependencies for this recipe + static_dependencies = envdata.getVar("DEPENDS", True) or "" + for dep in static_dependencies.split(): + static_build_dependency = StaticBuildDep.objects.get_or_create(name=dep) + static_build_dependency[0].save() + static_build_dependency[0].recipes.add(recipe) + + # Handle the PACKAGECONFIG variables for this recipe + package_config_VarFlags = envdata.getVarFlags("PACKAGECONFIG") + for key, value in package_config_VarFlags.items(): + if key == "doc": + continue + package_config = PackageConfig() + package_config.feature = key + package_config.recipe = recipe + package_config_vals = value.split(",") + try: + package_config.build_deps = package_config_vals[2] + except IndexError: + pass + try: + package_config.with_option = package_config_vals[0] + except IndexError: + pass + try: + package_config.without_option = package_config_vals[1] + except IndexError: + pass + package_config.save() + # Handle the dynamic dependencies for the PACKAGECONFIG variable + if package_config.build_deps: + for dep in package_config.build_deps.split(): + dynamic_build_dependency = DynamicBuildDep.objects.get_or_create(name=dep) + dynamic_build_dependency[0].save() + dynamic_build_dependency[0].package_configs.add(package_config) + dynamic_build_dependency[0].recipes.add(recipe) + # Get file dependencies within this layer deps = envdata.getVar('__depends', True) filedeps = []
layerindex/views.py +10 -1
@@ -10,7 +10,7 @@ from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidde from django.core.urlresolvers import reverse, reverse_lazy, resolve from django.core.exceptions import PermissionDenied from django.template import RequestContext -from layerindex.models import Branch, LayerItem, LayerMaintainer, LayerBranch, LayerDependency, LayerNote, Update, LayerUpdate, Recipe, Machine, Distro, BBClass, BBAppend, RecipeChange, RecipeChangeset, ClassicRecipe +from layerindex.models import Branch, LayerItem, LayerMaintainer, LayerBranch, LayerDependency, LayerNote, Update, LayerUpdate, Recipe, Machine, Distro, BBClass, BBAppend, RecipeChange, RecipeChangeset, ClassicRecipe, StaticBuildDep, DynamicBuildDep from datetime import datetime from django.views.generic import TemplateView, DetailView, ListView from django.views.generic.edit import CreateView, DeleteView, UpdateView
@@ -428,6 +428,13 @@ class RecipeSearchView(ListView): for item in query_items: if item.startswith('inherits:'): inherits.append(item.split(':')[1]) + + # support searches by build dependencies + elif item.startswith('depends:'): + static_build_dependencies = StaticBuildDep.objects.filter(name=item.split(':')[1])[0] + dynamic_build_dependencies = DynamicBuildDep.objects.filter(name=item.split(':')[1])[0] + init_qs = init_qs.filter(Q(staticbuilddep=static_build_dependencies) | Q(dynamicbuilddep=dynamic_build_dependencies)).distinct() + # support searches by layer name elif item.startswith('layer:'): query_layername = item.split(':')[1].strip().lower()
@@ -845,6 +852,8 @@ class RecipeDetailView(DetailView): if append.matches_recipe(recipe): verappends.append(append) context['verappends'] = verappends + context['packageconfigs'] = recipe.packageconfig_set.order_by('feature') + context['staticdependencies'] = recipe.staticbuilddep_set.order_by('name') return context
templates/layerindex/recipedetail.html +33 -0
@@ -112,6 +112,39 @@ {% endif %} </td> </tr> + <tr> + <th>Dependencies</th> + <td> + {% if staticdependencies %} + <ul class="unstyled"> + {% for dep in staticdependencies %} + <li> {{dep.name}} </li> + {% endfor %} + </ul> + {% endif %} + {% if packageconfigs %} + <ul class="unstyled"> + {% for pc in packageconfigs %} + {% for dep in pc.dynamicbuilddep_set.all %} + <li> {{dep.name}}<a data-toggle="tooltip" title="PACKAGECONFIG[{{pc.feature}}] = {{pc.with_option}},{{pc.without_option}}" i class="icon-cog"> </a> </li> + {% endfor %} + {% endfor %} + </ul> + {% endif %} + </td> + </tr> + <tr> + <th>Package Configs</th> + <td> + {% if packageconfigs %} + <ul class="unstyled"> + {% for pc in packageconfigs %} + <li> PACKAGECONFIG[{{ pc.feature }}] = "{{pc.with_option}},{{pc.without_option}},{{pc.build_deps}}," </li> + {% endfor %} + </ul> + {% endif %} + </td> + </tr> </tbody> </table>
Signature · 1 line
2.7.4