Commit Graph

43 Commits

Author SHA1 Message Date
Paul Eggleton
2c3c287a33 Fix errors due to races deleting bitbake temp files
Errors deleting bitbake.sock and bitbake.lock have been observed when
shutting down tinfoil at the end of some of these scripts. Move the code
used in the main layer index update script to a function in utils.py and
use it everywhere in order to avoid the issue.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2019-02-14 10:05:53 +13:00
Paul Eggleton
e591d1820a Track and enable reporting on duplicate inc files
It's not too common but there are instances where people have copied
.inc files into their own layer and modified them, and if you are using
such a layer that could result in unexpected behaviour. In order to get
a handle on when this is being done, collect data about all .inc files
and show duplicates in the Duplicates screen.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-11-06 10:33:39 +13:00
Paul Eggleton
8bb62fb82d update: allow patch parsing to work with python 2
If we want to be able to read in patch information on python2-based
branches (e.g. fido) then we need to use codecs.open() instead of open()
here since python2's open() did not support the encoding parameter.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-10-01 16:20:42 +13:00
Paul Eggleton
87594bdd92 update: fix confusing debug message on renaming bbappend
Print the full path here otherwise it looks like we renamed a file to a
directory.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-10-01 15:42:59 +13:00
Paul Eggleton
d328e392d2 update: fix error on LAYERRECOMMENDS failure
The code in recipeparse.setup_layer() was trying to log a warning in the
case where LAYERRECOMMENDS not being satisfied, however there is no
actual logger object in this context. Pass it in via a parameter and
update all callers to pass it.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-10-01 11:43:52 +13:00
Paul Eggleton
2554449baa update: fix regression caused by previous temp dir fix
In c26604146a I made a fix to change where
the bitbake code writes out bitbake.lock and other files it creates
during parsing, but didn't adequately test it and it turns out our
call to delete the temp directory races against bitbake deleting
bitbake.lock and bitbake.sock. For now the simplest way to deal with
this is to ignore the errors since we don't care about these files,
we just want the temp dir gone.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-09-27 15:35:38 +12:00
Paul Eggleton
f82f5f2754 update: add option to stop immediately on parse errors
During debugging a parsing issue, we don't really want to continue if a
parsing error occurs, and in that situation I usually end up using
Ctrl+C to exit early. Add an option to exit immediately upon error to
avoid having to do that.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-08-24 16:41:59 +12:00
Paul Eggleton
74ea84f696 update_layer: improve exception handling
When an exception occurs during the main part of update_layer, we were
catching and printing it but that's not enough - we need to do the
following as well:

* Use logger.error() to print the exception information, so that it gets
  logged and highlighted as an error in the layer update
* Exit with a non-zero return code so that update.py knows it has failed

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-08-14 15:07:34 +02:00
Paul Eggleton
3fa8a75a2f update: fix handling of moves outside of a layer
If a file is moved (renamed) to a path outside of the layer, e.g.
another layer within a multi-layer repository, then we need to treat it
as a delete. Up until now we were updating the path and continuing, and
then the recipe was also picked up as an add in the other layer, leading
to duplicate recipe entries. I'd noticed these duplicates before but up
until now I'd thought that they were due to another bug we already
fixed, apparently not.

In order to remove these erroneous duplicate entries in existing
databases I have also added a layerindex/tools/fixup_duplicates.py
script. I've also made the -r/--reload option delete them as well.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-07-19 15:12:45 +02:00
Paul Eggleton
7effcc8cfb update_layer: avoid errors on modified & renamed files
If a file is modified and renamed it will show up in both
iter_change_type('M') and iter_change_type('R'), however naturally the
file that will exist will be the b path and not the a one, so we should
be looking at the b path or we will get errors.

FYI you can reproduce this with OE-Core (in a scratch database) using
the following procedure:
1) (in the OE-Core layer directory):
   git checkout 59285b324f6d9ed270b0bef209ef5da22a620a83
2) update.py -l openembedded-core -b master -x --nofetch -r --fullreload
3) (in the OE-Core layer directory):
   git checkout 086308aa2a5e332de6f00ed397c4a55d132f158f
4) update.py -l openembedded-core -b master -x --nofetch

Without this change you'll see the following error:

