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>
This commit is contained in:
Simon Glass 2017-04-07 15:51:32 -06:00 committed by David Gibson
parent e20d9658cd
commit 90db6d9989
5 changed files with 112 additions and 16 deletions

View file

@ -267,6 +267,7 @@ TESTS_BIN += convert-dtsv0
TESTS_BIN += fdtput TESTS_BIN += fdtput
TESTS_BIN += fdtget TESTS_BIN += fdtget
TESTS_BIN += fdtdump TESTS_BIN += fdtdump
TESTS_PYLIBFDT += maybe_pylibfdt
include tests/Makefile.tests include tests/Makefile.tests

14
README
View file

@ -50,12 +50,18 @@ If you add new features, please check code coverage:
# Open 'htmlcov/index.html' in your browser # 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' If --prefix is not provided, the default prefix is used, typically '/usr'
or '/usr/local'. See Python's distutils documentation for details. 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, To disable building the python library, even if swig and Python are available,
use: use:

View file

@ -5,13 +5,16 @@ PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS))
WRAP = $(PYLIBFDT_objdir)/libfdt_wrap.c WRAP = $(PYLIBFDT_objdir)/libfdt_wrap.c
PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so PYMODULE = $(PYLIBFDT_objdir)/_libfdt.so
run_setup = SOURCES="$(1)" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)" \ define run_setup
VERSION="$(dtc_version)" \ SOURCES="$(1)" CPPFLAGS="$(CPPFLAGS)" OBJDIR="$(PYLIBFDT_objdir)"
python $(PYLIBFDT_objdir)/setup.py --quiet $(2) VERSION="$(dtc_version)"
$(PYLIBFDT_objdir)/setup.py --quiet $(2)
endef
$(PYMODULE): $(PYLIBFDT_srcs) $(WRAP) $(PYMODULE): $(PYLIBFDT_srcs) $(WRAP)
@$(VECHO) PYMOD $@ @$(VECHO) PYMOD $@
$(call run_setup, $^, build_ext --inplace) $(call run_setup, $^, build_ext --inplace)
mv _libfdt.so $@
$(WRAP): $(PYLIBFDT_srcdir)/libfdt.i $(WRAP): $(PYLIBFDT_srcdir)/libfdt.i
@$(VECHO) SWIG $@ @$(VECHO) SWIG $@

98
pylibfdt/setup.py Normal file → Executable file
View file

@ -2,26 +2,112 @@
""" """
setup.py file for SWIG libfdt 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 Files to be built into the extension are provided in SOURCES
C flags to use are provided in CPPFLAGS C flags to use are provided in CPPFLAGS
Object file directory is provided in OBJDIR 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 from distutils.core import setup, Extension
import os import os
import re
import sys 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] progname = sys.argv[0]
files = os.environ['SOURCES'].split() files = os.environ.get('SOURCES', '').split()
cflags = os.environ['CPPFLAGS'].split() cflags = os.environ.get('CPPFLAGS', '').split()
objdir = os.environ['OBJDIR'] objdir = os.environ.get('OBJDIR')
version = os.environ['VERSION'] 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_module = Extension(
'_libfdt', '_libfdt',
sources = files, sources = files,
extra_compile_args = cflags extra_compile_args = cflags,
swig_opts = swig_opts,
) )
setup( setup(
@ -31,5 +117,5 @@ setup(
description='Python binding for libfdt', description='Python binding for libfdt',
ext_modules=[libfdt_module], ext_modules=[libfdt_module],
package_dir={'': objdir}, package_dir={'': objdir},
py_modules=['libfdt'], py_modules=['pylibfdt/libfdt'],
) )

View file

@ -72,13 +72,13 @@ tests_clean:
rm -f $(STD_CLEANFILES:%=$(TESTS_PREFIX)%) rm -f $(STD_CLEANFILES:%=$(TESTS_PREFIX)%)
rm -f $(TESTS_CLEANFILES) rm -f $(TESTS_CLEANFILES)
check: tests ${TESTS_BIN} check: tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
cd $(TESTS_PREFIX); ./run_tests.sh 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.$$$$ 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 cd $(TESTS_PREFIX); ./run_tests.sh -v
ifneq ($(DEPTARGETS),) ifneq ($(DEPTARGETS),)