1#!/usr/bin/env python
2
3"""
4setup.py file for SWIG libfdt
5Copyright (C) 2017 Google, Inc.
6Written by Simon Glass <sjg@chromium.org>
7
8Files to be built into the extension are provided in SOURCES
9C flags to use are provided in CPPFLAGS
10Object file directory is provided in OBJDIR
11Version is provided in VERSION
12
13If these variables are not given they are parsed from the Makefiles. This
14allows this script to be run stand-alone, e.g.:
15
16    ./pylibfdt/setup.py install [--prefix=...]
17"""
18
19from distutils.core import setup, Extension
20import os
21import re
22import sys
23
24# Decodes a Makefile assignment line into key and value (and plus for +=)
25RE_KEY_VALUE = re.compile('(?P<key>\w+) *(?P<plus>[+])?= *(?P<value>.*)$')
26
27
28def ParseMakefile(fname):
29    """Parse a Makefile to obtain its variables.
30
31    This collects variable assigments of the form:
32
33        VAR = value
34        VAR += more
35
36    It does not pick out := assignments, as these are not needed here. It does
37    handle line continuation.
38
39    Returns a dict:
40        key: Variable name (e.g. 'VAR')
41        value: Variable value (e.g. 'value more')
42    """
43    makevars = {}
44    with open(fname) as fd:
45        prev_text = ''  # Continuation text from previous line(s)
46        for line in fd.read().splitlines():
47          if line and line[-1] == '\\':  # Deal with line continuation
48            prev_text += line[:-1]
49            continue
50          elif prev_text:
51            line = prev_text + line
52            prev_text = ''  # Continuation is now used up
53          m = RE_KEY_VALUE.match(line)
54          if m:
55            value = m.group('value') or ''
56            key = m.group('key')
57
58            # Appending to a variable inserts a space beforehand
59            if 'plus' in m.groupdict() and key in makevars:
60              makevars[key] += ' ' + value
61            else:
62              makevars[key] = value
63    return makevars
64
65def GetEnvFromMakefiles():
66    """Scan the Makefiles to obtain the settings we need.
67
68    This assumes that this script is being run from the top-level directory,
69    not the pylibfdt directory.
70
71    Returns:
72        Tuple with:
73            List of swig options
74            Version string
75            List of files to build
76            List of extra C preprocessor flags needed
77            Object directory to use (always '')
78    """
79    basedir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
80    swig_opts = ['-I%s' % basedir]
81    makevars = ParseMakefile(os.path.join(basedir, 'Makefile'))
82    version = '%s.%s.%s' % (makevars['VERSION'], makevars['PATCHLEVEL'],
83                            makevars['SUBLEVEL'])
84    makevars = ParseMakefile(os.path.join(basedir, 'libfdt', 'Makefile.libfdt'))
85    files = makevars['LIBFDT_SRCS'].split()
86    files = [os.path.join(basedir, 'libfdt', fname) for fname in files]
87    files.append('pylibfdt/libfdt.i')
88    cflags = ['-I%s' % basedir, '-I%s/libfdt' % basedir]
89    objdir = ''
90    return swig_opts, version, files, cflags, objdir
91
92
93progname = sys.argv[0]
94files = os.environ.get('SOURCES', '').split()
95cflags = os.environ.get('CPPFLAGS', '').split()
96objdir = os.environ.get('OBJDIR')
97version = os.environ.get('VERSION')
98swig_opts = []
99
100# If we were called directly rather than through our Makefile (which is often
101# the case with Python module installation), read the settings from the
102# Makefile.
103if not all((version, files, cflags, objdir)):
104    swig_opts, version, files, cflags, objdir = GetEnvFromMakefiles()
105
106libfdt_module = Extension(
107    '_libfdt',
108    sources = files,
109    extra_compile_args = cflags,
110    swig_opts = swig_opts,
111)
112
113setup(
114    name='libfdt',
115    version= version,
116    author='Simon Glass <sjg@chromium.org>',
117    description='Python binding for libfdt',
118    ext_modules=[libfdt_module],
119    package_dir={'': objdir},
120    py_modules=['pylibfdt/libfdt'],
121)
122