source: python/lib/gen_external.py @ e7556a1

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

python/lib/gen_code.py: add support for rdo

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