ERROR: Unable to read /opt/layerindex/layers/git___git_openembedded_org_openembedded-core/meta/recipes-devtools/python-numpy/python-numpy_1.13.1.bb: Traceback (most recent call last):
  File "/opt/layerindex/layers/bitbake/lib/bb/command.py", line 84, in runCommand
    result = command_method(self, commandline)
  File "/opt/layerindex/layers/bitbake/lib/bb/command.py", line 568, in parseRecipeFile
    envdata = bb.cache.parse_recipe(config_data, fn, appendfiles)['']
  File "/opt/layerindex/layers/bitbake/lib/bb/cache.py", line 315, in parse_recipe
    bb_data = bb.parse.handle(bbfile, bb_data)
  File "/opt/layerindex/layers/bitbake/lib/bb/parse/__init__.py", line 117, in handle
    return h['handle'](fn, data, include)
  File "/opt/layerindex/layers/bitbake/lib/bb/parse/parse_py/BBHandler.py", line 132, in handle
    abs_fn = resolve_file(fn, d)
  File "/opt/layerindex/layers/bitbake/lib/bb/parse/__init__.py", line 141, in resolve_file
    raise IOError(errno.ENOENT, "file %s not found" % fn)
FileNotFoundError: [Errno 2] file /opt/layerindex/layers/git___git_openembedded_org_openembedded-core/meta/recipes-devtools/python-numpy/python-numpy_1.13.1.bb not found

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-07-09 13:50:15 +02:00
Paul Eggleton
28e2a6b4b9 update_layer: drop debug message for deleting recipe file dependencies
This was too noisy and not particularly useful.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-07-09 13:50:15 +02:00
Robert Yang
7728c0f84f update.py: check whether branch existed when nocheckout
Fixed:
Assume there is no master branch in hello layer:
$ update.py -l hello -b master
INFO: Skipping update of layer hello - branch master doesn't exist

This is correct since hello layer doesn't have master branch, but when --nocheckout:
$ update.py -l hello -b master  --nocheckout
[snip]
INFO: Sorting layers for branch mater:
WARNING: Cannot find required collections on branch master:
WARNING: hello: LAYERDEPENDS: <snip>

This is incorrect, this patch fixed the problem, now it skips it since the
branch doesn't exists when --nocheckout.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-07-09 13:46:08 +02:00
Robert Yang
635187b594 update_layer.py: avoid calling setup_core_layer_sys_path() when --initial
Fixed:
$ update.py -b <new_branch>
[snip]
NOTE: Starting bitbake server...
Traceback (most recent call last):
  File "update_layer.py", line 471, in main
    utils.setup_core_layer_sys_path(settings, branch.name)
  File "/buildarea1/lyang1/layerindex-web/layerindex/utils.py", line 376, in setup_core_layer_sys_path
    core_layerdir = os.path.join(core_repodir, core_layerbranch.vcs_subdir)
AttributeError: 'NoneType' object has no attribute 'vcs_subdir'
[snip]

This is because core_layerbranch is not in database yet for completely new
branch, so it is None and we will get the error. Avoid calling
setup_core_layer_sys_path() when "update_layer.py --initial" will fix the
problem.

And also only add core layer's sys path when it is present, since core layer
may not be added yet for completely new branch.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-07-09 13:46:08 +02:00
Paul Eggleton
2da4f5d99b Implement patch tracking
Collect information about patches applied by a recipe, and record each
patch along with the upstream status, presenting them in the recipe
detail.

