source: python/lib/gen_external.py @ 50853b0

feature/autosinkfeature/constantqfeature/pitchshiftfeature/pydocstringsfeature/timestretch
Last change on this file since 50853b0 was 50853b0, checked in by GitHub <noreply@github.com>, 3 years ago

Update gen_external.py

gen_external :

  • fix unused variable in get_all_func_names_from_lib
  • use proper python syntax : 'is' and 'not in'
  • Property mode set to 100644
File size: 9.8 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': [], '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 'new_' in fn:
199                    lib[shortname]['new'].append(fn)
200                elif 'del_' in fn:
201                    lib[shortname]['del'].append(fn)
202                elif '_get_' in fn:
203                    lib[shortname]['get'].append(fn)
204                elif '_set_' in fn:
205                    lib[shortname]['set'].append(fn)
206                else:
207                    # print "no idea what to do about", fn
208                    lib[shortname]['other'].append(fn)
209    return lib
210
211
212def print_c_declarations_results(lib, c_declarations):
213    for fn in c_declarations:
214        found = 0
215        for o in lib:
216            for family in lib[o]:
217                if fn in lib[o][family]:
218                    found = 1
219        if found == 0:
220            print("missing", fn)
221
222    for o in lib:
223        for family in lib[o]:
224            if type(lib[o][family]) == str:
225                print("{:15s} {:10s} {:s}".format(o, family, lib[o][family]))
226            elif len(lib[o][family]) == 1:
227                print("{:15s} {:10s} {:s}".format(o, family, lib[o][family][0]))
228            else:
229                print("{:15s} {:10s} {:s}".format(o, family, lib[o][family]))
230
231
232def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True):
233    if not os.path.isdir(output_path):
234        os.mkdir(output_path)
235    elif not overwrite:
236        return sorted(glob.glob(os.path.join(output_path, '*.c')))
237
238    c_declarations = get_c_declarations(header, usedouble=usedouble)
239    cpp_objects = get_cpp_objects_from_c_declarations(c_declarations)
240
241    lib = generate_lib_from_c_declarations(cpp_objects, c_declarations)
242    # print_c_declarations_results(lib, c_declarations)
243
244    sources_list = []
245    try:
246        from .gen_code import MappedObject
247    except (SystemError, ValueError):
248        from gen_code import MappedObject
249    for o in lib:
250        out = source_header
251        mapped = MappedObject(lib[o], usedouble=usedouble)
252        out += mapped.gen_code()
253        output_file = os.path.join(output_path, 'gen-%s.c' % o)
254        with open(output_file, 'w') as f:
255            f.write(out)
256            print("wrote %s" % output_file)
257            sources_list.append(output_file)
258
259    out = source_header
260    out += "#include \"aubio-generated.h\""
261    check_types = "\n     ||  ".join(["PyType_Ready(&Py_%sType) < 0" % o for o in lib])
262    out += """
263
264int generated_types_ready (void)
265{{
266  return ({pycheck_types});
267}}
268""".format(pycheck_types=check_types)
269
270    add_types = "".join(["""
271  Py_INCREF (&Py_{name}Type);
272  PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name=o) for o in lib])
273    out += """
274
275void add_generated_objects ( PyObject *m )
276{{
277{add_types}
278}}
279""".format(add_types=add_types)
280
281    output_file = os.path.join(output_path, 'aubio-generated.c')
282    with open(output_file, 'w') as f:
283        f.write(out)
284        print("wrote %s" % output_file)
285        sources_list.append(output_file)
286
287    objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])
288    out = """// generated list of objects created with gen_external.py
289
290#include <Python.h>
291"""
292    if usedouble:
293        out += """
294#ifndef HAVE_AUBIO_DOUBLE
295#define HAVE_AUBIO_DOUBLE 1
296#endif
297"""
298    out += """
299{objlist}
300int generated_objects ( void );
301void add_generated_objects( PyObject *m );
302""".format(objlist=objlist)
303
304    output_file = os.path.join(output_path, 'aubio-generated.h')
305    with open(output_file, 'w') as f:
306        f.write(out)
307        print("wrote %s" % output_file)
308        # no need to add header to list of sources
309
310    return sorted(sources_list)
311
312if __name__ == '__main__':
313    if len(sys.argv) > 1:
314        header = sys.argv[1]
315    if len(sys.argv) > 2:
316        output_path = sys.argv[2]
317    generate_external(header, output_path)
Note: See TracBrowser for help on using the repository browser.