elmarco / rpms / dtc

Forked from rpms/dtc 2 years ago
Clone
Blob Blame History Raw
From 50f2507016315e0b9499dd58876ffc1acf91cc5a Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 17 Mar 2017 16:14:30 -0600
Subject: [PATCH 01/29] Add an initial Python library for libfdt

Add Python bindings for a bare-bones set of libfdt functions. These allow
navigating the tree and reading node names and properties.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile                   |   1 +
 pylibfdt/.gitignore        |   3 +
 pylibfdt/Makefile.pylibfdt |  17 ++
 pylibfdt/libfdt.swig       | 433 +++++++++++++++++++++++++++++++++++++++++++++
 pylibfdt/setup.py          |  34 ++++
 5 files changed, 488 insertions(+)
 create mode 100644 pylibfdt/.gitignore
 create mode 100644 pylibfdt/Makefile.pylibfdt
 create mode 100644 pylibfdt/libfdt.swig
 create mode 100644 pylibfdt/setup.py

diff --git a/Makefile b/Makefile
index c3f72e0..1b69f53 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,7 @@ CFLAGS = -g -Os -fPIC -Werror $(WARNINGS)
 
 BISON = bison
 LEX = flex
+SWIG = swig
 
 INSTALL = /usr/bin/install
 DESTDIR =
