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]

Signed-off-by: Amanda Brindle <amanda.r.brindle@intel.com>
This commit is contained in:
Amanda Brindle 2017-11-14 15:39:58 -08:00
parent a64bfed81b
commit 5817a0319e
6 changed files with 188 additions and 2 deletions

View File

@ -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)

View File

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('layerindex', '0009_layerbranch_collection'),
]
operations = [
migrations.CreateModel(
name='DynamicBuildDep',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=255)),
],
),
migrations.CreateModel(
name='PackageConfig',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('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)),
('recipe', models.ForeignKey(to='layerindex.Recipe')),
],
),
migrations.CreateModel(
name='StaticBuildDep',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('name', models.CharField(max_length=255)),
('recipes', models.ManyToManyField(to='layerindex.Recipe')),
],
),
migrations.AddField(
model_name='dynamicbuilddep',
name='package_configs',
field=models.ManyToManyField(to='layerindex.PackageConfig'),
),
migrations.AddField(
model_name='dynamicbuilddep',
name='recipes',
field=models.ManyToManyField(to='layerindex.Recipe'),
),
]

View File

@ -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)

View File

@ -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 = []

View File

@ -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,19 @@ 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:'):
depsearch = item.split(':')[1]
qobj = Q(pk__in=[])
static_build_dependencies = StaticBuildDep.objects.filter(name=depsearch).first()
dynamic_build_dependencies = DynamicBuildDep.objects.filter(name=depsearch).first()
if static_build_dependencies:
qobj |= Q(staticbuilddep=static_build_dependencies)
if dynamic_build_dependencies:
qobj |= Q(dynamicbuilddep=dynamic_build_dependencies)
init_qs = init_qs.filter(qobj).distinct()
# support searches by layer name
elif item.startswith('layer:'):
query_layername = item.split(':')[1].strip().lower()
@ -845,6 +858,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

View File

@ -112,6 +112,39 @@
{% endif %}
</td>
</tr>
<tr>
<th>Dependencies <i class="icon-exclamation-sign" data-toggle="tooltip" title="NOTE: Dependencies may vary based on configuration"></i></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}}<i class="icon-cog" data-toggle="tooltip" title="If &quot;{{pc.feature}}&quot; is set in PACKAGECONFIG"></i></li>
{% endfor %}
{% endfor %}
</ul>
{% endif %}
</td>
</tr>
<tr>
<th>PACKAGECONFIG options</th>
<td>
{% if packageconfigs %}
<ul class="unstyled">
{% for pc in packageconfigs %}
<li> {{ pc.feature }} </li>
{% endfor %}
</ul>
{% endif %}
</td>
</tr>
</tbody>
</table>
@ -149,3 +182,11 @@
{% endautoescape %}
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
});
</script>
{% endblock %}