Skip to contentSkip to conversation

Improvements for utility functions

PATCH New Paul Eggleton 1 participant 4 messages May 18, 2015

lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
index c97f3ef..1681efd 100644
@@ -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
#2 lore raw

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
index 1681efd..0db7e56 100644
@@ -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
#3 lore raw

PATCH 3/3 lib/bb/utils: fix and extend edit_metadata_file()

In reply to Paul Eggleton

Fix 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
index 6e09858..9171509 100644
@@ -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