Skip to content

Commit

Permalink
pylibfdt: Allow setup.py to operate stand-alone
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: David Gibson <[email protected]>
  • Loading branch information
sjg20 authored and dgibson committed Apr 8, 2017
1 parent e20d965 commit 90db6d9
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 16 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 10 additions & 4 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 6 additions & 3 deletions pylibfdt/Makefile.pylibfdt
Original file line number Diff line number Diff line change
Expand Up @@ -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 $@
Expand Down
98 changes: 92 additions & 6 deletions pylibfdt/setup.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,112 @@

"""
setup.py file for SWIG libfdt
Copyright (C) 2017 Google, Inc.
Written by Simon Glass <[email protected]>
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(
Expand All @@ -31,5 +117,5 @@
description='Python binding for libfdt',
ext_modules=[libfdt_module],
package_dir={'': objdir},
py_modules=['libfdt'],
py_modules=['pylibfdt/libfdt'],
)
6 changes: 3 additions & 3 deletions tests/Makefile.tests
Original file line number Diff line number Diff line change
Expand Up @@ -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),)
Expand Down

0 comments on commit 90db6d9

Please sign in to comment.