[Toaster] [PATCH 2/3] toaster: add modal to select custom image for editing

Smith, Elliot elliot.smith at intel.com
Thu Apr 14 00:27:39 PDT 2016


Thanks Dave, nice spot. I will fix it in the next version I submit for
review.

Elliot

On 13 April 2016 at 18:11, Lerner, David M (Wind River) <
dave.lerner at windriver.com> wrote:

> Hi Elliot,
> While reviewing open RRs, I noticed that you transposed the yocto number.
> It should be #9123.
> Dave
>
> > -----Original Message-----
> > From: toaster-bounces at yoctoproject.org [mailto:
> toaster-bounces at yoctoproject.org] On
> > Behalf Of Elliot Smith
> > Sent: Monday, April 11, 2016 9:56 AM
> > To: toaster at yoctoproject.org
> > Subject: [Toaster] [PATCH 2/3] toaster: add modal to select custom image
> for editing
> >
> > Add functionality to the placeholder button on the build dashboard
> > to open a modal dialog displaying editable custom images, in cases
> > where multiple custom images were built by the build. Where there
> > is only one editable custom image, go direct to its edit page.
> >
> > The images shown in the modal are custom recipes for the project
> > which were built during the build shown in the dashboard.
> >
> > This also affects the new custom image dialog, as that also has
> > to show custom image recipes as well as image recipes built during
> > the build. Modify the API on the Build object to support both.
> >
> > Also modify and rename the queryset_to_list template filter so that
> > it can deal with lists as well as querysets, as the new custom image
> > modal has to show a list of image recipes which is an amalgam of two
> > querysets.
> >
> > [YOCTO #9213]
>
> Bug 9213 - Enable thumb for ARM builds
> Bug 9123 - Build history pages are missing the image customisation links
>
> >
> > Signed-off-by: Elliot Smith <elliot.smith at intel.com>
> > ---
> >  bitbake/lib/toaster/orm/models.py                  | 45 ++++++++------
> >  .../lib/toaster/toastergui/static/js/libtoaster.js |  2 +
> >  .../toastergui/static/js/newcustomimage_modal.js   |  7 ++-
> >  bitbake/lib/toaster/toastergui/templates/base.html |  1 -
> >  .../toastergui/templates/basebuildpage.html        | 62
> +++++++++++---------
> >  .../templates/editcustomimage_modal.html           | 68
> ++++++++++++++++++----
> >  .../templatetags/objects_to_dictionaries_filter.py | 35 +++++++++++
> >  .../templatetags/queryset_to_list_filter.py        | 26 ---------
> >  bitbake/lib/toaster/toastergui/views.py            | 26 +++++++--
> >  9 files changed, 182 insertions(+), 90 deletions(-)
> >  create mode 100644
> >
> bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py
> >  delete mode 100644
> > bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py
> >
> > diff --git a/bitbake/lib/toaster/orm/models.py
> b/bitbake/lib/toaster/orm/models.py
> > index c63d631..a146541 100644
> > --- a/bitbake/lib/toaster/orm/models.py
> > +++ b/bitbake/lib/toaster/orm/models.py
> > @@ -497,33 +497,37 @@ class Build(models.Model):
> >          return Recipe.objects.filter(criteria) \
> >                               .select_related('layer_version',
> 'layer_version__layer')
> >
> > -    def get_custom_image_recipe_names(self):
> > -        """
> > -        Get the names of custom image recipes for this build's project
> > -        as a list; this is used to screen out custom image recipes from
> the
> > -        recipes for the build by name, and to distinguish image recipes
> from
> > -        custom image recipes
> > -        """
> > -        custom_image_recipes = \
> > -            CustomImageRecipe.objects.filter(project=self.project)
> > -        return custom_image_recipes.values_list('name', flat=True)
> > -
> >      def get_image_recipes(self):
> >          """
> > -        Returns a queryset of image recipes related to this build,
> sorted
> > -        by name
> > +        Returns a list of image Recipes (custom and built-in) related
> to this
> > +        build, sorted by name; note that this has to be done in two
> steps, as
> > +        there's no way to get all the custom image recipes and image
> recipes
> > +        in one query
> >          """
> > -        criteria = Q(is_image=True)
> > -        return self.get_recipes().filter(criteria).order_by('name')
> > +        custom_image_recipes = self.get_custom_image_recipes()
> > +        custom_image_recipe_names =
> custom_image_recipes.values_list('name', flat=True)
> > +
> > +        not_custom_image_recipes =
> ~Q(name__in=custom_image_recipe_names) & \
> > +                                   Q(is_image=True)
> > +
> > +        built_image_recipes =
> self.get_recipes().filter(not_custom_image_recipes)
> > +
> > +        # append to the custom image recipes and sort
> > +        customisable_image_recipes = list(
> > +            itertools.chain(custom_image_recipes, built_image_recipes)
> > +        )
> > +
> > +        return sorted(customisable_image_recipes, key=lambda recipe:
> recipe.name)
> >
> >      def get_custom_image_recipes(self):
> >          """
> > -        Returns a queryset of custom image recipes related to this
> build,
> > +        Returns a queryset of CustomImageRecipes related to this build,
> >          sorted by name
> >          """
> > -        custom_image_recipe_names = self.get_custom_image_recipe_names()
> > -        criteria = Q(is_image=True) &
> Q(name__in=custom_image_recipe_names)
> > -        return self.get_recipes().filter(criteria).order_by('name')
> > +        built_recipe_names = self.get_recipes().values_list('name',
> flat=True)
> > +        criteria = Q(name__in=built_recipe_names) &
> Q(project=self.project)
> > +        queryset =
> CustomImageRecipe.objects.filter(criteria).order_by('name')
> > +        return queryset
> >
> >      def get_outcome_text(self):
> >          return Build.BUILD_OUTCOME[int(self.outcome)][1]
> > @@ -1374,6 +1378,9 @@ class Layer(models.Model):
> >
> >  # LayerCommit class is synced with layerindex.LayerBranch
> >  class Layer_Version(models.Model):
> > +    """
> > +    A Layer_Version either belongs to a single project or no project
> > +    """
> >      search_allowed_fields = ["layer__name", "layer__summary",
> "layer__description",
> > "layer__vcs_url", "dirpath", "up_branch__name", "commit", "branch"]
> >      build = models.ForeignKey(Build,
> related_name='layer_version_build', default =
> > None, null = True)
> >      layer = models.ForeignKey(Layer, related_name='layer_version_layer')
> > diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
> > b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
> > index 8d1d20f..88caaff 100644
> > --- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
> > +++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
> > @@ -344,6 +344,8 @@ var libtoaster = (function (){
> >    }
> >
> >    function _createCustomRecipe(name, baseRecipeId, doneCb){
> > +    debugger;
> > +
> >      var data = {
> >        'name' : name,
> >        'project' : libtoaster.ctx.projectId,
> > diff --git
> a/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js
> > b/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js
> > index 1ae0d34..a6d5b1a 100644
> > --- a/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js
> > +++ b/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js
> > @@ -12,6 +12,7 @@ for the new custom image. This will manage the
> addition of radio
> > buttons
> >  to select the base image (or remove the radio buttons, if there is only
> a
> >  single base image available).
> >  */
> > +
> >  function newCustomImageModalInit(){
> >
> >    var newCustomImgBtn = $("#create-new-custom-image-btn");
> > @@ -112,13 +113,13 @@ function
> newCustomImageModalSetRecipes(baseRecipes) {
> >    var imageSelector = $('#new-custom-image-modal
> [data-role="image-selector"]');
> >    var imageSelectRadiosContainer = $('#new-custom-image-modal
> [data-role="image-
> > selector-radios"]');
> >
> > +  // remove any existing radio buttons + labels
> > +  imageSelector.remove('[data-role="image-radio"]');
> > +
> >    if (baseRecipes.length === 1) {
> >      // hide the radio button container
> >      imageSelector.hide();
> >
> > -    // remove any radio buttons + labels
> > -    imageSelector.remove('[data-role="image-radio"]');
> > -
> >      // set the single recipe ID on the modal as it's the only one
> >      // we can build from
> >      imgCustomModal.data('recipe', baseRecipes[0].id);
> > diff --git a/bitbake/lib/toaster/toastergui/templates/base.html
> > b/bitbake/lib/toaster/toastergui/templates/base.html
> > index 192f9fb..210cf33 100644
> > --- a/bitbake/lib/toaster/toastergui/templates/base.html
> > +++ b/bitbake/lib/toaster/toastergui/templates/base.html
> > @@ -43,7 +43,6 @@
> >          recipesTypeAheadUrl: {% url 'xhr_recipestypeahead' project.id
> as
> > paturl%}{{paturl|json}},
> >          layersTypeAheadUrl: {% url 'xhr_layerstypeahead' project.id as
> > paturl%}{{paturl|json}},
> >          machinesTypeAheadUrl: {% url 'xhr_machinestypeahead' project.id
> as
> > paturl%}{{paturl|json}},
> > -
> >          projectBuildsUrl: {% url 'projectbuilds' project.id as pburl
> %}{{pburl|json}},
> >          xhrCustomRecipeUrl : "{% url 'xhr_customrecipe' %}",
> >          projectId : {{project.id}},
> > diff --git a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
> > b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
> > index 4a8e2a7..0d8c882 100644
> > --- a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
> > +++ b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
> > @@ -1,7 +1,7 @@
> >  {% extends "base.html" %}
> >  {% load projecttags %}
> >  {% load project_url_tag %}
> > -{% load queryset_to_list_filter %}
> > +{% load objects_to_dictionaries_filter %}
> >  {% load humanize %}
> >  {% block pagecontent %}
> >    <!-- breadcrumbs -->
> > @@ -81,33 +81,40 @@
> >            </p>
> >          </li>
> >
> > -        <li>
> > -          <!-- edit custom image built during this build -->
> > -          <p class="navbar-btn" data-role="edit-custom-image-trigger">
> > -            <button class="btn btn-block">Edit custom image</button>
> > -          </p>
> > -          {% include 'editcustomimage_modal.html' %}
> > -          <script>
> > -            $(document).ready(function () {
> > -              var editableCustomImageRecipes = {{
> build.get_custom_image_recipes |
> > queryset_to_list:"id,name" | json }};
> > -
> > -              // edit custom image which was built during this build
> > -              var editCustomImageModal = $('#edit-custom-image-modal');
> > -              var editCustomImageTrigger =
> $('[data-role="edit-custom-image-
> > trigger"]');
> > +        {% with build.get_custom_image_recipes as custom_image_recipes
> %}
> > +          {% if custom_image_recipes.count > 0 %}
> > +            <!-- edit custom image built during this build -->
> > +            <li>
> > +              <p class="navbar-btn"
> data-role="edit-custom-image-trigger">
> > +                <button class="btn btn-block">Edit custom image</button>
> > +                {% include 'editcustomimage_modal.html' %}
> > +                <script>
> > +                  var editableCustomImageRecipes = {{
> custom_image_recipes |
> > objects_to_dictionaries:"id,name" | json }};
> >
> > -              editCustomImageTrigger.click(function () {
> > -                // if there is a single editable custom image, go
> direct to the edit
> > -                // page for it; if there are multiple editable custom
> images, show
> > -                // dialog to select one of them for editing
> > +                  $(document).ready(function () {
> > +                    var editCustomImageTrigger =
> $('[data-role="edit-custom-image-
> > trigger"]');
> > +                    var editCustomImageModal =
> $('#edit-custom-image-modal');
> >
> > -                // single editable custom image
> > -
> > -                // multiple editable custom images
> > -                editCustomImageModal.modal('show');
> > -              });
> > -            });
> > -          </script>
> > -        </li>
> > +                    // edit custom image which was built during this
> build
> > +                    editCustomImageTrigger.click(function () {
> > +                      // single editable custom image: redirect to the
> edit page
> > +                      // for that image
> > +                      if (editableCustomImageRecipes.length === 1) {
> > +                        var url = '{% url "customrecipe"
> build.project.id
> > custom_image_recipes.first.id %}';
> > +                        document.location.href = url;
> > +                      }
> > +                      // multiple editable custom images: show modal to
> select
> > +                      // one of them for editing
> > +                      else {
> > +                        editCustomImageModal.modal('show');
> > +                      }
> > +                    });
> > +                  });
> > +                </script>
> > +              </p>
> > +            </li>
> > +          {% endif %}
> > +        {% endwith %}
> >
> >          <li>
> >            <!-- new custom image from image recipe in this build -->
> > @@ -119,7 +126,7 @@
> >              // imageRecipes includes both custom image recipes and
> built-in
> >              // image recipes, any of which can be used as the basis for
> a
> >              // new custom image
> > -            var imageRecipes = {{ build.get_image_recipes |
> queryset_to_list:"id,name"
> > | json }};
> > +            var imageRecipes = {{ build.get_image_recipes |
> > objects_to_dictionaries:"id,name" | json }};
> >
> >              $(document).ready(function () {
> >                var newCustomImageModal = $('#new-custom-image-modal');
> > @@ -131,6 +138,7 @@
> >                  if (!imageRecipes.length) {
> >                    return;
> >                  }
> > +
> >                  newCustomImageModalSetRecipes(imageRecipes);
> >                  newCustomImageModal.modal('show');
> >                });
> > diff --git
> a/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html
> > b/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html
> > index fd998f6..8046c08 100644
> > --- a/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html
> > +++ b/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html
> > @@ -1,23 +1,71 @@
> >  <!--
> > -modal dialog shown on the build dashboard, for editing an existing
> custom image
> > +modal dialog shown on the build dashboard, for editing an existing
> custom image;
> > +only shown if more than one custom image was built, so the user needs to
> > +choose which one to edit
> > +
> > +required context:
> > +  build - a Build object
> >  -->
> >  <div class="modal hide fade in" aria-hidden="false"
> id="edit-custom-image-modal">
> >    <div class="modal-header">
> >      <button type="button" class="close" data-dismiss="modal" aria-
> > hidden="true">×</button>
> > -    <h3>Select custom image to edit</h3>
> > +    <h3>Which image do you want to edit?</h3>
> >    </div>
> > +
> >    <div class="modal-body">
> >      <div class="row-fluid">
> > -      <span class="help-block">
> > -        Explanation of what this modal is for
> > -      </span>
> > -    </div>
> > -    <div class="control-group controls">
> > -      <input type="text" class="huge" placeholder="input box" required>
> > -      <span class="help-block error" style="display:none">Error
> text</span>
> > +      {% for recipe in build.get_custom_image_recipes %}
> > +        <label class="radio">
> > +          {{recipe.name}}
> > +          <input type="radio" class="form-control"
> name="select-custom-image"
> > +                 data-url="{% url 'customrecipe' build.project.id
> recipe.id %}">
> > +        </label>
> > +      {% endfor %}
> >      </div>
> > +    <span class="help-block error" id="invalid-custom-image-help"
> style="display:none">
> > +      Please select a custom image to edit.
> > +    </span>
> >    </div>
> > +
> >    <div class="modal-footer">
> > -    <button class="btn btn-primary btn-large" disabled>Action</button>
> > +    <button class="btn btn-primary btn-large" data-url="#"
> > +       data-action="edit-custom-image" disabled>
> > +      Edit custom image
> > +    </button>
> >    </div>
> >  </div>
> > +
> > +<script>
> > +$(document).ready(function () {
> > +  var editCustomImageButton = $('[data-action="edit-custom-image"]');
> > +  var error = $('#invalid-custom-image-help');
> > +  var radios = $('[name="select-custom-image"]');
> > +
> > +  // return custom image radio buttons which are selected
> > +  var getSelectedRadios = function () {
> > +    return $('[name="select-custom-image"]:checked');
> > +  };
> > +
> > +  radios.change(function () {
> > +    if (getSelectedRadios().length === 1) {
> > +      editCustomImageButton.removeAttr('disabled');
> > +      error.hide();
> > +    }
> > +    else {
> > +      editCustomImageButton.attr('disabled', 'disabled');
> > +      error.show();
> > +    }
> > +  });
> > +
> > +  editCustomImageButton.click(function () {
> > +    var selectedRadios = getSelectedRadios();
> > +
> > +    if (selectedRadios.length === 1) {
> > +      document.location.href = selectedRadios.first().attr('data-url');
> > +    }
> > +    else {
> > +      error.show();
> > +    }
> > +  });
> > +});
> > +</script>
> > diff --git
> >
> a/bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py
> >
> b/bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py
> > new file mode 100644
> > index 0000000..0dcc7d2
> > --- /dev/null
> > +++
> b/bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py
> > @@ -0,0 +1,35 @@
> > +from django import template
> > +import json
> > +
> > +register = template.Library()
> > +
> > +def objects_to_dictionaries(iterable, fields):
> > +    """
> > +    Convert an iterable into a list of dictionaries; fields should be
> set
> > +    to a comma-separated string of properties for each item included in
> the
> > +    resulting list; e.g. for a queryset:
> > +
> > +        {{ queryset | objects_to_dictionaries:"id,name" }}
> > +
> > +    will return a list like
> > +
> > +        [{'id': 1, 'name': 'foo'}, ...]
> > +
> > +    providing queryset has id and name fields
> > +
> > +    This is mostly to support serialising querysets or lists of model
> objects
> > +    to JSON
> > +    """
> > +    objects = []
> > +
> > +    if fields:
> > +        fields_list = [field.strip() for field in fields.split(',')]
> > +        for item in iterable:
> > +            out = {}
> > +            for field in fields_list:
> > +                out[field] = getattr(item, field)
> > +            objects.append(out)
> > +
> > +    return objects
> > +
> > +register.filter('objects_to_dictionaries', objects_to_dictionaries)
> > diff --git
> a/bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py
> > b/bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py
> > deleted file mode 100644
> > index dfc094b..0000000
> > ---
> a/bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py
> > +++ /dev/null
> > @@ -1,26 +0,0 @@
> > -from django import template
> > -import json
> > -
> > -register = template.Library()
> > -
> > -def queryset_to_list(queryset, fields):
> > -    """
> > -    Convert a queryset to a list; fields can be set to a comma-separated
> > -    string of fields for each record included in the resulting list; if
> > -    omitted, all fields are included for each record, e.g.
> > -
> > -        {{ queryset | queryset_to_list:"id,name" }}
> > -
> > -    will return a list like
> > -
> > -        [{'id': 1, 'name': 'foo'}, ...]
> > -
> > -    (providing queryset has id and name fields)
> > -    """
> > -    if fields:
> > -        fields_list = [field.strip() for field in fields.split(',')]
> > -        return list(queryset.values(*fields_list))
> > -    else:
> > -        return list(queryset.values())
> > -
> > -register.filter('queryset_to_list', queryset_to_list)
> > diff --git a/bitbake/lib/toaster/toastergui/views.py
> > b/bitbake/lib/toaster/toastergui/views.py
> > index 60edb45..1f824ee 100755
> > --- a/bitbake/lib/toaster/toastergui/views.py
> > +++ b/bitbake/lib/toaster/toastergui/views.py
> > @@ -507,6 +507,7 @@ def builddashboard( request, build_id ):
> >
> >      context = {
> >              'build'           : build,
> > +            'project'         : build.project,
> >              'hasImages'       : hasImages,
> >              'ntargets'        : ntargets,
> >              'targets'         : targets,
> > @@ -797,6 +798,7 @@ eans multiple licenses exist that cover different
> parts of the
> > source',
> >      context = {
> >          'objectname': variant,
> >          'build'                : build,
> > +        'project'              : build.project,
> >          'target'               : Target.objects.filter( pk = target_id
> )[ 0 ],
> >          'objects'              : packages,
> >          'packages_sum'         : packages_sum[ 'installed_size__sum' ],
> > @@ -937,7 +939,10 @@ def dirinfo(request, build_id, target_id,
> file_path=None):
> >              if head != sep:
> >                  dir_list.insert(0, head)
> >
> > -    context = { 'build': Build.objects.get(pk=build_id),
> > +    build = Build.objects.get(pk=build_id)
> > +
> > +    context = { 'build': build,
> > +                'project': build.project,
> >                  'target': Target.objects.get(pk=target_id),
> >                  'packages_sum': packages_sum['installed_size__sum'],
> >                  'objects': objects,
> > @@ -1211,6 +1216,7 @@ def tasks_common(request, build_id, variant,
> task_anchor):
> >                  'filter_search_display': filter_search_display,
> >                  'mainheading': title_variant,
> >                  'build': build,
> > +                'project': build.project,
> >                  'objects': task_objects,
> >                  'default_orderby' : orderby,
> >                  'search_term': search_term,
> > @@ -1282,6 +1288,7 @@ def recipes(request, build_id):
> >      context = {
> >          'objectname': 'recipes',
> >          'build': build,
> > +        'project': build.project,
> >          'objects': recipes,
> >          'default_orderby' : 'name:+',
> >          'recipe_deps' : deps,
> > @@ -1366,10 +1373,12 @@ def configuration(request, build_id):
> >                   'MACHINE', 'DISTRO', 'DISTRO_VERSION',
> 'TUNE_FEATURES', 'TARGET_FPU')
> >      context = dict(Variable.objects.filter(build=build_id,
> > variable_name__in=var_names)\
> >                                             .values_list('variable_name',
> > 'variable_value'))
> > +    build = Build.objects.get(pk=build_id)
> >      context.update({'objectname': 'configuration',
> >                      'object_search_display':'variables',
> >                      'filter_search_display':'variables',
> > -                    'build': Build.objects.get(pk=build_id),
> > +                    'build': build,
> > +                    'project': build.project,
> >                      'targets': Target.objects.filter(build=build_id)})
> >      return render(request, template, context)
> >
> > @@ -1406,12 +1415,15 @@ def configvars(request, build_id):
> >          file_filter += '/bitbake.conf'
> >
> build_dir=re.sub("/tmp/log/.*","",Build.objects.get(pk=build_id).cooker_log_path)
> >
> > +    build = Build.objects.get(pk=build_id)
> > +
> >      context = {
> >                  'objectname': 'configvars',
> >                  'object_search_display':'BitBake variables',
> >                  'filter_search_display':'variables',
> >                  'file_filter': file_filter,
> > -                'build': Build.objects.get(pk=build_id),
> > +                'build': build,
> > +                'project': build.project,
> >                  'objects' : variables,
> >                  'total_count':queryset_with_search.count(),
> >                  'default_orderby' : 'variable_name:+',
> > @@ -1480,6 +1492,7 @@ def bpackage(request, build_id):
> >      context = {
> >          'objectname': 'packages built',
> >          'build': build,
> > +        'project': build.project,
> >          'objects' : packages,
> >          'default_orderby' : 'name:+',
> >          'tablecols':[
> > @@ -1554,7 +1567,12 @@ def bpackage(request, build_id):
> >  def bfile(request, build_id, package_id):
> >      template = 'bfile.html'
> >      files = Package_File.objects.filter(package = package_id)
> > -    context = {'build': Build.objects.get(pk=build_id), 'objects' :
> files}
> > +    build = Build.objects.get(pk=build_id)
> > +    context = {
> > +        'build': build,
> > +        'project': build.project,
> > +        'objects' : files
> > +    }
> >      return render(request, template, context)
> >
> >
> > --
> > 1.9.3
> >
> > ---------------------------------------------------------------------
> > Intel Corporation (UK) Limited
> > Registered No. 1134945 (England)
> > Registered Office: Pipers Way, Swindon SN3 1RJ
> > VAT No: 860 2173 47
> >
> > This e-mail and any attachments may contain confidential material for
> > the sole use of the intended recipient(s). Any review or distribution
> > by others is strictly prohibited. If you are not the intended
> > recipient, please contact the sender and delete all copies.
> > --
> > _______________________________________________
> > toaster mailing list
> > toaster at yoctoproject.org
> > https://lists.yoctoproject.org/listinfo/toaster
>



-- 
Elliot Smith
Software Engineer
Intel Open Source Technology Centre
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.yoctoproject.org/pipermail/toaster/attachments/20160414/abfc7b85/attachment-0001.html>


More information about the toaster mailing list