pylibfdt: move pylibfdt to scripts/dtc/pylibfdt and refactor makefile
authorMasahiro Yamada <yamada.masahiro@socionext.com>
Tue, 17 Oct 2017 04:42:43 +0000 (13:42 +0900)
committerTom Rini <trini@konsulko.com>
Fri, 17 Nov 2017 12:43:32 +0000 (07:43 -0500)
The pylibfdt is used by dtoc (and, indirectly by binman), but there
is no reason why it must be generated in the tools/ directory.

Recently, U-Boot switched over to the bundled DTC, and the directory
structure under scripts/dtc/ now mirrors the upstream DTC project.
So, scripts/dtc/pylibfdt is the best location.

I also rewrote the Makefile in a cleaner Kbuild style.

The scripts from the upstream have been moved as follows:

  lib/libfdt/pylibfdt/setup.py -> scripts/dtc/pylibfdt/setup.py
  lib/libfdt/pylibfdt/libfdt.i -> scripts/dtc/pylibfdt/libfdt.i_shipped

The .i_shipped is coped to .i during building because the .i must be
located in the objtree when we build it out of tree.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
12 files changed:
Makefile
lib/libfdt/pylibfdt/libfdt.i [deleted file]
lib/libfdt/pylibfdt/setup.py [deleted file]
scripts/Makefile.spl
scripts/dtc/Makefile
scripts/dtc/pylibfdt/.gitignore [new file with mode: 0644]
scripts/dtc/pylibfdt/Makefile [new file with mode: 0644]
scripts/dtc/pylibfdt/libfdt.i_shipped [new file with mode: 0644]
scripts/dtc/pylibfdt/setup.py [new file with mode: 0755]
tools/.gitignore
tools/Makefile
tools/binman/binman.py

index 67f01ad7e4e88f5c2ae26a19839629633420a517..f94813d86b797b45c5a42073caa22e1c3ef2bebd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1380,7 +1380,7 @@ $(timestamp_h): $(srctree)/Makefile FORCE
        $(call filechk,timestamp.h)
 
 checkbinman: tools
-       @if ! ( echo 'import libfdt' | ( PYTHONPATH=tools $(PYTHON) )); then \
+       @if ! ( echo 'import libfdt' | ( PYTHONPATH=scripts/dtc/pylibfdt $(PYTHON) )); then \
                echo >&2; \
                echo >&2 '*** binman needs the Python libfdt library.'; \
                echo >&2 '*** Either install it on your system, or try:'; \
