python/lib/gen_external.py: omit pitchyinfast
[aubio.git] / python / lib / gen_external.py
1 import distutils.ccompiler
2 import sys, os, subprocess, glob
3
4 header = os.path.join('src', 'aubio.h')
5 output_path = os.path.join('python', 'gen')
6
7 source_header = """// this file is generated! do not modify
8 #include "aubio-types.h"
9 """
10
11 skip_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
46 def 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
79 def 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
126 def 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
158 def 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
178 def 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
207 int 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
218 void 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}
243 int generated_objects ( void );
244 void 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
255 if __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)