1 import distutils.ccompiler
7 header = os.path.join('src', 'aubio.h')
8 output_path = os.path.join('python', 'gen')
10 source_header = """// this file is generated! do not modify
11 #include "aubio-types.h"
49 def get_preprocessor():
50 # findout which compiler to use
51 from distutils.sysconfig import customize_compiler
52 compiler_name = distutils.ccompiler.get_default_compiler()
53 compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)
55 customize_compiler(compiler)
56 except AttributeError as e:
57 print("Warning: failed customizing compiler ({:s})".format(repr(e)))
59 if hasattr(compiler, 'initialize'):
62 except ValueError as e:
63 print("Warning: failed initializing compiler ({:s})".format(repr(e)))
66 if hasattr(compiler, 'preprocessor'): # for unixccompiler
67 cpp_cmd = compiler.preprocessor
68 elif hasattr(compiler, 'compiler'): # for ccompiler
69 cpp_cmd = compiler.compiler.split()
71 elif hasattr(compiler, 'cc'): # for msvccompiler
72 cpp_cmd = compiler.cc.split()
76 print("Warning: could not guess preprocessor, using env's CC")
77 cpp_cmd = os.environ.get('CC', 'cc').split()
79 cpp_cmd += ['-x', 'c'] # force C language (emcc defaults to c++)
83 def get_c_declarations(header=header, usedouble=False):
84 ''' return a dense and preprocessed string of all c declarations implied by aubio.h
86 cpp_cmd = get_preprocessor()
88 macros = [('AUBIO_UNSTABLE', 1)]
90 macros += [('HAVE_AUBIO_DOUBLE', 1)]
92 if not os.path.isfile(header):
93 raise Exception("could not find include file " + header)
95 includes = [os.path.dirname(header)]
96 cpp_cmd += distutils.ccompiler.gen_preprocess_options(macros, includes)
99 print("Running command: {:s}".format(" ".join(cpp_cmd)))
100 proc = subprocess.Popen(cpp_cmd,
101 stderr=subprocess.PIPE,
102 stdout=subprocess.PIPE)
103 assert proc, 'Proc was none'
104 cpp_output = proc.stdout.read()
105 err_output = proc.stderr.read()
107 raise Exception("preprocessor output is empty:\n%s" % err_output)
109 print("Warning: preprocessor produced warnings:\n%s" % err_output)
110 if not isinstance(cpp_output, list):
111 cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')]
113 cpp_output = filter(lambda y: len(y) > 1, cpp_output)
114 cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output))
118 if i >= len(cpp_output):
120 if ('{' in cpp_output[i - 1]) and (not '}' in cpp_output[i - 1]) or (not ';' in cpp_output[i - 1]):
121 cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i]
122 cpp_output.pop(i - 1)
123 elif ('}' in cpp_output[i]):
124 cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i]
125 cpp_output.pop(i - 1)
129 # clean pointer notations
132 tmp += [l.replace(' *', '* ')]
138 def get_cpp_objects_from_c_declarations(c_declarations):
139 typedefs = filter(lambda y: y.startswith('typedef struct _aubio'), c_declarations)
140 cpp_objects = [a.split()[3][:-1] for a in typedefs]
144 def get_all_func_names_from_lib(lib, depth=0):
145 ''' return flat string of all function used in lib
149 for k, v in lib.items():
150 if isinstance(v, dict):
151 res += get_all_func_names_from_lib(v, depth + 1)
152 elif isinstance(v, list):
156 continue # not a function
157 fname_part = e[0].strip().split(' ')
158 fname = fname_part[-1]
162 raise NameError('gen_lib : weird function: ' + str(e))
167 def generate_lib_from_c_declarations(cpp_objects, c_declarations):
168 ''' returns a lib from given cpp_object names
170 a lib is a dict grouping functions by family (onset,pitch...)
171 each eement is itself a dict of functions grouped by puposes as :
172 struct, new, del, do, get, set and other
176 for o in cpp_objects:
178 if o[:6] == 'aubio_':
179 shortname = o[6:-2] # without aubio_
180 longname = o[:-2] # without _t
181 else: # support object not starting with aubio_ (fvec...)
185 if shortname in skip_objects:
187 lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []}
188 lib[shortname]['longname'] = longname
189 lib[shortname]['shortname'] = shortname
191 for fn in c_declarations:
192 func_name = fn.split('(')[0].strip().split(' ')[1:]
194 func_name = func_name[-1]
196 raise NameError('Warning : error while parsing : unexpected line %s' % fn)
197 if func_name.startswith(longname + '_') or func_name.endswith(longname):
198 # print "found", shortname, "in", fn
199 if 'typedef struct ' in fn:
200 lib[shortname]['struct'].append(fn)
202 lib[shortname]['do'].append(fn)
204 lib[shortname]['new'].append(fn)
206 lib[shortname]['del'].append(fn)
208 lib[shortname]['get'].append(fn)
210 lib[shortname]['set'].append(fn)
212 # print "no idea what to do about", fn
213 lib[shortname]['other'].append(fn)
217 def print_c_declarations_results(lib, c_declarations):
218 for fn in c_declarations:
221 for family in lib[o]:
222 if fn in lib[o][family]:
228 for family in lib[o]:
229 if type(lib[o][family]) == str:
230 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family]))
231 elif len(lib[o][family]) == 1:
232 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family][0]))
234 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family]))
237 def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True):
238 if not os.path.isdir(output_path):
239 os.mkdir(output_path)
241 return sorted(glob.glob(os.path.join(output_path, '*.c')))
243 c_declarations = get_c_declarations(header, usedouble=usedouble)
244 cpp_objects = get_cpp_objects_from_c_declarations(c_declarations)
246 lib = generate_lib_from_c_declarations(cpp_objects, c_declarations)
247 # print_c_declarations_results(lib, c_declarations)
251 from .gen_code import MappedObject
252 except (SystemError, ValueError):
253 from gen_code import MappedObject
256 mapped = MappedObject(lib[o], usedouble=usedouble)
257 out += mapped.gen_code()
258 output_file = os.path.join(output_path, 'gen-%s.c' % o)
259 with open(output_file, 'w') as f:
261 print("wrote %s" % output_file)
262 sources_list.append(output_file)
265 out += "#include \"aubio-generated.h\""
266 check_types = "\n || ".join(["PyType_Ready(&Py_%sType) < 0" % o for o in lib])
269 int generated_types_ready (void)
271 return ({pycheck_types});
273 """.format(pycheck_types=check_types)
275 add_types = "".join(["""
276 Py_INCREF (&Py_{name}Type);
277 PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name=o) for o in lib])
280 void add_generated_objects ( PyObject *m )
284 """.format(add_types=add_types)
286 output_file = os.path.join(output_path, 'aubio-generated.c')
287 with open(output_file, 'w') as f:
289 print("wrote %s" % output_file)
290 sources_list.append(output_file)
292 objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])
293 out = """// generated list of objects created with gen_external.py
299 #ifndef HAVE_AUBIO_DOUBLE
300 #define HAVE_AUBIO_DOUBLE 1
305 int generated_objects ( void );
306 void add_generated_objects( PyObject *m );
307 """.format(objlist=objlist)
309 output_file = os.path.join(output_path, 'aubio-generated.h')
310 with open(output_file, 'w') as f:
312 print("wrote %s" % output_file)
313 # no need to add header to list of sources
315 return sorted(sources_list)
317 if __name__ == '__main__':
318 if len(sys.argv) > 1:
320 if len(sys.argv) > 2:
321 output_path = sys.argv[2]
322 generate_external(header, output_path)