diff --git a/lib/libfdt/pylibfdt/libfdt.i b/lib/libfdt/pylibfdt/libfdt.i
deleted file mode 100644 (file)
index 5b1a8cf..0000000
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * pylibfdt - Flat Device Tree manipulation in Python
- * Copyright (C) 2017 Google, Inc.
- * Written by Simon Glass <sjg@chromium.org>
- *
- * SPDX-License-Identifier:    GPL-2.0+ BSD-2-Clause
- */
-
-%module libfdt
-
-%include <stdint.i>
-
-%{
-#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 subnode_offset(self, parentoffset, name, quiet=()):
-        """Get the offset of a named subnode
-
-        Args:
-            parentoffset: Offset of the parent node to check
-            name: Name of the required subnode, e.g. 'subnode@1'
-            quiet: Errors to ignore (empty to raise on all errors)
-
-        Returns:
-            The node offset of the found node, if any
-
-        Raises
-            FdtException if there is no node with that name, or other error
-        """
-        return check_err(fdt_subnode_offset(self._fdt, parentoffset, name),
-                         quiet)
-
-    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])
-
-    def get_phandle(self, nodeoffset):
-        """Get the phandle of a node
-
-        Args:
-            nodeoffset: Node offset to check
-
-        Returns:
-            phandle of node, or 0 if the node has no phandle or another error
-            occurs
-        """
-        return fdt_get_phandle(self._fdt, nodeoffset)
-
-    def parent_offset(self, nodeoffset, quiet=()):
-        """Get the offset of a node's parent
-
-        Args:
-            nodeoffset: Node offset to check
-            quiet: Errors to ignore (empty to raise on all errors)
-
-        Returns:
-            The offset of the parent node, if any
-
-        Raises:
-            FdtException if no parent found or other error occurs
-        """
-        return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet)
-
-    def node_offset_by_phandle(self, phandle, quiet=()):
-        """Get the offset of a node with the given phandle
-
-        Args:
-            phandle: Phandle to search for
-            quiet: Errors to ignore (empty to raise on all errors)
-
-        Returns:
-            The offset of node with that phandle, if any
-
-        Raises:
-            FdtException if no node found or other error occurs
-        """
-        return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet)
-
-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/lib/libfdt/pylibfdt/setup.py b/lib/libfdt/pylibfdt/setup.py
deleted file mode 100755 (executable)
index daf1089..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env python
-
-"""
-setup.py file for SWIG libfdt
-Copyright (C) 2017 Google, Inc.
-Written by Simon Glass <sjg@chromium.org>
-
-SPDX-License-Identifier:       GPL-2.0+ BSD-2-Clause
-
-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.get('SOURCES', '').split()
-cflags = os.environ.get('CPPFLAGS', '').split()
-objdir = os.environ.get('OBJDIR')
-version = os.environ.get('VERSION')
-swig_opts = os.environ.get('SWIG_OPTS', '').split()
-
-# 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((swig_opts, version, files, cflags, objdir)):
-    swig_opts, version, files, cflags, objdir = GetEnvFromMakefiles()
-
-libfdt_module = Extension(
-    '_libfdt',
-    sources = files,
-    extra_compile_args = cflags,
-    swig_opts = swig_opts,
-)
-
-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=['pylibfdt/libfdt'],
-)
index 49b27ac9267be60e55bd03fc41819fa15ec9694a..065bb259d529b0b23e27de449887f3c034de17ee 100644 (file)
@@ -257,7 +257,7 @@ quiet_cmd_fdtgrep = FDTGREP $@
 $(obj)/$(SPL_BIN).dtb: dts/dt.dtb $(objtree)/tools/fdtgrep FORCE
        $(call if_changed,fdtgrep)
 
-pythonpath = PYTHONPATH=tools
+pythonpath = PYTHONPATH=scripts/dtc/pylibfdt
 
 quiet_cmd_dtocc = DTOC C  $@
 cmd_dtocc = $(pythonpath) $(srctree)/tools/dtoc/dtoc -d $(obj)/$(SPL_BIN).dtb -o $@ platdata
@@ -381,7 +381,7 @@ ifneq ($(cmd_files),)
 endif
 
 checkdtoc: tools
-       @if ! ( echo 'import libfdt' | ( PYTHONPATH=tools $(PYTHON) )); then \
+       @if ! ( echo 'import libfdt' | ( PYTHONPATH=scripts/dtc/pylibfdt $(PYTHON) )); then \
                echo '*** dtoc needs the Python libfdt library. Either '; \
                echo '*** install it on your system, or try:'; \
                echo '***'; \
index 2a48022c41e7d184660c59de87fa7dce0b345d4b..f4a16ed2a5f37db52cd01d60fa7056163fce0233 100644 (file)
@@ -29,3 +29,6 @@ $(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h
 
 # generated files need to be cleaned explicitly
 clean-files    := dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h
+
+# Added for U-Boot
+subdir-y += pylibfdt
diff --git a/scripts/dtc/pylibfdt/.gitignore b/scripts/dtc/pylibfdt/.gitignore
new file mode 100644 (file)
index 0000000..033f23d
--- /dev/null
@@ -0,0 +1,4 @@
+/_libfdt.so
+/libfdt.py
+/libfdt.pyc
+/libfdt_wrap.c
diff --git a/scripts/dtc/pylibfdt/Makefile b/scripts/dtc/pylibfdt/Makefile
new file mode 100644 (file)
index 0000000..01d5e0f
--- /dev/null
@@ -0,0 +1,30 @@
+# Unfortunately setup.py below cannot handle srctree being ".." which it often
+# is. It fails with an error like:
+# Fatal error: can't create build/temp.linux-x86_64-2.7/../lib/libfdt/fdt.o:
+#    No such file or directory
+# To fix this, use an absolute path.
+LIBFDT_srcdir = $(abspath $(srctree)/$(src)/../libfdt)
+
+include $(LIBFDT_srcdir)/Makefile.libfdt
+
+# Unfortunately setup.py (or actually the Python distutil implementation) puts
+# files into the same directory as the .i file. We cannot touch the source
+# directory, so we "ship" .i file into the objtree.
+PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS)) \
+               $(obj)/libfdt.i
+
+quiet_cmd_pymod = PYMOD   $@
+      cmd_pymod = unset CC; unset CROSS_COMPILE; unset CFLAGS;\
+               LDFLAGS="$(HOSTLDFLAGS)" \
+               VERSION="u-boot-$(UBOOTVERSION)" \
+               CPPFLAGS="$(HOSTCFLAGS) -I$(LIBFDT_srcdir)" OBJDIR=$(obj) \
+               SOURCES="$(PYLIBFDT_srcs)" \
+               SWIG_OPTS="-I$(LIBFDT_srcdir) -I$(LIBFDT_srcdir)/.." \
+               $(PYTHON) $< --quiet build_ext --inplace
+
+$(obj)/_libfdt.so: $(src)/setup.py $(PYLIBFDT_srcs) FORCE
+       $(call if_changed,pymod)
+
+always += _libfdt.so
+
+clean-files += libfdt.i _libfdt.so libfdt.py libfdt_wrap.c
diff --git a/scripts/dtc/pylibfdt/libfdt.i_shipped b/scripts/dtc/pylibfdt/libfdt.i_shipped
new file mode 100644 (file)
index 0000000..5b1a8cf
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * pylibfdt - Flat Device Tree manipulation in Python
+ * Copyright (C) 2017 Google, Inc.
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+ BSD-2-Clause
+ */
+
+%module libfdt
+
+%include <stdint.i>
+
+%{
+#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 subnode_offset(self, parentoffset, name, quiet=()):
+        """Get the offset of a named subnode
+
+        Args:
+            parentoffset: Offset of the parent node to check
+            name: Name of the required subnode, e.g. 'subnode@1'
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The node offset of the found node, if any
+
+        Raises
+            FdtException if there is no node with that name, or other error
+        """
+        return check_err(fdt_subnode_offset(self._fdt, parentoffset, name),
+                         quiet)
+
+    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])
+
+    def get_phandle(self, nodeoffset):
+        """Get the phandle of a node
+
+        Args:
+            nodeoffset: Node offset to check
+
+        Returns:
+            phandle of node, or 0 if the node has no phandle or another error
+            occurs
+        """
+        return fdt_get_phandle(self._fdt, nodeoffset)
+
+    def parent_offset(self, nodeoffset, quiet=()):
+        """Get the offset of a node's parent
+
+        Args:
+            nodeoffset: Node offset to check
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of the parent node, if any
+
+        Raises:
+            FdtException if no parent found or other error occurs
+        """
+        return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet)
+
+    def node_offset_by_phandle(self, phandle, quiet=()):
+        """Get the offset of a node with the given phandle
+
+        Args:
+            phandle: Phandle to search for
+            quiet: Errors to ignore (empty to raise on all errors)
+
+        Returns:
+            The offset of node with that phandle, if any
+
+        Raises:
+            FdtException if no node found or other error occurs
+        """
+        return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet)
+
+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/scripts/dtc/pylibfdt/setup.py b/scripts/dtc/pylibfdt/setup.py
new file mode 100755 (executable)
index 0000000..daf1089
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+"""
+setup.py file for SWIG libfdt
+Copyright (C) 2017 Google, Inc.
+Written by Simon Glass <sjg@chromium.org>
+
+SPDX-License-Identifier:       GPL-2.0+ BSD-2-Clause
+
+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.get('SOURCES', '').split()
+cflags = os.environ.get('CPPFLAGS', '').split()
+objdir = os.environ.get('OBJDIR')
+version = os.environ.get('VERSION')
+swig_opts = os.environ.get('SWIG_OPTS', '').split()
+
+# 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((swig_opts, version, files, cflags, objdir)):
+    swig_opts, version, files, cflags, objdir = GetEnvFromMakefiles()
+
+libfdt_module = Extension(
+    '_libfdt',
+    sources = files,
+    extra_compile_args = cflags,
+    swig_opts = swig_opts,
+)
+
+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=['pylibfdt/libfdt'],
+)
index 5293d44697ca3784b93d8d25973d5cbafc962d19..6a487d22027aa6838eb1fbc2335473a731863b62 100644 (file)
@@ -1,4 +1,3 @@
-/_libfdt.so
 /atmel_pmecc_params
 /bin2header
 /bmp_logo