Implements [YOCTO #7909].

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-05-04 23:57:53 +12:00
Paul Eggleton
ebceecb7fd Save recipe source URLs
Save each remote SRC_URI so we can use these for the recipe reporting
system. This replaces an earlier implementation in the rrs branch where
we simply stored SRC_URI verbatim.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-05-04 23:57:53 +12:00
Robert Yang
430ca8c639 update_layer.py: move layer validation to update.py (Performance improve)
The utils.setup_django() costs a lot of time, but both update.py and
update_layer.py call it, so move layer validation from update_layer.py
to update.py to avoid calling update_layer.py when possible can save a
lot of time.

Now we don't have to call update_layer.py in the following cases:
* The branch doesn't exist
* The layer is already update to date on specified branch (when no
  reload)
* The layer dir or conf/layer.layer doesn't exist

We can save up to 98% time in my testing:

$ update.py -b master --nofetch [--fullreload]

                   Before    Now       Reduced
No update:         276s       3.6s      98%
Partial update:    312s      87s        72%
Full reload:      1016s     980s         3%

Note:
* All of the testing are based on --nofetch

* "No update" means all layers on the branch is up-to-date, for
  example, when we run it twice, there is no update in the second run,
  so we only need about 3s now, which is the most common case when we
  use cron to run it per half an hour.

* "Partial update" means part of the layers have been updated.

* "Full reload" means all of the layers have been updated.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-04-24 10:13:39 +12:00
Robert Yang
d27abc10ba update_layer.py: only call init_parser when needed
tinfoil is not needed in cases like the layer is already up-to-date or the
layer is invalid, so only init it when needed.

This can save about 1min when run "update.py -b <branch>" (124 layers).

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-02-05 10:58:37 +13:00
Paul Eggleton
87fe124ad7 update_layer: fix handling of database errors
If a database error occurs when we save a recipe (e.g. because a
database-level constraint is voilated) it will mess up the transaction.
Unfortunately that means we need to break out of updating the entire
layer rather than catching the error, because if we do catch it we just
get errors on every update after the initial error; failing early and
giving up on the transaction is a little better in terms of not filling
up the update logs with further useless errors.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2018-01-09 17:10:45 +13:00
Paul Eggleton
d24cfadd08 update_layer: fix handling of get_or_create()
Use the more typical handling of the return of get_or_create() and check
if the item was created before saving.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-12-21 16:14:11 +13:00
Paul Eggleton
71206d54fe update_layer: fix duplicate PACKAGECONFIG items
Fix duplicate PackageConfig records being created each time a recipe
is being updated - we need to delete the old ones first.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-12-21 16:14:07 +13:00
Amanda Brindle
51e83c8d31 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
dependencies, 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 that depend on any particular recipe. Use "depends:recipename"
in the recipe search to activate this.

Fixes [YOCTO #12129]
Fixes [YOCTO #11415]

Signed-off-by: Amanda Brindle <amanda.r.brindle@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-12-18 09:01:29 +13:00
Paul Eggleton
36d315972b update: allow preserving temp directory
If you're diagnosing problems with the bitbake server when running the
update script, then you need to be able to look at
bitbake-cookerdaemon.log, but you couldn't do that after the fact
because the temporary directory it gets written out to was being
unconditionally deleted. Add a --keep-temp option which preserves it and
some debug messages to tell you where it is.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-10-03 11:42:51 +13:00
Robert Yang
e93eef34bd update_layer.py: delete layerbranch for non-existent branch
The branch is not needed any more when it has been removed from the repo, so we
also need remove its layerbranch, otherwise it still can be got from the web,
which causes confusions.

Note, we have to move the location of tinfoil's code to avoid creating tinfoil
when not needed, otherewise, if tinfoil is created (but not needed), and the
program exists earlier before "try ... finally" block, then tinfoil.shutdown()
doesn't run so that it is not shutdown. Move the code back, right before where
it is needed can fix the problem.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-07-21 08:15:53 +02:00
Robert Yang
b478ff069e update_layer.py: move the location of transaction.atomic()
It doesn't need to be so ahead since we only need it when writing database, and
a following patch will remove layerbranch from database when the branch had
been removed from the repo, it's not easy to do the work in
transaction.atomic() block.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-07-21 08:15:53 +02:00
Robert Yang
de4400674d update_layer.py: remove --update-dependencies
It never works since it is in the middle of transaction.atomic() block, and
update.py doesn't need it any more, so remove it.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-07-21 08:15:53 +02:00
Robert Yang
3ae517091b update.py: update layers in dependency order
* Problems
The update.py couldn't handle new (not only new branch, in fact, but also
existing branches, see below for more info) branch well, for example, there are
3 layers: layer_A, layer_B and layer_C, and create new branch "branch_1" for
them, and they have depends:

layer_A -> layer_B -> layer_C

The "->" means depends on.

Then run "update.py -b branch_1", there would be errors like:

ERROR: Dependency layer_B of layer_A does not have branch record for branch branch_1

Though update.py runs "update_layer.py" twice, but it didn't help since
layerbranch was None when it was failed to create in the first run.

The reason is if update.py updates layer_A firstly, it would fail since it
can't find layer_B:branch_1 in database (not added to database yet), similarly,
if add layer_B before layer_C, it would also fail. Only layer_C can be added
(assume it has no dependencies). So we have to re-run update.py again and again
to make it work, here we may have to run update.py 3 times, and more runs are
needed if the dependency chain is longer.

* Solutions:
Make update.py pass layers orderly to update_layer.py according to dependencies
can fix the problem, we can get intial dependencies from tinfoil, add an option
"-i, --initial" to update_layer.py to get them.

Not only new branch, but also existing branches may have the problem, because
collections and dependencies maybe changed in the coming update, so we can't
trust database when the layer is going to be updated, for example, if there are
10 layers in database, and 3 of them will be updated (-l layer1,layer2,layer3),
then we can not use the 3 layers' collections data from database, we need get
them from tinfoil.

* Performance improvement:
  It should be the same as before in theory, I have tested it with 97 layers:
  - Before: 11m6.472s, but only 75 layers were added, 22 ones were failed, I
    have to re-run update.py again and again (maybe 4 times to make all of
    them added). So:
    (11 * 60 + 6)/75*97/60 = 14m35s

  - Now 12m10.350s, all the layers are added in the first run.

   So about 2 minutes are saved.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-07-21 08:15:53 +02:00
Robert Yang
d689b785c4 update_layer.py: set layerbranch's collection before add_dependencies
The _add_dependency() uses:
if layerbranch.collection:
    var_name = layerbranch.collection

The layerbranch.collection is none if it is newly created, thus it can't get
LAYERDEPENDS, because what defined in layer.conf is LAYERDEPENDS_<collection>,
but what it would get is LAYERDEPENDS_<layer_name>, this patch can fix the
problem.

Reproducer:
$ python3 update_layer.py -l mete-xfce -b <newbranch> --fullreload -d

It would get None LAYERDEPENDS.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-07-21 08:14:57 +02:00
Robert Yang
a474dae060 layerconfparse.py: remove unused layerbranch from parse_layer()
The layerbranch is not used in parse_layer(), so remove it.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-07-21 08:14:57 +02:00
Paul Eggleton
2919424f89 update_layer.py: use DISTRO_NAME as primary distro short description
When reading conf/distro/*.conf to create distro records, attempt to
parse the config file and use DISTRO_NAME (if set) to populate the
description field for the record. If that's not set then fall back to
the less commonly used meta-comment that we supported previously.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-03-14 15:19:53 +13:00
Paul Eggleton
19a559cfa6 update_layer: fix tinfoil shutdown check to work for fido branch
The fido branch of OE-Core expects BitBake 1.26.x. Unfortunately that
version had a commit backported which introduced a non-working
tinfoil.shutdown() method, so we can't simply rely on its presence to
determine whether or not it should be called. Use a check on the BitBake
version number instead.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-01-18 11:20:23 +13:00
Paul Eggleton
3b4fecb217 update: fix logic for updating layer dependencies at the end
* We were passing the incorrect path (to the top of the layer repo) if
  the layer had a subdirectory, so this doesn't seem to have been able
  to work for such layers previously.
* Doing this update in the main update.py script meant that this could
  never work across branches requiring a python version change (using
  PythonEnvironment records) since the code was running within the same
  environment in which update.py was launched - the entire point of the
  separation of the two scripts. Move the checking to update_layer.py
  and call it separately to perform these updates, splitting out some
  common code in order to do so.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-01-16 13:08:18 +13:00
Paul Eggleton
15748efcc1 update_layer: fix tinfoil shutdown
Fix a couple of problems with shutting down tinfoil:

1) tinfoil.shutdown() wasn't being called on error because it wasn't
   inside a finally: section, thus on error the script hung with bitbake
   master (since the tinfoil2 changes) - this is almost certainly a bug
   in bitbake but let's handle it here anyway.
2) We need to check whether the tinfoil object actually has a shutdown
   attribute since we still want to support updating for older branches
   where tinfoil didn't have a shutdown() method.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2017-01-16 13:07:58 +13:00
Paul Eggleton
05f3bccbc6 update_layer: fix regressions in missing layer detection
If a layer is removed by its subdirectory being deleted then we want to
pick up on that and produce an appropriate error message - so let the
layer_update code do the checking out and verifying things are correct
before trying to parse layer.conf.

This also fixes --nocheckout still checking out a branch.

Additionally, drop some code that gets the layerbranch which we just
retrieved a few lines above.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2016-11-07 16:39:38 +13:00
Mark Hatle
9282cba99f layerindex/update_layer.py: Preserve the recipedependency files
In order to keep primary keys from constantly changing, preserve the
existing keys as much as possible.

Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
Signed-off-by: Liam R. Howlett <Liam.Howlett@WindRiver.com>
2016-10-18 16:42:16 +13:00
Liam R. Howlett
61845b7465 layerindexer: Add layer recommends support
Parse layer.conf and add dependencies that are not required from
LAYERRECOMMENDS_<name>.  Update the layerindex/template to support
recommends.  Uses bitbake parsing code & checks versions.

Signed-off-by: Liam R. Howlett <Liam.Howlett@WindRiver.com>

Added associated migration.

Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
2016-10-18 16:42:16 +13:00
Liam R. Howlett
65f0b71ade layerindex: Add collection and version to layerbranch
Collection and version will be pulled from the layer.conf if it exists
and dependencies will be resolved by first checking for layers with the
dependency name and then checking for collections.

Signed-off-by: Liam R. Howlett <Liam.Howlett@WindRiver.com>

Added associated migration.

Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
2016-10-18 16:42:16 +13:00
Liam R. Howlett
540336edde layerindex: Detect dependencies from layer.conf files
Read dependencies from layer.conf and try to create the LayerDependency
entry by looking up the correct database object.  Dependencies are found
by layer name only - no collection support.  layer.conf parsing is
handled by the bitbake code.

Once all layers are added, the dependencies have to be rechecked in case
the layers are not added in order.

Signed-off-by: Liam R. Howlett <Liam.Howlett@WindRiver.com>
2016-10-18 16:42:16 +13:00
Liam R. Howlett
31c85196d0 layerindex: Add distro to web interface and model
Add the distros to the index.  This looks a lot like the machines
and allows users to search for a particular distro.

Signed-off-by: Liam R. Howlett <Liam.Howlett@WindRiver.com>

Added associated migration.

Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
2016-10-18 16:42:15 +13:00
Paul Eggleton
00bae9978d update_layer.py: fix up for bitbake API change
The multiconfig changes broke the calls here to loadDataFull(). To avoid
this being an issue in future, make use of tinfoil's new parse_recipe_file()
function (if available) to isolate the code here from any future changes to
bitbake's internal code.

Part of the fix for [YOCTO #10192].

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2016-08-31 08:57:37 +12:00
Paul Eggleton
5e6a50c07d update_layer.py: use new-style transaction API
The old transaction API has been removed in Django 1.8 and was
deprecated at 1.6. There's no explicit open transaction, commit or
rollback now - we just wrap the layer operations in
"with transaction.atomic()"; if we need to roll back we just raise a
"dummy" exception.

Part of the implementation for [YOCTO #9620].

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2016-06-12 11:33:08 +12:00
Paul Eggleton
3919f74a2a update_layer.py: fix handling of renames with newer GitPython
Newer versions of GitPython implement rename detection, which means that
such changes don't show up as adds and deletes anymore and you get a
bunch of ENOENT errors for renamed files. Add some code to explicitly
look for renames and process them appropriately.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2016-06-12 11:33:08 +12:00
Paul Eggleton
a6054920df update_layer.py: rename confusing loop variables
Stop using "d" (sometimes multiple levels!) and use proper descriptive
variable names instead.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2016-06-12 11:33:08 +12:00
Paul Eggleton
c64e4c57a9 update.py: refactor into two separate scripts
In order to try to avoid problems with leaking memory, context bleeding
from one layer to another, and lay the groundwork for supporting
updating all branches in one operation, split the updating of a single
layer out to its own internal script. This means that the tinfoil
instantiation and metadata parsing is in a completely separate process
per layer.

Implements [YOCTO #9647].

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
2016-05-30 16:05:25 +12:00