Improvements for utility functions
cover Improvements for utility functions
A patch adding a utility function for getting the layer a file is contained in, plus a couple of patches extending and improving the behaviour of edit_metadata_file() (in support of some devtool/recipetool enhancements in OE that are about to be posted). I kept the two patches separate as the first one may be backportable to previous branches.
The following changes since commit 76f095107a0eaf987a5a6a48eed7b98f87aea121:
tests/parse: Add file missing from previous commit (2015-05-16 22:40:30 +0100)
are available in the git repository at:
git://git.yoctoproject.org/poky-contrib paule/bb-edit-metadata-fixes
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=paule/bb-edit-metadata-fixes
Paul Eggleton (3):
lib/bb/utils: add function to get layer containing a file lib/bb/utils: fix several bugs in edit_metadata_file() lib/bb/utils: fix and extend edit_metadata_file()
lib/bb/tests/utils.py | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/bb/utils.py | 256 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 472 insertions(+), 55 deletions(-)
Signature · 1 line
2.1.0
PATCH 1/3 lib/bb/utils: add function to get layer containing a file
In certain contexts it can be useful to find the layer that a file (e.g. a recipe) appears in.
Implements [YOCTO #7723].
--- lib/bb/utils.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
lib/bb/utils.py +16 -0
@@ -1097,3 +1097,19 @@ def edit_bblayers_conf(bblayers_conf, add, remove): return (notadded, notremoved) + +def get_file_layer(filename, d): + """Determine the collection (as defined by a layer's layer.conf file) containing the specified file""" + collections = (d.getVar('BBFILE_COLLECTIONS', True) or '').split() + collection_res = {} + for collection in collections: + collection_res[collection] = d.getVar('BBFILE_PATTERN_%s' % collection, True) or '' + + # Use longest path so we handle nested layers + matchlen = 0 + match = None + for collection, regex in collection_res.iteritems(): + if len(regex) > matchlen and re.match(regex, filename): + matchlen = len(regex) + match = collection + return match
Signature · 1 line
2.1.0
PATCH 2/3 lib/bb/utils: fix several bugs in edit_metadata_file()
In reply to Paul Eggleton* Fix unchanged assignments being dropped if other lines changed
* Fix not passing variable name from single-line assignments to the function
* Fix not trimming the trailing quote from values
--- lib/bb/utils.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
lib/bb/utils.py +11 -3
@@ -974,6 +974,7 @@ def edit_metadata_file(meta_file, variables, func): updated = False varset_start = '' + varlines = [] newlines = [] in_var = None full_value = ''
@@ -1001,14 +1002,19 @@ def edit_metadata_file(meta_file, variables, func): else: newlines.append('%s "%s"\n' % (varset_start, newvalue)) return True - return False + else: + # Put the old lines back where they were + newlines.extend(varlines) + return False with open(meta_file, 'r') as f: for line in f: if in_var: value = line.rstrip() + varlines.append(line) full_value += value[:-1] if value.endswith('"') or value.endswith("'"): + full_value = full_value[:-1] if handle_var_end(): updated = True in_var = None
@@ -1022,11 +1028,13 @@ def edit_metadata_file(meta_file, variables, func): if value.endswith('\\'): value = value[:-1] full_value = value + varlines = [line] + in_var = varname if value.endswith('"') or value.endswith("'"): + full_value = full_value[:-1] if handle_var_end(): updated = True - else: - in_var = varname + in_var = None matched = True break if not matched:
Signature · 1 line
2.1.0
PATCH 3/3 lib/bb/utils: fix and extend edit_metadata_file()
In reply to Paul EggletonFix several bugs and add some useful enhancements to make this into a more generic metadata editing function:
* Support modifying function values (name must be specified ending with "()")
* Support dropping values by returning None as the new value
* Split out edit_metadata() function to provide same functionality on a list/iterable
* Pass operation to callback and allow function to return them
* Pass current output lines to callback so they can be modified
* Fix handling of single-quoted values
* Handle :=, =+, .=, and =. operators
* Support arbitrary indent string
* Support indenting by length of assignment (by specifying -1)
* Fix typo in variablename - intentspc -> indentspc
* Expand function docstring to cover arguments / usage
* Add a parameter to enable matching names with overrides applied
* Add some bitbake-selftest tests
Note that this does change the expected signature of the callback function. The only known caller is in lib/bb/utils.py itself; I doubt anyone else has made extensive use of this function yet.
--- lib/bb/tests/utils.py | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/bb/utils.py | 238 +++++++++++++++++++++++++++++++++----------- 2 files changed, 451 insertions(+), 58 deletions(-)
lib/bb/tests/utils.py +271 -0
@@ -22,6 +22,7 @@ import unittest import bb import os +import tempfile class VerCmpString(unittest.TestCase):
@@ -105,3 +106,273 @@ class Path(unittest.TestCase): for arg1, correctresult in checkitems: result = bb.utils._check_unsafe_delete_path(arg1) self.assertEqual(result, correctresult, '_check_unsafe_delete_path("%s") != %s' % (arg1, correctresult)) + + +class EditMetadataFile(unittest.TestCase): + _origfile = """ +# A comment +HELLO = "oldvalue" + +THIS = "that" + +# Another comment +NOCHANGE = "samevalue" +OTHER = 'anothervalue' + +MULTILINE = "a1 \\ + a2 \\ + a3" + +MULTILINE2 := " \\ + b1 \\ + b2 \\ + b3 \\ + " + + +MULTILINE3 = " \\ + c1 \\ + c2 \\ + c3 \\ +" + +do_functionname() { + command1 ${VAL1} ${VAL2} + command2 ${VAL3} ${VAL4} +} +""" + def _testeditfile(self, varvalues, compareto, dummyvars=None): + if dummyvars is None: + dummyvars = [] + with tempfile.NamedTemporaryFile('w', delete=False) as tf: + tf.write(self._origfile) + tf.close() + try: + varcalls = [] + def handle_file(varname, origvalue, op, newlines): + self.assertIn(varname, varvalues, 'Callback called for variable %s not in the list!' % varname) + self.assertNotIn(varname, dummyvars, 'Callback called for variable %s in dummy list!' % varname) + varcalls.append(varname) + return varvalues[varname] + + bb.utils.edit_metadata_file(tf.name, varvalues.keys(), handle_file) + with open(tf.name) as f: + modfile = f.readlines() + # Ensure the output matches the expected output + self.assertEqual(compareto.splitlines(True), modfile) + # Ensure the callback function was called for every variable we asked for + # (plus allow testing behaviour when a requested variable is not present) + self.assertEqual(sorted(varvalues.keys()), sorted(varcalls + dummyvars)) + finally: + os.remove(tf.name) + + + def test_edit_metadata_file_nochange(self): + # Test file doesn't get modified with nothing to do + self._testeditfile({}, self._origfile) + # Test file doesn't get modified with only dummy variables + self._testeditfile({'DUMMY1': ('should_not_set', None, 0, True), + 'DUMMY2': ('should_not_set_again', None, 0, True)}, self._origfile, dummyvars=['DUMMY1', 'DUMMY2']) + # Test file doesn't get modified with some the same values + self._testeditfile({'THIS': ('that', None, 0, True), + 'OTHER': ('anothervalue', None, 0, True), + 'MULTILINE3': (' c1 c2 c3', None, 4, False)}, self._origfile) + + def test_edit_metadata_file_1(self): + + newfile1 = """ +# A comment +HELLO = "newvalue" + +THIS = "that" + +# Another comment +NOCHANGE = "samevalue" +OTHER = 'anothervalue' + +MULTILINE = "a1 \\ + a2 \\ + a3" + +MULTILINE2 := " \\ + b1 \\ + b2 \\ + b3 \\ + " + + +MULTILINE3 = " \\ + c1 \\ + c2 \\ + c3 \\ +" + +do_functionname() { + command1 ${VAL1} ${VAL2} + command2 ${VAL3} ${VAL4} +} +""" + self._testeditfile({'HELLO': ('newvalue', None, 4, True)}, newfile1) + + + def test_edit_metadata_file_2(self): + + newfile2 = """ +# A comment +HELLO = "oldvalue" + +THIS = "that" + +# Another comment +NOCHANGE = "samevalue" +OTHER = 'anothervalue' + +MULTILINE = " \\ + d1 \\ + d2 \\ + d3 \\ + " + +MULTILINE2 := " \\ + b1 \\ + b2 \\ + b3 \\ + " + + +MULTILINE3 = "nowsingle" + +do_functionname() { + command1 ${VAL1} ${VAL2} + command2 ${VAL3} ${VAL4} +} +""" + self._testeditfile({'MULTILINE': (['d1','d2','d3'], None, 4, False), + 'MULTILINE3': ('nowsingle', None, 4, True), + 'NOTPRESENT': (['a', 'b'], None, 4, False)}, newfile2, dummyvars=['NOTPRESENT']) + + + def test_edit_metadata_file_3(self): + + newfile3 = """ +# A comment +HELLO = "oldvalue" + +# Another comment +NOCHANGE = "samevalue" +OTHER = "yetanothervalue" + +MULTILINE = "e1 \\ + e2 \\ + e3 \\ + " + +MULTILINE2 := "f1 \\ +\tf2 \\ +\t" + + +MULTILINE3 = " \\ + c1 \\ + c2 \\ + c3 \\ +" + +do_functionname() { + othercommand_one a b c + othercommand_two d e f +} +""" + + self._testeditfile({'do_functionname()': (['othercommand_one a b c', 'othercommand_two d e f'], None, 4, False), + 'MULTILINE2': (['f1', 'f2'], None, '\t', True), + 'MULTILINE': (['e1', 'e2', 'e3'], None, -1, True), + 'THIS': (None, None, 0, False), + 'OTHER': ('yetanothervalue', None, 0, True)}, newfile3) + + + def test_edit_metadata_file_4(self): + + newfile4 = """ +# A comment +HELLO = "oldvalue" + +THIS = "that" + +# Another comment +OTHER = 'anothervalue' + +MULTILINE = "a1 \\ + a2 \\ + a3" + +MULTILINE2 := " \\ + b1 \\ + b2 \\ + b3 \\ + " + + +""" + + self._testeditfile({'NOCHANGE': (None, None, 0, False), + 'MULTILINE3': (None, None, 0, False), + 'THIS': ('that', None, 0, False), + 'do_functionname()': (None, None, 0, False)}, newfile4) + + + def test_edit_metadata(self): + newfile5 = """ +# A comment +HELLO = "hithere" + +# A new comment +THIS += "that" + +# Another comment +NOCHANGE = "samevalue" +OTHER = 'anothervalue' + +MULTILINE = "a1 \\ + a2 \\ + a3" + +MULTILINE2 := " \\ + b1 \\ + b2 \\ + b3 \\ + " + + +MULTILINE3 = " \\ + c1 \\ + c2 \\ + c3 \\ +" + +NEWVAR = "value" + +do_functionname() { + command1 ${VAL1} ${VAL2} + command2 ${VAL3} ${VAL4} +} +""" + + + def handle_var(varname, origvalue, op, newlines): + if varname == 'THIS': + newlines.append('# A new comment\n') + elif varname == 'do_functionname()': + newlines.append('NEWVAR = "value"\n') + newlines.append('\n') + valueitem = varvalues.get(varname, None) + if valueitem: + return valueitem + else: + return (origvalue, op, 0, True) + + varvalues = {'HELLO': ('hithere', None, 0, True), 'THIS': ('that', '+=', 0, True)} + varlist = ['HELLO', 'THIS', 'do_functionname()'] + (updated, newlines) = bb.utils.edit_metadata(self._origfile.splitlines(True), varlist, handle_var) + self.assertTrue(updated, 'List should be updated but isn\'t') + self.assertEqual(newlines, newfile5.splitlines(True))
lib/bb/utils.py +180 -58
280 lines not on this page · raw message
Signature · 1 line
2.1.0