@@ -17,9 +16,6 @@
 /img2srec
 /kwboot
 /lib/
-/libfdt.py
-/libfdt.pyc
-/libfdt_wrap.c
 /mips-relocs
 /mkenvimage
 /mkexynosspl
index 5db2a542252a8a695fb75077fdb7a76270f8ec8c..2b87e184f3c4dcbfd27a2f55727a44d15857f8f5 100644 (file)
@@ -63,15 +63,6 @@ LIBFDT_CSRCS := fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c  \
                        fdt_empty_tree.c fdt_addresses.c fdt_overlay.c \
                        fdt_region.c
 
-# Unfortunately setup.py below cannot handle srctree being ".." which it often
-# is. It fails with an error like:
-# Fatal error: can't create build/temp.linux-x86_64-2.7/../lib/libfdt/fdt.o:
-#    No such file or directory
-# To fix this, use an absolute path.
-libfdt_tree := $(shell readlink -f $(srctree)/lib/libfdt)
-
-LIBFDT_SRCS := $(addprefix $(libfdt_tree)/, $(LIBFDT_CSRCS))
-LIBFDT_SWIG := $(addprefix $(libfdt_tree)/, pylibfdt/libfdt.i)
 LIBFDT_OBJS := $(addprefix lib/libfdt/, $(patsubst %.c, %.o, $(LIBFDT_CSRCS)))
 
 RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \
