source: python/lib/gen_external.py @ 67cf45a

feature/autosinkfeature/constantqfeature/pitchshiftfeature/pydocstringsfeature/timestretch
Last change on this file since 67cf45a was 67cf45a, checked in by Paul Brossier <piem@piem.org>, 3 years ago

python/lib/gen_external.py: omit pitchyinfast

  • Property mode set to 100644
File size: 7.8 KB
Line 
1import distutils.ccompiler
2import sys, os, subprocess, glob
3
4header = os.path.join('src', 'aubio.h')
5output_path = os.path.join('python', 'gen')
6
7source_header = """// this file is generated! do not modify
8#include "aubio-types.h"
9"""
10
11skip_objects = [
12  # already in ext/
13  'fft',
14  'pvoc',
15  'filter',
16  'filterbank',
17  # AUBIO_UNSTABLE
18  'hist',
19  'parameter',
20  'scale',
21  'beattracking',
22  'resampler',
23  'peakpicker',
24  'pitchfcomb',
25  'pitchmcomb',
26  'pitchschmitt',
27  'pitchspecacf',
28  'pitchyin',
29  'pitchyinfft',
30  'pitchyinfast',
31  'sink',
32  'sink_apple_audio',
33  'sink_sndfile',
34  'sink_wavwrite',
35  #'mfcc',
36  'source',
37  'source_apple_audio',
38  'source_sndfile',
39  'source_avcodec',
40  'source_wavread',
41  #'sampler',
42  'audio_unit',
43  'spectral_whitening',
44  ]
45
46def get_preprocessor():
47    # findout which compiler to use
48    from distutils.sysconfig import customize_compiler
49    compiler_name = distutils.ccompiler.get_default_compiler()
50    compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)
51    try:
52        customize_compiler(compiler)
53    except AttributeError as e:
54        print("Warning: failed customizing compiler ({:s})".format(repr(e)))
55
56    if hasattr(compiler, 'initialize'):
57        try:
58            compiler.initialize()
59        except ValueError as e:
60            print("Warning: failed initializing compiler ({:s})".format(repr(e)))
61
62    cpp_cmd = None
63    if hasattr(compiler, 'preprocessor'): # for unixccompiler
64        cpp_cmd = compiler.preprocessor
65    elif hasattr(compiler, 'compiler'): # for ccompiler
66        cpp_cmd = compiler.compiler.split()
67        cpp_cmd += ['-E']
68    elif hasattr(compiler, 'cc'): # for msvccompiler
69        cpp_cmd = compiler.cc.split()
70        cpp_cmd += ['-E']
71
72    if not cpp_cmd:
73        print("Warning: could not guess preprocessor, using env's CC")
74        cpp_cmd = os.environ.get('CC', 'cc').split()
75        cpp_cmd += ['-E']
76
77    return cpp_cmd
78
79def get_cpp_objects(header=header, usedouble=False):
80    cpp_cmd = get_preprocessor()
81
82    macros = [('AUBIO_UNSTABLE', 1)]
83    if usedouble:
84        macros += [('HAVE_AUBIO_DOUBLE', 1)]
85
86    if not os.path.isfile(header):
87        raise Exception("could not find include file " + header)
88
89    includes = [os.path.dirname(header)]
90    cpp_cmd += distutils.ccompiler.gen_preprocess_options(macros, includes)
91    cpp_cmd += [header]
92
93    print("Running command: {:s}".format(" ".join(cpp_cmd)))
94    proc = subprocess.Popen(cpp_cmd,
95            stderr=subprocess.PIPE,
96            stdout=subprocess.PIPE)
97    assert proc, 'Proc was none'
98    cpp_output = proc.stdout.read()
99    err_output = proc.stderr.read()
100    if not cpp_output:
101        raise Exception("preprocessor output is empty:\n%s" % err_output)
102    elif err_output:
103        print ("Warning: preprocessor produced warnings:\n%s" % err_output)
104    if not isinstance(cpp_output, list):
105        cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')]
106
107    cpp_output = filter(lambda y: len(y) > 1, cpp_output)
108    cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output))
109
110    i = 1
111    while 1:
112        if i >= len(cpp_output): break
113        if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'):
114            cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i]
115            cpp_output.pop(i-1)
116        else:
117            i += 1
118
119    typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output)
120
121    cpp_objects = [a.split()[3][:-1] for a in typedefs]
122
123    return cpp_output, cpp_objects
124
125
126def analyze_cpp_output(cpp_objects, cpp_output):
127    lib = {}
128
129    for o in cpp_objects:
130        if o[:6] != 'aubio_':
131            continue
132        shortname = o[6:-2]
133        if shortname in skip_objects:
134            continue
135        lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []}
136        lib[shortname]['longname'] = o
137        lib[shortname]['shortname'] = shortname
138        for fn in cpp_output:
139            if o[:-1] in fn:
140                #print "found", o[:-1], "in", fn
141                if 'typedef struct ' in fn:
142                    lib[shortname]['struct'].append(fn)
143                elif '_do' in fn:
144                    lib[shortname]['do'].append(fn)
145                elif 'new_' in fn:
146                    lib[shortname]['new'].append(fn)
147                elif 'del_' in fn:
148                    lib[shortname]['del'].append(fn)
149                elif '_get_' in fn:
150                    lib[shortname]['get'].append(fn)
151                elif '_set_' in fn:
152                    lib[shortname]['set'].append(fn)
153                else:
154                    #print "no idea what to do about", fn
155                    lib[shortname]['other'].append(fn)
156    return lib
157
158def print_cpp_output_results(lib, cpp_output):
159    for fn in cpp_output:
160        found = 0
161        for o in lib:
162            for family in lib[o]:
163                if fn in lib[o][family]:
164                    found = 1
165        if found == 0:
166            print ("missing", fn)
167
168    for o in lib:
169        for family in lib[o]:
170            if type(lib[o][family]) == str:
171                print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )
172            elif len(lib[o][family]) == 1:
173                print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ) )
174            else:
175                print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )
176
177
178def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True):
179    if not os.path.isdir(output_path): os.mkdir(output_path)
180    elif not overwrite: return sorted(glob.glob(os.path.join(output_path, '*.c')))
181
182    cpp_output, cpp_objects = get_cpp_objects(header, usedouble=usedouble)
183
184    lib = analyze_cpp_output(cpp_objects, cpp_output)
185    # print_cpp_output_results(lib, cpp_output)
186
187    sources_list = []
188    try:
189        from .gen_code import MappedObject
190    except (SystemError, ValueError):
191        from gen_code import MappedObject
192    for o in lib:
193        out = source_header
194        mapped = MappedObject(lib[o], usedouble = usedouble)
195        out += mapped.gen_code()
196        output_file = os.path.join(output_path, 'gen-%s.c' % o)
197        with open(output_file, 'w') as f:
198            f.write(out)
199            print ("wrote %s" % output_file )
200            sources_list.append(output_file)
201
202    out = source_header
203    out += "#include \"aubio-generated.h\""
204    check_types = "\n     ||  ".join(["PyType_Ready(&Py_%sType) < 0" % o for o in lib])
205    out += """
206
207int generated_types_ready (void)
208{{
209  return ({pycheck_types});
210}}
211""".format(pycheck_types = check_types)
212
213    add_types = "".join(["""
214  Py_INCREF (&Py_{name}Type);
215  PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name = o) for o in lib])
216    out += """
217
218void add_generated_objects ( PyObject *m )
219{{
220{add_types}
221}}
222""".format(add_types = add_types)
223
224    output_file = os.path.join(output_path, 'aubio-generated.c')
225    with open(output_file, 'w') as f:
226        f.write(out)
227        print ("wrote %s" % output_file )
228        sources_list.append(output_file)
229
230    objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])
231    out = """// generated list of objects created with gen_external.py
232
233#include <Python.h>
234"""
235    if usedouble:
236        out += """
237#ifndef HAVE_AUBIO_DOUBLE
238#define HAVE_AUBIO_DOUBLE 1
239#endif
240"""
241    out += """
242{objlist}
243int generated_objects ( void );
244void add_generated_objects( PyObject *m );
245""".format(objlist = objlist)
246
247    output_file = os.path.join(output_path, 'aubio-generated.h')
248    with open(output_file, 'w') as f:
249        f.write(out)
250        print ("wrote %s" % output_file )
251        # no need to add header to list of sources
252
253    return sorted(sources_list)
254
255if __name__ == '__main__':
256    if len(sys.argv) > 1: header = sys.argv[1]
257    if len(sys.argv) > 2: output_path = sys.argv[2]
258    generate_external(header, output_path)
Note: See TracBrowser for help on using the repository browser.