diff --git a/pylibfdt/.gitignore b/pylibfdt/.gitignore
new file mode 100644
index 0000000..5e8c5e3
--- /dev/null
+++ b/pylibfdt/.gitignore
@@ -0,0 +1,3 @@
+libfdt.py
+libfdt.pyc
+libfdt_wrap.c
diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
new file mode 100644
index 0000000..0d8c010
--- /dev/null
+++ b/pylibfdt/Makefile.pylibfdt
@@ -0,0 +1,17 @@
+# Makefile.pylibfdt
+#
+
+PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS))
+WRAP = $(PYLIBFDT_objdir)/libfdt_wrap.c
+PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
+
+$(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
+	@$(VECHO) PYMOD $@
+	python $(PYLIBFDT_objdir)/setup.py "$(CPPFLAGS)" $^
+	mv _libfdt.so $(PYMODULE)
+
+$(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
+	@$(VECHO) SWIG $@
+	$(SWIG) -python -o $@ $<
+
+PYLIBFDT_cleanfiles = libfdt_wrap.c libfdt.py libfdt.pyc _libfdt.so
diff --git a/pylibfdt/libfdt.swig b/pylibfdt/libfdt.swig
new file mode 100644
index 0000000..cd1c6a9
--- /dev/null
+++ b/pylibfdt/libfdt.swig
@@ -0,0 +1,433 @@
+/*
+ * pylibfdt - Flat Device Tree manipulation in Python
+ * Copyright (C) 2017 Google, Inc.
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ *  a) This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This library is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public
+ *     License along with this library; if not, write to the Free
+ *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ *     MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ *  b) Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *     1. Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *     2. Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+%module libfdt
+
+%{
+#define SWIG_FILE_WITH_INIT
+#include "libfdt.h"
+%}
+
+%pythoncode %{
+
+import struct
+
+# Error codes, corresponding to FDT_ERR_... in libfdt.h
+(NOTFOUND,
+        EXISTS,
+        NOSPACE,
+        BADOFFSET,
+        BADPATH,
+        BADPHANDLE,
+        BADSTATE,
+        TRUNCATED,
+        BADMAGIC,
+        BADVERSION,
+        BADSTRUCTURE,
+        BADLAYOUT,
+        INTERNAL,
+        BADNCELLS,
+        BADVALUE,
+        BADOVERLAY,
+        NOPHANDLES) = QUIET_ALL = range(1, 18)
+# QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions
+# altogether. All # functions passed this value will return an error instead
+# of raising an exception.
+
+# Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors,
+# instead of raising an exception.
+QUIET_NOTFOUND = (NOTFOUND,)
+
+
+class FdtException(Exception):
+    """An exception caused by an error such as one of the codes above"""
+    def __init__(self, err):
+        self.err = err
+
+    def __str__(self):
+        return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err))
+
+def strerror(fdt_err):
+    """Get the string for an error number
+
+    Args:
+        fdt_err: Error number (-ve)
+
+    Returns:
+        String containing the associated error
+    """
+    return fdt_strerror(fdt_err)
+
+def check_err(val, quiet=()):
+    """Raise an error if the return value is -ve
+
+    This is used to check for errors returned by libfdt C functions.
+
+    Args:
+        val: Return value from a libfdt function
+        quiet: Errors to ignore (empty to raise on all errors)
+
+    Returns:
+        val if val >= 0
+
+    Raises
+        FdtException if val < 0
+    """
+    if val < 0:
+        if -val not in quiet:
+            raise FdtException(val)
+    return val
+
+def check_err_null(val, quiet=()):
+    """Raise an error if the return value is NULL
+
+    This is used to check for a NULL return value from certain libfdt C
+    functions
+
+    Args:
+        val: Return value from a libfdt function
+        quiet: Errors to ignore (empty to raise on all errors)
+
+    Returns:
+        val if val is a list, None if not
+
+    Raises
+        FdtException if val indicates an error was reported and the error
+        is not in @quiet.
+    """
+    # Normally a list is returned which contains the data and its length.
+    # If we get just an integer error code, it means the function failed.
+    if not isinstance(val, list):
+        if -val not in quiet:
+            raise FdtException(val)
+    return val
+
+class Fdt:
+    """Device tree class, supporting all operations
+
+    The Fdt object is created is created from a device tree binary file,
+    e.g. with something like:
+
+       fdt = Fdt(open("filename.dtb").read())
+
+    Operations can then be performed using the methods in this class. Each
+    method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...).
+
+    All methods raise an FdtException if an error occurs. To avoid this
+    behaviour a 'quiet' parameter is provided for some functions. This
+    defaults to empty, but you can pass a list of errors that you expect.
+    If one of these errors occurs, the function will return an error number
+    (e.g. -NOTFOUND).
+    """
+    def __init__(self, data):
+        self._fdt = bytearray(data)
+        check_err(fdt_check_header(self._fdt));
+
+    def path_offset(self, path, quiet=()):
+        """Get the offset for a given path
+
+        Args:
+            path: Path to the required node, e.g. '/node@3/subnode@1'
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Node offset
+
+        Raises
+            FdtException if the path is not valid or not found
+        """
+        return check_err(fdt_path_offset(self._fdt, path), quiet)
+
+    def first_property_offset(self, nodeoffset, quiet=()):
+        """Get the offset of the first property in a node offset
+
+        Args:
+            nodeoffset: Offset to the node to check
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Offset of the first property
+
+        Raises
+            FdtException if the associated node has no properties, or some
+                other error occurred
+        """
+        return check_err(fdt_first_property_offset(self._fdt, nodeoffset),
+                         quiet)
+
+    def next_property_offset(self, prop_offset, quiet=()):
+        """Get the next property in a node
+
+        Args:
+            prop_offset: Offset of the previous property
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Offset of the next property
+
+        Raises:
+            FdtException if the associated node has no more properties, or
+                some other error occurred
+        """
+        return check_err(fdt_next_property_offset(self._fdt, prop_offset),
+                         quiet)
+
+    def get_name(self, nodeoffset):
+        """Get the name of a node
+
+        Args:
+            nodeoffset: Offset of node to check
+
+        Returns:
+            Node name
+
+        Raises:
+            FdtException on error (e.g. nodeoffset is invalid)
+        """
+        return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
+
+    def get_property_by_offset(self, prop_offset, quiet=()):
+        """Obtains a property that can be examined
+
+        Args:
+            prop_offset: Offset of property (e.g. from first_property_offset())
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Property object, or None if not found
+
+        Raises:
+            FdtException on error (e.g. invalid prop_offset or device
+            tree format)
+        """
+        pdata = check_err_null(
+                fdt_get_property_by_offset(self._fdt, prop_offset), quiet)
+        if isinstance(pdata, (int)):
+            return pdata
+        return Property(pdata[0], pdata[1])
+
+    def first_subnode(self, nodeoffset, quiet=()):
+        """Find the first subnode of a parent node
+
+        Args:
+            nodeoffset: Node offset of parent node
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of the first subnode, if any
+
+        Raises:
+            FdtException if no subnode found or other error occurs
+        """
+        return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
+
+    def next_subnode(self, nodeoffset, quiet=()):
+        """Find the next subnode
+
+        Args:
+            nodeoffset: Node offset of previous subnode
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of the next subnode, if any
+
+        Raises:
+            FdtException if no more subnode found or other error occurs
+        """
+        return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
+
+    def totalsize(self):
+        """Return the total size of the device tree
+
+        Returns:
+            Total tree size in bytes
+        """
+        return check_err(fdt_totalsize(self._fdt))
+
+    def off_dt_struct(self):
+        """Return the start of the device tree struct area
+
+        Returns:
+            Start offset of struct area
+        """
+        return check_err(fdt_off_dt_struct(self._fdt))
+
+    def pack(self, quiet=()):
+        """Pack the device tree to remove unused space
+
+        This adjusts the tree in place.
+
+        Args:
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Raises:
+            FdtException if any error occurs
+        """
+        return check_err(fdt_pack(self._fdt), quiet)
+
+    def delprop(self, nodeoffset, prop_name):
+        """Delete a property from a node
+
+        Args:
+            nodeoffset: Node offset containing property to delete
+            prop_name: Name of property to delete
+
+        Raises:
+            FdtError if the property does not exist, or another error occurs
+        """
+        return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
+
+    def getprop(self, nodeoffset, prop_name, quiet=()):
+        """Get a property from a node
+
+        Args:
+            nodeoffset: Node offset containing property to get
+            prop_name: Name of property to get
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            Value of property as a bytearray, or -ve error number
+
+        Raises:
+            FdtError if any error occurs (e.g. the property is not found)
+        """
+        pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
+                               quiet)
+        if isinstance(pdata, (int)):
+            return pdata
+        return bytearray(pdata[0])
+
+
+class Property:
+    """Holds a device tree property name and value.
+
+    This holds a copy of a property taken from the device tree. It does not
+    reference the device tree, so if anything changes in the device tree,
+    a Property object will remain valid.
+
+    Properties:
+        name: Property name
+        value: Proper value as a bytearray
+    """
+    def __init__(self, name, value):
+        self.name = name
+        self.value = value
+%}
+
+%rename(fdt_property) fdt_property_func;
+
+typedef int fdt32_t;
+
+%include "libfdt/fdt.h"
+
+%include "typemaps.i"
+
+/* Most functions don't change the device tree, so use a const void * */
+%typemap(in) (const void *)(const void *fdt) {
+	if (!PyByteArray_Check($input)) {
+		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
+			"', argument " "$argnum"" of type '" "$type""'");
+	}
+	$1 = (void *)PyByteArray_AsString($input);
+        fdt = $1;
+        fdt = fdt; /* avoid unused variable warning */
+}
+
+/* Some functions do change the device tree, so use void * */
+%typemap(in) (void *)(const void *fdt) {
+	if (!PyByteArray_Check($input)) {
+		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
+			"', argument " "$argnum"" of type '" "$type""'");
+	}
+	$1 = PyByteArray_AsString($input);
+        fdt = $1;
+        fdt = fdt; /* avoid unused variable warning */
+}
+
+%typemap(out) (struct fdt_property *) {
+	PyObject *buff;
+
+	if ($1) {
+		resultobj = PyString_FromString(
+			fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
+		buff = PyByteArray_FromStringAndSize(
+			(const char *)($1 + 1), fdt32_to_cpu($1->len));
+		resultobj = SWIG_Python_AppendOutput(resultobj, buff);
+	}
+}
+
+%apply int *OUTPUT { int *lenp };
+
+/* typemap used for fdt_getprop() */
+%typemap(out) (const void *) {
+	if (!$1)
+		$result = Py_None;
+	else
+		$result = Py_BuildValue("s#", $1, *arg4);
+}
+
+/* We have both struct fdt_property and a function fdt_property() */
+%warnfilter(302) fdt_property;
+
+/* These are macros in the header so have to be redefined here */
+int fdt_magic(const void *fdt);
+int fdt_totalsize(const void *fdt);
+int fdt_off_dt_struct(const void *fdt);
+int fdt_off_dt_strings(const void *fdt);
+int fdt_off_mem_rsvmap(const void *fdt);
+int fdt_version(const void *fdt);
+int fdt_last_comp_version(const void *fdt);
+int fdt_boot_cpuid_phys(const void *fdt);
+int fdt_size_dt_strings(const void *fdt);
+int fdt_size_dt_struct(const void *fdt);
+
+%include <../libfdt/libfdt.h>
diff --git a/pylibfdt/setup.py b/pylibfdt/setup.py
new file mode 100644
index 0000000..0ff160c
--- /dev/null
+++ b/pylibfdt/setup.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+"""
+setup.py file for SWIG libfdt
+"""
+
+from distutils.core import setup, Extension
+import os
+import sys
+
+progname = sys.argv[0]
+cflags = sys.argv[1]
+files = sys.argv[2:]
+
+if cflags:
+    cflags = [flag for flag in cflags.split(' ') if flag]
+else:
+    cflags = None
+
+libfdt_module = Extension(
+    '_libfdt',
+    sources = files,
+    extra_compile_args =  cflags
+)
+
+sys.argv = [progname, '--quiet', 'build_ext', '--inplace']
+
+setup (name = 'libfdt',
+       version = '0.1',
+       author      = "Simon Glass <sjg@chromium.org>",
+       description = """Python binding for libfdt""",
+       ext_modules = [libfdt_module],
+       py_modules = ["libfdt"],
+       )
-- 
2.13.0

From 12cfb740cc76c9c9fa906fee240dc028da2bddd0 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 17 Mar 2017 16:14:31 -0600
Subject: [PATCH 02/29] Add tests for pylibfdt

Add a set of tests to cover the functionality in pylibfdt.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 tests/pylibfdt_tests.py | 288 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 288 insertions(+)
 create mode 100644 tests/pylibfdt_tests.py

diff --git a/tests/pylibfdt_tests.py b/tests/pylibfdt_tests.py
new file mode 100644
index 0000000..ae392bb
--- /dev/null
+++ b/tests/pylibfdt_tests.py
@@ -0,0 +1,288 @@
+# pylibfdt - Tests for Flat Device Tree manipulation in Python
+# Copyright (C) 2017 Google, Inc.
+# Written by Simon Glass <sjg@chromium.org>
+#
+# libfdt is dual licensed: you can use it either under the terms of
+# the GPL, or the BSD license, at your option.
+#
+#  a) This library is free software; you can redistribute it and/or
+#     modify it under the terms of the GNU General Public License as
+#     published by the Free Software Foundation; either version 2 of the
+#     License, or (at your option) any later version.
+#
+#     This library is distributed in the hope that it will be useful,
+#     but WITHOUT ANY WARRANTY; without even the implied warranty of
+#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#     GNU General Public License for more details.
+#
+#     You should have received a copy of the GNU General Public
+#     License along with this library; if not, write to the Free
+#     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+#     MA 02110-1301 USA
+#
+# Alternatively,
+#
+#  b) Redistribution and use in source and binary forms, with or
+#     without modification, are permitted provided that the following
+#     conditions are met:
+#
+#     1. Redistributions of source code must retain the above
+#        copyright notice, this list of conditions and the following
+#        disclaimer.
+#     2. Redistributions in binary form must reproduce the above
+#        copyright notice, this list of conditions and the following
+#        disclaimer in the documentation and/or other materials
+#        provided with the distribution.
+#
+#     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+#     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+#     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+#     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+#     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+#     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+#     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+#     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+#     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+#     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+#     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+import sys
+import types
+import unittest
+
+sys.path.append('../pylibfdt')
+import libfdt
+from libfdt import FdtException, QUIET_NOTFOUND, QUIET_ALL
+
+def get_err(err_code):
+    """Convert an error code into an error message
+
+    Args:
+        err_code: Error code value (FDT_ERR_...)
+
+    Returns:
+        String error code
+    """
+    return 'pylibfdt error %d: %s' % (-err_code, libfdt.fdt_strerror(-err_code))
+
+def _ReadFdt(fname):
+    """Read a device tree file into an Fdt object, ready for use
+
+    Args:
+        fname: Filename to read from
+
+    Returns:
+        Fdt bytearray suitable for passing to libfdt functions
+    """
+    return libfdt.Fdt(open(fname).read())
+
+class PyLibfdtTests(unittest.TestCase):
+    """Test class for pylibfdt
+
+    Properties:
+        fdt: Device tree file used for testing
+    """
+
+    def setUp(self):
+        """Read in the device tree we use for testing"""
+        self.fdt = _ReadFdt('test_tree1.dtb')
+
+    def GetPropList(self, node_path):
+        """Read a list of properties from a node
+
+        Args:
+            node_path: Full path to node, e.g. '/subnode@1/subsubnode'
+
+        Returns:
+            List of property names for that node, e.g. ['compatible', 'reg']
+        """
+        prop_list = []
+        node = self.fdt.path_offset(node_path)
+        poffset = self.fdt.first_property_offset(node, QUIET_NOTFOUND)
+        while poffset > 0:
+            prop = self.fdt.get_property_by_offset(poffset)
+            prop_list.append(prop.name)
+            poffset = self.fdt.next_property_offset(poffset, QUIET_NOTFOUND)
+        return prop_list
+
+    def testImport(self):
+        """Check that we can import the library correctly"""
+        self.assertEquals(type(libfdt), types.ModuleType)
+
+    def testBadFdt(self):
+        """Check that a filename provided accidentally is not accepted"""
+        with self.assertRaises(FdtException) as e:
+            fdt = libfdt.Fdt('a string')
+        self.assertEquals(e.exception.err, -libfdt.BADMAGIC)
+
+    def testPathOffset(self):
+        """Check that we can find the offset of a node"""
+        self.assertEquals(self.fdt.path_offset('/'), 0)
+        self.assertTrue(self.fdt.path_offset('/subnode@1') > 0)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.path_offset('/wibble')
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+        self.assertEquals(self.fdt.path_offset('/wibble', QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+
+    def testPropertyOffset(self):
+        """Walk through all the properties in the root node"""
+        offset = self.fdt.first_property_offset(0)
+        self.assertTrue(offset > 0)
+        for i in range(5):
+            next_offset = self.fdt.next_property_offset(offset)
+            self.assertTrue(next_offset > offset)
+            offset = next_offset
+        self.assertEquals(self.fdt.next_property_offset(offset, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+
+    def testPropertyOffsetExceptions(self):
+        """Check that exceptions are raised as expected"""
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(107)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+        # Quieten the NOTFOUND exception and check that a BADOFFSET
+        # exception is still raised.
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(107, QUIET_NOTFOUND)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.next_property_offset(107, QUIET_NOTFOUND)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+        # Check that NOTFOUND can be quietened.
+        node = self.fdt.path_offset('/subnode@1/ss1')
+        self.assertEquals(self.fdt.first_property_offset(node, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(node)
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+    def testGetName(self):
+        """Check that we can get the name of a node"""
+        self.assertEquals(self.fdt.get_name(0), '')
+        node = self.fdt.path_offset('/subnode@1/subsubnode')
+        self.assertEquals(self.fdt.get_name(node), 'subsubnode')
+
+        with self.assertRaises(FdtException) as e:
+            self.fdt.get_name(-2)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+    def testGetPropertyByOffset(self):
+        """Check that we can read the name and contents of a property"""
+        root = 0
+        poffset = self.fdt.first_property_offset(root)
+        prop = self.fdt.get_property_by_offset(poffset)
+        self.assertEquals(prop.name, 'compatible')
+        self.assertEquals(prop.value, 'test_tree1\0')
+
+        with self.assertRaises(FdtException) as e:
+            self.fdt.get_property_by_offset(-2)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        self.assertEquals(
+                -libfdt.BADOFFSET,
+                self.fdt.get_property_by_offset(-2, [libfdt.BADOFFSET]))
+
+    def testGetProp(self):
+        """Check that we can read the contents of a property by name"""
+        root = self.fdt.path_offset('/')
+        value = self.fdt.getprop(root, "compatible")
+        self.assertEquals(value, 'test_tree1\0')
+        self.assertEquals(-libfdt.NOTFOUND, self.fdt.getprop(root, 'missing',
+                                                             QUIET_NOTFOUND))
+
+        with self.assertRaises(FdtException) as e:
+            self.fdt.getprop(root, 'missing')
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+        node = self.fdt.path_offset('/subnode@1/subsubnode')
+        value = self.fdt.getprop(node, "compatible")
+        self.assertEquals(value, 'subsubnode1\0subsubnode\0')
+
+    def testStrError(self):
+        """Check that we can get an error string"""
+        self.assertEquals(libfdt.strerror(-libfdt.NOTFOUND),
+                          'FDT_ERR_NOTFOUND')
+
+    def testFirstNextSubnodeOffset(self):
+        """Check that we can walk through subnodes"""
+        node_list = []
+        node = self.fdt.first_subnode(0, QUIET_NOTFOUND)
+        while node >= 0:
+            node_list.append(self.fdt.get_name(node))
+            node = self.fdt.next_subnode(node, QUIET_NOTFOUND)
+        self.assertEquals(node_list, ['subnode@1', 'subnode@2'])
+
+    def testFirstNextSubnodeOffsetExceptions(self):
+        """Check except handling for first/next subnode functions"""
+        node = self.fdt.path_offset('/subnode@1/subsubnode', QUIET_NOTFOUND)
+        self.assertEquals(self.fdt.first_subnode(node, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_subnode(node)
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+        node = self.fdt.path_offset('/subnode@1/ss1', QUIET_NOTFOUND)
+        self.assertEquals(self.fdt.next_subnode(node, QUIET_NOTFOUND),
+                          -libfdt.NOTFOUND)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.next_subnode(node)
+        self.assertEquals(e.exception.err, -libfdt.NOTFOUND)
+
+    def testDeleteProperty(self):
+        """Test that we can delete a property"""
+        node_name = '/subnode@1'
+        self.assertEquals(self.GetPropList(node_name),
+                          ['compatible', 'reg', 'prop-int'])
+        node = self.fdt.path_offset('/%s' % node_name)
+        self.assertEquals(self.fdt.delprop(node, 'reg'), 0)
+        self.assertEquals(self.GetPropList(node_name),
+                          ['compatible', 'prop-int'])
+
+    def testHeader(self):
+        """Test that we can access the header values"""
+        self.assertEquals(self.fdt.totalsize(), len(self.fdt._fdt))
+        self.assertEquals(self.fdt.off_dt_struct(), 88)
+
+    def testPack(self):
+        """Test that we can pack the tree after deleting something"""
+        orig_size = self.fdt.totalsize()
+        node = self.fdt.path_offset('/subnode@2', QUIET_NOTFOUND)
+        self.assertEquals(self.fdt.delprop(node, 'prop-int'), 0)
+        self.assertEquals(orig_size, self.fdt.totalsize())
+        self.assertEquals(self.fdt.pack(), 0)
+        self.assertTrue(self.fdt.totalsize() < orig_size)
+
+    def testBadPropertyOffset(self):
+        """Test that bad property offsets are detected"""
+        with self.assertRaises(FdtException) as e:
+            self.fdt.get_property_by_offset(13)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.first_property_offset(3)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+        with self.assertRaises(FdtException) as e:
+            self.fdt.next_property_offset(3)
+        self.assertEquals(e.exception.err, -libfdt.BADOFFSET)
+
+    def testBadPathOffset(self):
+        """Test that bad path names are detected"""
+        with self.assertRaisesRegexp(FdtException, get_err(libfdt.BADPATH)):
+            self.fdt.path_offset('not-present')
+
+    def testQuietAll(self):
+        """Check that exceptions can be masked by QUIET_ALL"""
+        self.assertEquals(-libfdt.NOTFOUND,
+                          self.fdt.path_offset('/missing', QUIET_ALL))
+        self.assertEquals(-libfdt.BADOFFSET,
+                          self.fdt.get_property_by_offset(13, QUIET_ALL))
+        self.assertEquals(-libfdt.BADPATH,
+                          self.fdt.path_offset('missing', QUIET_ALL))
+
+
+if __name__ == "__main__":
+    unittest.main()
-- 
2.13.0

From b40aa8359affa52bd79afe468c26683d6bb41c68 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 17 Mar 2017 16:14:32 -0600
Subject: [PATCH 03/29] Mention pylibfdt in the documentation

Add a note about pylibfdt in the README.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 README | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/README b/README
index f92008f..96d8486 100644
--- a/README
+++ b/README
@@ -7,6 +7,53 @@ DTC and LIBFDT are maintained by:
 David Gibson <david@gibson.dropbear.id.au>
 Jon Loeliger <jdl@jdl.com>
 
+
+Python library
+--------------
+
+A Python library is also available. To build this you will need to install
+swig and Python development files. On Debian distributions:
+
+   sudo apt-get install swig python-dev
+
+The library provides an Fdt class which you can use like this:
+
+$ PYTHONPATH=../pylibfdt python
+>>> import libfdt
+>>> fdt = libfdt.Fdt(open('test_tree1.dtb').read())
+>>> node = fdt.path_offset('/subnode@1')
+>>> print node
+124
+>>> prop_offset = fdt.first_property_offset(node)
+>>> prop = fdt.get_property_by_offset(prop_offset)
+>>> print '%s=%r' % (prop.name, prop.value)
+compatible=bytearray(b'subnode1\x00')
+>>> print '%s=%s' % (prop.name, prop.value)
+compatible=subnode1
+>>> node2 = fdt.path_offset('/')
+>>> print fdt.getprop(node2, 'compatible')
+test_tree1
+
+You will find tests in tests/pylibfdt_tests.py showing how to use each
+method. Help is available using the Python help command, e.g.:
+
+    $ cd pylibfdt
+    $ python -c "import libfdt; help(libfdt)"
+
+If you add new features, please check code coverage:
+
+    $ sudo apt-get install python-pip python-pytest
+    $ sudo pip install coverage
+    $ cd tests
+    $ coverage run pylibfdt_tests.py
+    $ coverage html
+    # Open 'htmlcov/index.html' in your browser
+
+
+More work remains to support all of libfdt, including access to numeric
+values.
+
+
 Mailing list
 ------------
 The following list is for discussion about dtc and libfdt implementation
-- 
2.13.0

From 8cb3896358e9f70b6f742772734b038ed0d4ea19 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 17 Mar 2017 16:14:33 -0600
Subject: [PATCH 04/29] Adjust libfdt.h to work with swig

There are a few places where libfdt.h cannot be used as is with swig:

- macros like fdt_totalsize() have to be defined as C declarations
- fdt_offset_ptr() and fdt_getprop_namelen() need special treatment due to
    a TODO in the wrapper for fdt_getprop(). However they are not useful to
    Python so can be removed

Add #ifdefs to work around these problem.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 libfdt/libfdt.h | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index ac42e04..2c9ddb4 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -143,7 +143,9 @@
 /* Low-level functions (you probably don't need these)                */
 /**********************************************************************/
 
+#ifndef SWIG /* This function is not useful in Python */
 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
+#endif
 static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
 {
 	return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
@@ -210,7 +212,6 @@ int fdt_next_subnode(const void *fdt, int offset);
 /**********************************************************************/
 /* General functions                                                  */
 /**********************************************************************/
-
 #define fdt_get_header(fdt, field) \
 	(fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
 #define fdt_magic(fdt)			(fdt_get_header(fdt, magic))
@@ -354,8 +355,10 @@ int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
  * useful for finding subnodes based on a portion of a larger string,
  * such as a full path.
  */
+#ifndef SWIG /* Not available in Python */
 int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
 			       const char *name, int namelen);
+#endif
 /**
  * fdt_subnode_offset - find a subnode of a given node
  * @fdt: pointer to the device tree blob
@@ -391,7 +394,9 @@ int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
  * Identical to fdt_path_offset(), but only consider the first namelen
  * characters of path as the path name.
  */
+#ifndef SWIG /* Not available in Python */
 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
+#endif
 
 /**
  * fdt_path_offset - find a tree node by its full path
@@ -550,10 +555,12 @@ const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
  * Identical to fdt_get_property(), but only examine the first namelen
  * characters of name for matching the property name.
  */
+#ifndef SWIG /* Not available in Python */
 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
 						    int nodeoffset,
 						    const char *name,
 						    int namelen, int *lenp);
+#endif
 
 /**
  * fdt_get_property - find a given property in a given node
@@ -624,8 +631,10 @@ static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
  *		-FDT_ERR_BADSTRUCTURE,
  *		-FDT_ERR_TRUNCATED, standard meanings
  */
+#ifndef SWIG /* This function is not useful in Python */
 const void *fdt_getprop_by_offset(const void *fdt, int offset,
 				  const char **namep, int *lenp);
+#endif
 
 /**
  * fdt_getprop_namelen - get property value based on substring
@@ -638,6 +647,7 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
  * Identical to fdt_getprop(), but only examine the first namelen
  * characters of name for matching the property name.
  */
+#ifndef SWIG /* Not available in Python */
 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
 				const char *name, int namelen, int *lenp);
 static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
@@ -647,6 +657,7 @@ static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
 	return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
 						      namelen, lenp);
 }
+#endif
 
 /**
  * fdt_getprop - retrieve the value of a given property
@@ -707,8 +718,10 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
  * Identical to fdt_get_alias(), but only examine the first namelen
  * characters of name for matching the alias name.
  */
+#ifndef SWIG /* Not available in Python */
 const char *fdt_get_alias_namelen(const void *fdt,
 				  const char *name, int namelen);
+#endif
 
 /**
  * fdt_get_alias - retrieve the path referenced by a given alias
@@ -1106,10 +1119,12 @@ int fdt_size_cells(const void *fdt, int nodeoffset);
  * of the name. It is useful when you want to manipulate only one value of
  * an array and you have a string that doesn't end with \0.
  */
+#ifndef SWIG /* Not available in Python */
 int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
 					const char *name, int namelen,
 					uint32_t idx, const void *val,
 					int len);
+#endif
 
 /**
  * fdt_setprop_inplace - change a property's value, but not its size
@@ -1139,8 +1154,10 @@ int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
  *	-FDT_ERR_BADSTRUCTURE,
  *	-FDT_ERR_TRUNCATED, standard meanings
  */
+#ifndef SWIG /* Not available in Python */
 int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
 			const void *val, int len);
+#endif
 
 /**
  * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property
@@ -1734,8 +1751,10 @@ int fdt_delprop(void *fdt, int nodeoffset, const char *name);
  * creating subnodes based on a portion of a larger string, such as a
  * full path.
  */
+#ifndef SWIG /* Not available in Python */
 int fdt_add_subnode_namelen(void *fdt, int parentoffset,
 			    const char *name, int namelen);
+#endif
 
 /**
  * fdt_add_subnode - creates a new node
-- 
2.13.0

From 756ffc4f52f6863ba8bf3a67129271566ba2000c Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 17 Mar 2017 16:14:34 -0600
Subject: [PATCH 05/29] Build pylibfdt as part of the normal build process

If swig and the Python are available, build pylibfdt automatically.
Adjust the tests to run Python tests too in this case.

Signed-off-by: Simon Glass <sjg@chromium.org>
[dwg: Make error message clearer that missing swig or python-dev isn't
 fatal to the whole build]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile           | 34 ++++++++++++++++++++++++++++++++--
 tests/run_tests.sh | 28 ++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index 1b69f53..ed95384 100644
--- a/Makefile
+++ b/Makefile
@@ -116,7 +116,21 @@ BIN += fdtput
 
 SCRIPTS = dtdiff
 
-all: $(BIN) libfdt
+# We need both Python and swig to build pylibfdt.
+.PHONY: maybe_pylibfdt
+maybe_pylibfdt: FORCE
+	if pkg-config --cflags python >/dev/null 2>&1; then \
+		if which swig >/dev/null 2>&1; then \
+			can_build=yes; \
+		fi; \
+	fi; \
+	if [ "$$can_build" = "yes" ]; then \
+		$(MAKE) pylibfdt; \
+	else \
+		echo "## Skipping pylibgfdt (install python dev and swig to build)"; \
+	fi
+
+all: $(BIN) libfdt maybe_pylibfdt
 
 
 ifneq ($(DEPTARGETS),)
@@ -203,6 +217,22 @@ dist:
 	cat ../dtc-$(dtc_version).tar | \
 		gzip -9 > ../dtc-$(dtc_version).tar.gz
 
+
+#
+# Rules for pylibfdt
+#
+PYLIBFDT_srcdir = pylibfdt
+PYLIBFDT_objdir = pylibfdt
+
+include $(PYLIBFDT_srcdir)/Makefile.pylibfdt
+
+.PHONY: pylibfdt
+pylibfdt: $(PYLIBFDT_objdir)/_libfdt.so
+
+pylibfdt_clean:
+	@$(VECHO) CLEAN "(pylibfdt)"
+	rm -f $(addprefix $(PYLIBFDT_objdir)/,$(PYLIBFDT_cleanfiles))
+
 #
 # Release signing and uploading
 # This is for maintainer convenience, don't try this at home.
@@ -244,7 +274,7 @@ include tests/Makefile.tests
 STD_CLEANFILES = *~ *.o *.$(SHAREDLIB_EXT) *.d *.a *.i *.s core a.out vgcore.* \
 	*.tab.[ch] *.lex.c *.output
 
-clean: libfdt_clean tests_clean
+clean: libfdt_clean pylibfdt_clean tests_clean
 	@$(VECHO) CLEAN
 	rm -f $(STD_CLEANFILES)
 	rm -f $(VERSION_FILE)
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 0f5c3db..2a1ba44 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -771,6 +771,26 @@ fdtdump_tests () {
     run_fdtdump_test fdtdump.dts
 }
 
+pylibfdt_tests () {
+    TMP=/tmp/tests.stderr.$$
+    python pylibfdt_tests.py -v 2> $TMP
+
+    # Use the 'ok' message meaning the test passed, 'ERROR' meaning it failed
+    # and the summary line for total tests (e.g. 'Ran 17 tests in 0.002s').
+    # We could add pass + fail to get total tests, but this provides a useful
+    # sanity check.
+    pass_count=$(grep "\.\.\. ok$" $TMP | wc -l)
+    fail_count=$(grep "^ERROR: " $TMP | wc -l)
+    total_tests=$(sed -n 's/^Ran \([0-9]*\) tests.*$/\1/p' $TMP)
+    cat $TMP
+    rm $TMP
+
+    # Extract the test results and add them to our totals
+    tot_fail=$((tot_fail + $fail_count))
+    tot_pass=$((tot_pass + $pass_count))
+    tot_tests=$((tot_tests + $total_tests))
+}
+
 while getopts "vt:me" ARG ; do
     case $ARG in
 	"v")
@@ -790,6 +810,11 @@ done
 
 if [ -z "$TESTSETS" ]; then
     TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump"
+
+    # Test pylibfdt if the libfdt Python module is available.
+    if [ -f ../pylibfdt/_libfdt.so ]; then
+        TESTSETS="$TESTSETS pylibfdt"
+    fi
 fi
 
 # Make sure we don't have stale blobs lying around
@@ -818,6 +843,9 @@ for set in $TESTSETS; do
 	"fdtdump")
 	    fdtdump_tests
 	    ;;
+	"pylibfdt")
+	    pylibfdt_tests
+	    ;;
     esac
 done
 
-- 
2.13.0

From 6afd7d9688f58436bcc6025180473aa2ec1cdec4 Mon Sep 17 00:00:00 2001
From: David Gibson <david@gibson.dropbear.id.au>
Date: Wed, 22 Mar 2017 16:34:39 +1100
Subject: [PATCH 06/29] Correct typo: s/pylibgfdt/pylibfdt/

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index ed95384..1d08ec1 100644
--- a/Makefile
+++ b/Makefile
@@ -127,7 +127,7 @@ maybe_pylibfdt: FORCE
 	if [ "$$can_build" = "yes" ]; then \
 		$(MAKE) pylibfdt; \
 	else \
-		echo "## Skipping pylibgfdt (install python dev and swig to build)"; \
+		echo "## Skipping pylibfdt (install python dev and swig to build)"; \
 	fi
 
 all: $(BIN) libfdt maybe_pylibfdt
-- 
2.13.0

From 4e0e0d049757b15d53209a9687d9ea33ab3704c5 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sun, 26 Mar 2017 13:06:17 -0600
Subject: [PATCH 07/29] pylibfdt: Allow pkg-config to be supplied in the
 environment

Some build systems have their own version of the pkg-config tool.
Use a variable for this instead of hard-coding it, to allow for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
Suggested-by: Mike Frysinger <vapier@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 1d08ec1..e6d8251 100644
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,7 @@ CFLAGS = -g -Os -fPIC -Werror $(WARNINGS)
 BISON = bison
 LEX = flex
 SWIG = swig
+PKG_CONFIG ?= pkg-config
 
 INSTALL = /usr/bin/install
 DESTDIR =
@@ -119,7 +120,7 @@ SCRIPTS = dtdiff
 # We need both Python and swig to build pylibfdt.
 .PHONY: maybe_pylibfdt
 maybe_pylibfdt: FORCE
-	if pkg-config --cflags python >/dev/null 2>&1; then \
+	if $(PKG_CONFIG) --cflags python >/dev/null 2>&1; then \
 		if which swig >/dev/null 2>&1; then \
 			can_build=yes; \
 		fi; \
-- 
2.13.0

From 89a5062ab23163a7cc4f6ec3d693e6b6883ac0a1 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sun, 26 Mar 2017 13:06:19 -0600
Subject: [PATCH 08/29] pylibfdt: Use environment to pass C flags and files

At present setup.py adjusts its command line when running, so that the
C flags and file list can be passed as arguments. Pass them in environment
variables instead, so we can avoid this messiness. It also allows us to
support the 'install' command.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pylibfdt/Makefile.pylibfdt |  3 ++-
 pylibfdt/setup.py          | 16 ++++++----------
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index 0d8c010..3d99fd4 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -7,7 +7,8 @@ PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
 
 $(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
 	@$(VECHO) PYMOD $@
-	python $(PYLIBFDT_objdir)/setup.py "$(CPPFLAGS)" $^
+	SOURCES="$^" CPPFLAGS="$(CPPFLAGS)" \
+	python $(PYLIBFDT_objdir)/setup.py --quiet build_ext --inplace
 	mv _libfdt.so $(PYMODULE)
 
 $(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
diff --git a/pylibfdt/setup.py b/pylibfdt/setup.py
index 0ff160c..e45f110 100644
--- a/pylibfdt/setup.py
+++ b/pylibfdt/setup.py
@@ -2,6 +2,9 @@
 
 """
 setup.py file for SWIG libfdt
+
+Files to be built into the extension are provided in SOURCES
+C flags to use are provided in CPPFLAGS
 """
 
 from distutils.core import setup, Extension
@@ -9,22 +12,15 @@ import os
 import sys
 
 progname = sys.argv[0]
-cflags = sys.argv[1]
-files = sys.argv[2:]
-
-if cflags:
-    cflags = [flag for flag in cflags.split(' ') if flag]
-else:
-    cflags = None
+files = os.environ['SOURCES'].split()
+cflags = os.environ['CPPFLAGS'].split()
 
 libfdt_module = Extension(
     '_libfdt',
     sources = files,
-    extra_compile_args =  cflags
+    extra_compile_args = cflags
 )
 
-sys.argv = [progname, '--quiet', 'build_ext', '--inplace']
-
 setup (name = 'libfdt',
        version = '0.1',
        author      = "Simon Glass <sjg@chromium.org>",
-- 
2.13.0

From 14c4171f4f9ad2674249e06c54eb9ce0b533d4b6 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sun, 26 Mar 2017 13:06:20 -0600
Subject: [PATCH 09/29] pylibfdt: Use package_dir to set the package directory

At present we manually move _libfdt.so into the correct place. Provide a
package directory so we can avoid needing to do this.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pylibfdt/Makefile.pylibfdt | 3 +--
 pylibfdt/setup.py          | 3 +++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index 3d99fd4..861e67c 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -7,9 +7,8 @@ PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
 
 $(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
 	@$(VECHO) PYMOD $@
-	SOURCES="$^" CPPFLAGS="$(CPPFLAGS)" \
+	SOURCES="$^" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)" \
 	python $(PYLIBFDT_objdir)/setup.py --quiet build_ext --inplace
-	mv _libfdt.so $(PYMODULE)
 
 $(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
 	@$(VECHO) SWIG $@
diff --git a/pylibfdt/setup.py b/pylibfdt/setup.py
index e45f110..ef6e2c0 100644
--- a/pylibfdt/setup.py
+++ b/pylibfdt/setup.py
@@ -5,6 +5,7 @@ setup.py file for SWIG libfdt
 
 Files to be built into the extension are provided in SOURCES
 C flags to use are provided in CPPFLAGS
+Object file directory is provided in OBJDIR
 """
 
 from distutils.core import setup, Extension
@@ -14,6 +15,7 @@ import sys
 progname = sys.argv[0]
 files = os.environ['SOURCES'].split()
 cflags = os.environ['CPPFLAGS'].split()
+objdir = os.environ['OBJDIR']
 
 libfdt_module = Extension(
     '_libfdt',
@@ -26,5 +28,6 @@ setup (name = 'libfdt',
        author      = "Simon Glass <sjg@chromium.org>",
        description = """Python binding for libfdt""",
        ext_modules = [libfdt_module],
+       package_dir = {'': objdir},
        py_modules = ["libfdt"],
        )
-- 
2.13.0

From 8a892fd85d94c733bbf184ff6df5d0ad5422be12 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sun, 26 Mar 2017 13:06:18 -0600
Subject: [PATCH 11/29] pylibfdt: Allow building to be disabled

Some build systems want to build python libraries separately from the
rest of the build.

Add a NO_PYTHON option to enable this.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile | 1 +
 README   | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/Makefile b/Makefile
index e6d8251..5cf4aee 100644
--- a/Makefile
+++ b/Makefile
@@ -120,6 +120,7 @@ SCRIPTS = dtdiff
 # We need both Python and swig to build pylibfdt.
 .PHONY: maybe_pylibfdt
 maybe_pylibfdt: FORCE
+	if [ -n "${NO_PYTHON}" ]; then exit; fi; \
 	if $(PKG_CONFIG) --cflags python >/dev/null 2>&1; then \
 		if which swig >/dev/null 2>&1; then \
 			can_build=yes; \
diff --git a/README b/README
index 96d8486..d2323fd 100644
--- a/README
+++ b/README
@@ -50,6 +50,12 @@ If you add new features, please check code coverage:
     # Open 'htmlcov/index.html' in your browser
 
 
+To disable building the python library, even if swig and Python are available,
+use:
+
+    make NO_PYTHON=1
+
+
 More work remains to support all of libfdt, including access to numeric
 values.
 
-- 
2.13.0

From e91c652af21557698751c3944ceb7c46e5e58164 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sun, 26 Mar 2017 13:06:21 -0600
Subject: [PATCH 12/29] pylibfdt: Enable installation of Python module

Adjust the setup script to support installation, and call it from the
Makefile if enabled. It will be disabled if we were unable to build the
module (e.g. due to swig being missing), or the NO_PYTHON environment
variable is set.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile                   |  2 +-
 README                     |  7 +++++++
 pylibfdt/Makefile.pylibfdt | 14 ++++++++++++++
 3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 5cf4aee..52ff72c 100644
--- a/Makefile
+++ b/Makefile
@@ -195,7 +195,7 @@ install-includes:
 	$(INSTALL) -d $(DESTDIR)$(INCLUDEDIR)
 	$(INSTALL) -m 644 $(LIBFDT_include) $(DESTDIR)$(INCLUDEDIR)
 
-install: install-bin install-lib install-includes
+install: install-bin install-lib install-includes maybe_install_pylibfdt
 
 $(VERSION_FILE): Makefile FORCE
 	$(call filechk,version)
diff --git a/README b/README
index d2323fd..5add557 100644
--- a/README
+++ b/README
@@ -50,6 +50,13 @@ If you add new features, please check code coverage:
     # Open 'htmlcov/index.html' in your browser
 
 
+To install the library use:
+
+    make install_pylibfdt SETUP_PREFIX=/path/to/install_dir
+
+If SETUP_PREFIX is not provided, the default prefix is used, typically '/usr'
+or '/usr/local'. See Python's distutils documentation for details.
+
 To disable building the python library, even if swig and Python are available,
 use:
 
diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index 861e67c..a0271da 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -14,4 +14,18 @@ $(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
 	@$(VECHO) SWIG $@
 	$(SWIG) -python -o $@ $<
 
+install_pylibfdt: $(WRAP) $(PYMODULE)
+	$(VECHO) INSTALL-PYLIB; \
+	SOURCES="$(PYLIBFDT_srcs) $(WRAP)" CPPFLAGS="$(CPPFLAGS)" \
+	OBJDIR="$(PYLIBFDT_objdir)" \
+	python $(PYLIBFDT_objdir)/setup.py --quiet install \
+		$(if $(SETUP_PREFIX),--prefix=$(SETUP_PREFIX))
+
+maybe_install_pylibfdt:
+	if [ -e $(PYMODULE) ]; then \
+		if [ -z "$(NO_PYTHON)" ]; then \
+			$(MAKE) install_pylibfdt; \
+		fi; \
+	fi
+
 PYLIBFDT_cleanfiles = libfdt_wrap.c libfdt.py libfdt.pyc _libfdt.so
-- 
2.13.0

From 9f2e3a3a1f19b569b9524fa0f4cb4790e23ad983 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sun, 26 Mar 2017 13:06:22 -0600
Subject: [PATCH 13/29] pylibfdt: Use the correct libfdt version in the module

Use the same version number in the module as with the rest of libfdt. This
can be examined with:

   import pkg_resources
   print pkg_resources.require('libfdt')[0].version

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pylibfdt/Makefile.pylibfdt | 3 ++-
 pylibfdt/setup.py          | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index a0271da..a74cd30 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -8,6 +8,7 @@ PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
 $(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
 	@$(VECHO) PYMOD $@
 	SOURCES="$^" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)" \
+	VERSION="$(dtc_version)" \
 	python $(PYLIBFDT_objdir)/setup.py --quiet build_ext --inplace
 
 $(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
@@ -17,7 +18,7 @@ $(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
 install_pylibfdt: $(WRAP) $(PYMODULE)
 	$(VECHO) INSTALL-PYLIB; \
 	SOURCES="$(PYLIBFDT_srcs) $(WRAP)" CPPFLAGS="$(CPPFLAGS)" \
-	OBJDIR="$(PYLIBFDT_objdir)" \
+	OBJDIR="$(PYLIBFDT_objdir)" VERSION="$(dtc_version)" \
 	python $(PYLIBFDT_objdir)/setup.py --quiet install \
 		$(if $(SETUP_PREFIX),--prefix=$(SETUP_PREFIX))
 
diff --git a/pylibfdt/setup.py b/pylibfdt/setup.py
index ef6e2c0..3bafe30 100644
--- a/pylibfdt/setup.py
+++ b/pylibfdt/setup.py
@@ -16,6 +16,7 @@ progname = sys.argv[0]
 files = os.environ['SOURCES'].split()
 cflags = os.environ['CPPFLAGS'].split()
 objdir = os.environ['OBJDIR']
+version = os.environ['VERSION']
 
 libfdt_module = Extension(
     '_libfdt',
@@ -24,7 +25,7 @@ libfdt_module = Extension(
 )
 
 setup (name = 'libfdt',
-       version = '0.1',
+       version = version,
        author      = "Simon Glass <sjg@chromium.org>",
        description = """Python binding for libfdt""",
        ext_modules = [libfdt_module],
-- 
2.13.0

From ab15256d8d027fc379438a18a8bd85e7765557c6 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sun, 26 Mar 2017 13:06:23 -0600
Subject: [PATCH 14/29] pylibfdt: Use the call function to simplify the
 Makefile

This is in a separate patch since I not sure if GNU make features
are permitted in the Makefile.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pylibfdt/Makefile.pylibfdt | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index a74cd30..0d95c11 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -5,11 +5,13 @@ PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS))
 WRAP = $(PYLIBFDT_objdir)/libfdt_wrap.c
 PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
 
+run_setup = SOURCES="$(1)" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)" \
+	VERSION="$(dtc_version)" \
+	python $(PYLIBFDT_objdir)/setup.py --quiet $(2)
+
 $(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
 	@$(VECHO) PYMOD $@
-	SOURCES="$^" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)" \
-	VERSION="$(dtc_version)" \
-	python $(PYLIBFDT_objdir)/setup.py --quiet build_ext --inplace
+	$(call run_setup, $^, build_ext --inplace)
 
 $(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
 	@$(VECHO) SWIG $@
@@ -17,10 +19,8 @@ $(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
 
 install_pylibfdt: $(WRAP) $(PYMODULE)
 	$(VECHO) INSTALL-PYLIB; \
-	SOURCES="$(PYLIBFDT_srcs) $(WRAP)" CPPFLAGS="$(CPPFLAGS)" \
-	OBJDIR="$(PYLIBFDT_objdir)" VERSION="$(dtc_version)" \
-	python $(PYLIBFDT_objdir)/setup.py --quiet install \
-		$(if $(SETUP_PREFIX),--prefix=$(SETUP_PREFIX))
+	$(call run_setup, $(PYLIBFDT_srcs) $(WRAP), \
+		install $(if $(SETUP_PREFIX),--prefix=$(SETUP_PREFIX)))
 
 maybe_install_pylibfdt:
 	if [ -e $(PYMODULE) ]; then \
-- 
2.13.0

From 580a9f6c288079e952eae6707c267644338f7c7b Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Sat, 1 Apr 2017 09:31:41 -0600
Subject: [PATCH 15/29] Add a libfdt function to write a property placeholder

The existing function to add a new property to a tree being built requires
that the entire contents of the new property be passed in. For some
applications it is more convenient to be able to add the property contents
later, perhaps by reading from a file. This avoids double-buffering of the
contents.

Add a new function to support this and adjust the existing fdt_property() to
use it.
Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 libfdt/fdt_sw.c                    | 16 ++++++++++++++--
 libfdt/libfdt.h                    | 16 ++++++++++++++++
 tests/include7.dts                 |  1 +
 tests/sw_tree1.c                   |  5 +++++
 tests/test_tree1.dts               |  1 +
 tests/test_tree1_label_noderef.dts |  1 +
 tests/trees.S                      |  2 ++
 7 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c
index 6a80485..2bd15e7 100644
--- a/libfdt/fdt_sw.c
+++ b/libfdt/fdt_sw.c
@@ -220,7 +220,7 @@ static int _fdt_find_add_string(void *fdt, const char *s)
 	return offset;
 }
 
-int fdt_property(void *fdt, const char *name, const void *val, int len)
+int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
 {
 	struct fdt_property *prop;
 	int nameoff;
@@ -238,7 +238,19 @@ int fdt_property(void *fdt, const char *name, const void *val, int len)
 	prop->tag = cpu_to_fdt32(FDT_PROP);
 	prop->nameoff = cpu_to_fdt32(nameoff);
 	prop->len = cpu_to_fdt32(len);
-	memcpy(prop->data, val, len);
+	*valp = prop->data;
+	return 0;
+}
+
+int fdt_property(void *fdt, const char *name, const void *val, int len)
+{
+	void *ptr;
+	int ret;
+
+	ret = fdt_property_placeholder(fdt, name, len, &ptr);
+	if (ret)
+		return ret;
+	memcpy(ptr, val, len);
 	return 0;
 }
 
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index 2c9ddb4..a248b1b 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -1314,6 +1314,22 @@ static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
 {
 	return fdt_property_u32(fdt, name, val);
 }
+
+/**
+ * fdt_property_placeholder - add a new property and return a ptr to its value
+ *
+ * @fdt: pointer to the device tree blob
+ * @name: name of property to add
+ * @len: length of property value in bytes
+ * @valp: returns a pointer to where where the value should be placed
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_NOSPACE, standard meanings
+ */
+int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp);
+
 #define fdt_property_string(fdt, name, str) \
 	fdt_property(fdt, name, str, strlen(str)+1)
 int fdt_end_node(void *fdt);
diff --git a/tests/include7.dts b/tests/include7.dts
index 2f6eb89..ab2c948 100644
--- a/tests/include7.dts
+++ b/tests/include7.dts
@@ -5,6 +5,7 @@
 
 		subsubnode {
 			compatible = "subsubnode1", "subsubnode";
+			placeholder = "this is a placeholder string", "string2";
 			prop-int = <0xdeadbeef>;
 		};
 
diff --git a/tests/sw_tree1.c b/tests/sw_tree1.c
index 4887dc3..6a338fc 100644
--- a/tests/sw_tree1.c
+++ b/tests/sw_tree1.c
@@ -85,6 +85,9 @@ int main(int argc, char *argv[])
 	size_t size;
 	int err;
 	bool created = false;
+	void *place;
+	const char place_str[] = "this is a placeholder string\0string2";
+	int place_len = sizeof(place_str);
 
 	test_init(argc, argv);
 
@@ -135,6 +138,8 @@ int main(int argc, char *argv[])
 	CHECK(fdt_begin_node(fdt, "subsubnode"));
 	CHECK(fdt_property(fdt, "compatible", "subsubnode1\0subsubnode",
 			   23));
+	CHECK(fdt_property_placeholder(fdt, "placeholder", place_len, &place));
+	memcpy(place, place_str, place_len);
 	CHECK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_1));
 	CHECK(fdt_end_node(fdt));
 	CHECK(fdt_begin_node(fdt, "ss1"));
diff --git a/tests/test_tree1.dts b/tests/test_tree1.dts
index 67ecfd0..77ea325 100644
--- a/tests/test_tree1.dts
+++ b/tests/test_tree1.dts
@@ -18,6 +18,7 @@
 
 		subsubnode {
 			compatible = "subsubnode1", "subsubnode";
+			placeholder = "this is a placeholder string", "string2";
 			prop-int = <0xdeadbeef>;
 		};
 
diff --git a/tests/test_tree1_label_noderef.dts b/tests/test_tree1_label_noderef.dts
index b2b194c..cfe5946 100644
--- a/tests/test_tree1_label_noderef.dts
+++ b/tests/test_tree1_label_noderef.dts
@@ -18,6 +18,7 @@
 
 		subsubnode {
 			compatible = "subsubnode1", "subsubnode";
+			placeholder = "this is a placeholder string", "string2";
 			prop-int = <0xdeadbeef>;
 		};
 
diff --git a/tests/trees.S b/tests/trees.S
index 3d24aa2..9854d1d 100644
--- a/tests/trees.S
+++ b/tests/trees.S
@@ -102,6 +102,7 @@ test_tree1_struct:
 
 	BEGIN_NODE("subsubnode")
 	PROP_STR(test_tree1, compatible, "subsubnode1\0subsubnode")
+	PROP_STR(test_tree1, placeholder, "this is a placeholder string\0string2")
 	PROP_INT(test_tree1, prop_int, TEST_VALUE_1)
 	END_NODE
 
@@ -141,6 +142,7 @@ test_tree1_strings:
 	STRING(test_tree1, linux_phandle, "linux,phandle")
 	STRING(test_tree1, phandle, "phandle")
 	STRING(test_tree1, reg, "reg")
+	STRING(test_tree1, placeholder, "placeholder")
 	STRING(test_tree1, address_cells, "#address-cells")
 	STRING(test_tree1, size_cells, "#size-cells")
 test_tree1_strings_end:
-- 
2.13.0

From 1c5170d3a466dc96ec67c08f71a570631b404c62 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Wed, 5 Apr 2017 10:01:38 -0600
Subject: [PATCH 16/29] pylibfdt: Rename libfdt.swig to libfdt.i

The .i extension allows Python distutils to automatically handle the swig
file. Rename it.

Signed-off-by: Simon Glass <sjg@chromium.org>
Suggested-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pylibfdt/Makefile.pylibfdt         | 2 +-
 pylibfdt/{libfdt.swig => libfdt.i} | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename pylibfdt/{libfdt.swig => libfdt.i} (100%)

diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index 0d95c11..06f9296 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -13,7 +13,7 @@ $(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
 	@$(VECHO) PYMOD $@
 	$(call run_setup, $^, build_ext --inplace)
 
-$(WRAP): $(PYLIBFDT_srcdir)/libfdt.swig
+$(WRAP): $(PYLIBFDT_srcdir)/libfdt.i
 	@$(VECHO) SWIG $@
 	$(SWIG) -python -o $@ $<
 
diff --git a/pylibfdt/libfdt.swig b/pylibfdt/libfdt.i
similarity index 100%
rename from pylibfdt/libfdt.swig
rename to pylibfdt/libfdt.i
-- 
2.13.0

From b04a2cf08862ddac1ff7da40eef58ecb360da941 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Wed, 5 Apr 2017 10:01:39 -0600
Subject: [PATCH 17/29] pylibfdt: Fix code style in setup.py

We should follow PEP8 even for our setup() call.

Signed-off-by: Simon Glass <sjg@chromium.org>
Suggested-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pylibfdt/setup.py | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/pylibfdt/setup.py b/pylibfdt/setup.py
index 3bafe30..1597b44 100644
--- a/pylibfdt/setup.py
+++ b/pylibfdt/setup.py
@@ -24,11 +24,12 @@ libfdt_module = Extension(
     extra_compile_args = cflags
 )
 
-setup (name = 'libfdt',
-       version = version,
-       author      = "Simon Glass <sjg@chromium.org>",
-       description = """Python binding for libfdt""",
-       ext_modules = [libfdt_module],
-       package_dir = {'': objdir},
-       py_modules = ["libfdt"],
-       )
+setup(
+    name='libfdt',
+    version= version,
+    author='Simon Glass <sjg@chromium.org>',
+    description='Python binding for libfdt',
+    ext_modules=[libfdt_module],
+    package_dir={'': objdir},
+    py_modules=['libfdt'],
+)
-- 
2.13.0

From 90db6d9989ca09ed3c32fbdc646d284ebf9fe1cf Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 7 Apr 2017 15:51:32 -0600
Subject: [PATCH 19/29] pylibfdt: Allow setup.py to operate stand-alone

At present we require that setup.py is executed from the Makefile, which
sets up various important things like the list of files to build and the
version number.

However many installation systems expect to be able to change to the
directory containing setup.py and run it. This allows them to support (for
example) building/installing for multiple Python versions, varying
installation paths, particular C flags, etc.

The problem in implementing this is that we don't want to duplicate the
information in the Makefile. A common solution (so I am told) is to parse
the Makefile to obtain the required information.

Update the setup.py script to read a few Makefiles when it does not see
the required information in its environment. This allows installation
using:

   ./pylibfdt/setup.py install

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile                   |  1 +
 README                     | 14 +++++--
 pylibfdt/Makefile.pylibfdt |  9 +++--
 pylibfdt/setup.py          | 98 +++++++++++++++++++++++++++++++++++++++++++---
 tests/Makefile.tests       |  6 +--
 5 files changed, 112 insertions(+), 16 deletions(-)
 mode change 100644 => 100755 pylibfdt/setup.py

diff --git a/Makefile b/Makefile
index 52ff72c..154f5bf 100644
--- a/Makefile
+++ b/Makefile
@@ -267,6 +267,7 @@ TESTS_BIN += convert-dtsv0
 TESTS_BIN += fdtput
 TESTS_BIN += fdtget
 TESTS_BIN += fdtdump
+TESTS_PYLIBFDT += maybe_pylibfdt
 
 include tests/Makefile.tests
 
diff --git a/README b/README
index 5add557..17dc845 100644
--- a/README
+++ b/README
@@ -50,12 +50,18 @@ If you add new features, please check code coverage:
     # Open 'htmlcov/index.html' in your browser
 
 
-To install the library use:
+To install the library via the normal setup.py method, use:
 
-    make install_pylibfdt SETUP_PREFIX=/path/to/install_dir
+    ./pylibfdt/setup.py [--prefix=/path/to/install_dir]
 
-If SETUP_PREFIX is not provided, the default prefix is used, typically '/usr'
-or '/usr/local'. See Python's distutils documentation for details.
+If --prefix is not provided, the default prefix is used, typically '/usr'
+or '/usr/local'. See Python's distutils documentation for details. You can
+also install via the Makefile if you like, but the above is more common.
+
+To install both libfdt and pylibfdt you can use:
+
+    make install [SETUP_PREFIX=/path/to/install_dir] \
+            [PREFIX=/path/to/install_dir]
 
 To disable building the python library, even if swig and Python are available,
 use:
diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index 06f9296..0e8ac5f 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -5,13 +5,16 @@ PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS))
 WRAP = $(PYLIBFDT_objdir)/libfdt_wrap.c
 PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
 