@@ -123,23 +114,6 @@ mkimage-objs   := $(dumpimage-mkimage-objs) mkimage.o
 fit_info-objs   := $(dumpimage-mkimage-objs) fit_info.o
 fit_check_sign-objs   := $(dumpimage-mkimage-objs) fit_check_sign.o
 
-# Unfortunately setup.py (or actually the Python distutil implementation)
-# puts files into the same directory as the .i file. We cannot touch the source
-# directory, so we copy the .i file into the tools/ build subdirectory before
-# calling setup. This directory is safe to write to. This ensures that we get
-# all three files in $(obj)/tools: _libfdt.so, libfdt.py and libfdt_wrap.c
-# The latter is a temporary file which we could actually remove.
-tools/_libfdt.so: $(LIBFDT_SRCS) $(LIBFDT_SWIG)
-       $(Q)cp $(LIBFDT_SWIG) tools/.
-       $(Q)unset CC; \
-       unset CROSS_COMPILE; \
-       LDFLAGS="$(HOSTLDFLAGS)" CFLAGS= VERSION="u-boot-$(UBOOTVERSION)" \
-               CPPFLAGS="$(_hostc_flags)" OBJDIR=tools \
-               SOURCES="$(LIBFDT_SRCS) tools/libfdt.i" \
-               SWIG_OPTS="-I$(srctree)/lib/libfdt -I$(srctree)/lib" \
-               $(PYTHON) $(libfdt_tree)/pylibfdt/setup.py --quiet build_ext \
-                       --build-lib tools
-
 ifneq ($(CONFIG_MX23)$(CONFIG_MX28),)
 # Add CONFIG_MXS into host CFLAGS, so we can check whether or not register
 # the mxsimage support within tools/mxsimage.c .
@@ -231,10 +205,6 @@ clean-dirs := lib common
 
 always := $(hostprogs-y)
 
-# Build a libfdt Python module if swig is available
-# Use 'sudo apt-get install swig libpython-dev' to enable this
-always += $(if $(shell which swig 2> /dev/null),_libfdt.so)
-
 # Generated LCD/video logo
 LOGO_H = $(objtree)/include/bmp_logo.h
 LOGO_DATA_H = $(objtree)/include/bmp_logo_data.h
index 09dc36a3f7913b356c1d5eaf0a68ad76eeaf61da..e75a59d9517d23304a6b7fd7f9c680ad659a7d5e 100755 (executable)
@@ -21,7 +21,7 @@ for dirname in ['../patman', '../dtoc', '..']:
     sys.path.insert(0, os.path.join(our_path, dirname))
 
 # Bring in the libfdt module
-sys.path.insert(0, 'tools')
+sys.path.insert(0, 'scripts/dtc/pylibfdt')
 
 # Also allow entry-type modules to be brought in from the etype directory.
 sys.path.insert(0, os.path.join(our_path, 'etype'))