-run_setup = SOURCES="$(1)" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)" \
-	VERSION="$(dtc_version)" \
-	python $(PYLIBFDT_objdir)/setup.py --quiet $(2)
+define run_setup
+	SOURCES="$(1)" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)"
+	VERSION="$(dtc_version)"
+	$(PYLIBFDT_objdir)/setup.py --quiet $(2)
+endef
 
 $(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
 	@$(VECHO) PYMOD $@
 	$(call run_setup, $^, build_ext --inplace)
+	mv _libfdt.so $@
 
 $(WRAP): $(PYLIBFDT_srcdir)/libfdt.i
 	@$(VECHO) SWIG $@
diff --git a/pylibfdt/setup.py b/pylibfdt/setup.py
old mode 100644
new mode 100755
index 1597b44..90e80f3
--- a/pylibfdt/setup.py
+++ b/pylibfdt/setup.py
@@ -2,26 +2,112 @@
 
 """
 setup.py file for SWIG libfdt
+Copyright (C) 2017 Google, Inc.
+Written by Simon Glass <sjg@chromium.org>
 
 Files to be built into the extension are provided in SOURCES
 C flags to use are provided in CPPFLAGS
 Object file directory is provided in OBJDIR
+Version is provided in VERSION
+
+If these variables are not given they are parsed from the Makefiles. This
+allows this script to be run stand-alone, e.g.:
+
+    ./pylibfdt/setup.py install [--prefix=...]
 """
 
 from distutils.core import setup, Extension
 import os
+import re
 import sys
 
+# Decodes a Makefile assignment line into key and value (and plus for +=)
+RE_KEY_VALUE = re.compile('(?P<key>\w+) *(?P<plus>[+])?= *(?P<value>.*)$')
+
+
+def ParseMakefile(fname):
+    """Parse a Makefile to obtain its variables.
+
+    This collects variable assigments of the form:
+
+        VAR = value
+        VAR += more
+
+    It does not pick out := assignments, as these are not needed here. It does
+    handle line continuation.
+
+    Returns a dict:
+        key: Variable name (e.g. 'VAR')
+        value: Variable value (e.g. 'value more')
+    """
+    makevars = {}
+    with open(fname) as fd:
+        prev_text = ''  # Continuation text from previous line(s)
+        for line in fd.read().splitlines():
+          if line and line[-1] == '\\':  # Deal with line continuation
+            prev_text += line[:-1]
+            continue
+          elif prev_text:
+            line = prev_text + line
+            prev_text = ''  # Continuation is now used up
+          m = RE_KEY_VALUE.match(line)
+          if m:
+            value = m.group('value') or ''
+            key = m.group('key')
+
+            # Appending to a variable inserts a space beforehand
+            if 'plus' in m.groupdict() and key in makevars:
+              makevars[key] += ' ' + value
+            else:
+              makevars[key] = value
+    return makevars
+
+def GetEnvFromMakefiles():
+    """Scan the Makefiles to obtain the settings we need.
+
+    This assumes that this script is being run from the top-level directory,
+    not the pylibfdt directory.
+
+    Returns:
+        Tuple with:
+            List of swig options
+            Version string
+            List of files to build
+            List of extra C preprocessor flags needed
+            Object directory to use (always '')
+    """
+    basedir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+    swig_opts = ['-I%s' % basedir]
+    makevars = ParseMakefile(os.path.join(basedir, 'Makefile'))
+    version = '%s.%s.%s' % (makevars['VERSION'], makevars['PATCHLEVEL'],
+                            makevars['SUBLEVEL'])
+    makevars = ParseMakefile(os.path.join(basedir, 'libfdt', 'Makefile.libfdt'))
+    files = makevars['LIBFDT_SRCS'].split()
+    files = [os.path.join(basedir, 'libfdt', fname) for fname in files]
+    files.append('pylibfdt/libfdt.i')
+    cflags = ['-I%s' % basedir, '-I%s/libfdt' % basedir]
+    objdir = ''
+    return swig_opts, version, files, cflags, objdir
+
+
 progname = sys.argv[0]
-files = os.environ['SOURCES'].split()
-cflags = os.environ['CPPFLAGS'].split()
-objdir = os.environ['OBJDIR']
-version = os.environ['VERSION']
+files = os.environ.get('SOURCES', '').split()
+cflags = os.environ.get('CPPFLAGS', '').split()
+objdir = os.environ.get('OBJDIR')
+version = os.environ.get('VERSION')
+swig_opts = []
+
+# If we were called directly rather than through our Makefile (which is often
+# the case with Python module installation), read the settings from the
+# Makefile.
+if not all((version, files, cflags, objdir)):
+    swig_opts, version, files, cflags, objdir = GetEnvFromMakefiles()
 
 libfdt_module = Extension(
     '_libfdt',
     sources = files,
-    extra_compile_args = cflags
+    extra_compile_args = cflags,
+    swig_opts = swig_opts,
 )
 
 setup(
@@ -31,5 +117,5 @@ setup(
     description='Python binding for libfdt',
     ext_modules=[libfdt_module],
     package_dir={'': objdir},
-    py_modules=['libfdt'],
+    py_modules=['pylibfdt/libfdt'],
 )
diff --git a/tests/Makefile.tests b/tests/Makefile.tests
index 3d7a4f8..2258135 100644
--- a/tests/Makefile.tests
+++ b/tests/Makefile.tests
@@ -72,13 +72,13 @@ tests_clean:
 	rm -f $(STD_CLEANFILES:%=$(TESTS_PREFIX)%)
 	rm -f $(TESTS_CLEANFILES)
 
-check:	tests ${TESTS_BIN}
+check:	tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
 	cd $(TESTS_PREFIX); ./run_tests.sh
 
-checkm: tests ${TESTS_BIN}
+checkm: tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
 	cd $(TESTS_PREFIX); ./run_tests.sh -m 2>&1 | tee vglog.$$$$
 
-checkv:	tests ${TESTS_BIN}
+checkv:	tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
 	cd $(TESTS_PREFIX); ./run_tests.sh -v
 
 ifneq ($(DEPTARGETS),)
-- 
2.13.0

From 896f1c1332650f5370a21c1c507106a87d17fd3d Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 7 Apr 2017 15:51:33 -0600
Subject: [PATCH 20/29] pylibfdt: Use Makefile constructs to implement
 NO_PYTHON

The current mechanism uses a shell construct, but it seems better to use
a Makefile approach.

Signed-off-by: Simon Glass <sjg@chromium.org>
Suggested-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile                   | 15 ++++++++++++---
 pylibfdt/Makefile.pylibfdt |  7 -------
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/Makefile b/Makefile
index 154f5bf..beca4a0 100644
--- a/Makefile
+++ b/Makefile
@@ -117,10 +117,11 @@ BIN += fdtput
 
 SCRIPTS = dtdiff
 
+all: $(BIN) libfdt
+
 # We need both Python and swig to build pylibfdt.
 .PHONY: maybe_pylibfdt
 maybe_pylibfdt: FORCE
-	if [ -n "${NO_PYTHON}" ]; then exit; fi; \
 	if $(PKG_CONFIG) --cflags python >/dev/null 2>&1; then \
 		if which swig >/dev/null 2>&1; then \
 			can_build=yes; \
@@ -132,7 +133,9 @@ maybe_pylibfdt: FORCE
 		echo "## Skipping pylibfdt (install python dev and swig to build)"; \
 	fi
 
-all: $(BIN) libfdt maybe_pylibfdt
+ifeq ($(NO_PYTHON),)
+all: maybe_pylibfdt
+endif
 
 
 ifneq ($(DEPTARGETS),)
@@ -195,7 +198,11 @@ install-includes:
 	$(INSTALL) -d $(DESTDIR)$(INCLUDEDIR)
 	$(INSTALL) -m 644 $(LIBFDT_include) $(DESTDIR)$(INCLUDEDIR)
 
-install: install-bin install-lib install-includes maybe_install_pylibfdt
+install: install-bin install-lib install-includes
+
+ifeq ($(NO_PYTHON),)
+install: install_pylibfdt
+endif
 
 $(VERSION_FILE): Makefile FORCE
 	$(call filechk,version)
@@ -267,7 +274,9 @@ TESTS_BIN += convert-dtsv0
 TESTS_BIN += fdtput
 TESTS_BIN += fdtget
 TESTS_BIN += fdtdump
+ifeq ($(NO_PYTHON),)
 TESTS_PYLIBFDT += maybe_pylibfdt
+endif
 
 include tests/Makefile.tests
 
diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index 0e8ac5f..4036b1f 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -25,11 +25,4 @@ install_pylibfdt: $(WRAP) $(PYMODULE)
 	$(call run_setup, $(PYLIBFDT_srcs) $(WRAP), \
 		install $(if $(SETUP_PREFIX),--prefix=$(SETUP_PREFIX)))
 
-maybe_install_pylibfdt:
-	if [ -e $(PYMODULE) ]; then \
-		if [ -z "$(NO_PYTHON)" ]; then \
-			$(MAKE) install_pylibfdt; \
-		fi; \
-	fi
-
 PYLIBFDT_cleanfiles = libfdt_wrap.c libfdt.py libfdt.pyc _libfdt.so
-- 
2.13.0

From e56f2b07be3866eff49651cbe34be3bce79ceb38 Mon Sep 17 00:00:00 2001
From: Simon Glass <sjg@chromium.org>
Date: Fri, 7 Apr 2017 15:51:34 -0600
Subject: [PATCH 21/29] pylibfdt: Use setup.py to build the swig file

Since we are using the standard .i extension for the swig file, we can use
setup.py to build the wrapper. Drop the existing build code since it is
not needed.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pylibfdt/Makefile.pylibfdt | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/pylibfdt/Makefile.pylibfdt b/pylibfdt/Makefile.pylibfdt
index 4036b1f..9507d3d 100644
--- a/pylibfdt/Makefile.pylibfdt
+++ b/pylibfdt/Makefile.pylibfdt
@@ -1,8 +1,8 @@
 # Makefile.pylibfdt
 #
 
-PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS))
-WRAP = $(PYLIBFDT_objdir)/libfdt_wrap.c
+PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS)) \
+		$(PYLIBFDT_srcdir)/libfdt.i
 PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
 
 define run_setup
@@ -11,18 +11,14 @@ define run_setup
 	$(PYLIBFDT_objdir)/setup.py --quiet $(2)
 endef
 
-$(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
+$(PYMODULE): $(PYLIBFDT_srcs)
 	@$(VECHO) PYMOD $@
 	$(call run_setup, $^, build_ext --inplace)
 	mv _libfdt.so $@
 
-$(WRAP): $(PYLIBFDT_srcdir)/libfdt.i
-	@$(VECHO) SWIG $@
-	$(SWIG) -python -o $@ $<
-
-install_pylibfdt: $(WRAP) $(PYMODULE)
+install_pylibfdt: $(PYMODULE)
 	$(VECHO) INSTALL-PYLIB; \
-	$(call run_setup, $(PYLIBFDT_srcs) $(WRAP), \
+	$(call run_setup, $(PYLIBFDT_srcs), \
 		install $(if $(SETUP_PREFIX),--prefix=$(SETUP_PREFIX)))
 
 PYLIBFDT_cleanfiles = libfdt_wrap.c libfdt.py libfdt.pyc _libfdt.so
-- 
2.13.0