python/lib/gen_external.py: remove duplicate
[aubio.git] / python / lib / gen_external.py
index 735cfef..d2115b5 100644 (file)
@@ -1,6 +1,10 @@
-import os
+import distutils.ccompiler
+import sys, os, subprocess, glob
 
-header = """// this file is generated! do not modify
+header = os.path.join('src', 'aubio.h')
+output_path = os.path.join('python', 'gen')
+
+source_header = """// this file is generated! do not modify
 #include "aubio-types.h"
 """
 
@@ -10,7 +14,6 @@ skip_objects = [
   'pvoc',
   'filter',
   'filterbank',
-  #'resampler',
   # AUBIO_UNSTABLE
   'hist',
   'parameter',
@@ -36,20 +39,71 @@ skip_objects = [
   'source_wavread',
   #'sampler',
   'audio_unit',
-
-  'tss',
   ]
 
+def get_preprocessor():
+    # findout which compiler to use
+    from distutils.sysconfig import customize_compiler
+    compiler_name = distutils.ccompiler.get_default_compiler()
+    compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)
+    try:
+        customize_compiler(compiler)
+    except AttributeError as e:
+        print("Warning: failed customizing compiler ({:s})".format(repr(e)))
+
+    if hasattr(compiler, 'initialize'):
+        try:
+            compiler.initialize()
+        except ValueError as e:
+            print("Warning: failed initializing compiler ({:s})".format(repr(e)))
+
+    cpp_cmd = None
+    if hasattr(compiler, 'preprocessor'): # for unixccompiler
+        cpp_cmd = compiler.preprocessor
+    elif hasattr(compiler, 'compiler'): # for ccompiler
+        cpp_cmd = compiler.compiler.split()
+        cpp_cmd += ['-E']
+    elif hasattr(compiler, 'cc'): # for msvccompiler
+        cpp_cmd = compiler.cc.split()
+        cpp_cmd += ['-E']
+
+    if not cpp_cmd:
+        print("Warning: could not guess preprocessor, using env's CC")
+        cpp_cmd = os.environ.get('CC', 'cc').split()
+        cpp_cmd += ['-E']
+
+    return cpp_cmd
 
-def get_cpp_objects():
+def get_cpp_objects(header=header, usedouble=False):
+    cpp_cmd = get_preprocessor()
 
-    cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=1 -I../build/src ../src/aubio.h').readlines()]
-    #cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=0 -I../build/src ../src/onset/onset.h').readlines()]
-    #cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=0 -I../build/src ../src/pitch/pitch.h').readlines()]
+    macros = [('AUBIO_UNSTABLE', 1)]
+    if usedouble:
+        macros += [('HAVE_AUBIO_DOUBLE', 1)]
+
+    if not os.path.isfile(header):
+        raise Exception("could not find include file " + header)
+
+    includes = [os.path.dirname(header)]
+    cpp_cmd += distutils.ccompiler.gen_preprocess_options(macros, includes)
+    cpp_cmd += [header]
+
+    print("Running command: {:s}".format(" ".join(cpp_cmd)))
+    proc = subprocess.Popen(cpp_cmd,
+            stderr=subprocess.PIPE,
+            stdout=subprocess.PIPE)
+    assert proc, 'Proc was none'
+    cpp_output = proc.stdout.read()
+    err_output = proc.stderr.read()
+    if not cpp_output:
+        raise Exception("preprocessor output is empty:\n%s" % err_output)
+    elif err_output:
+        print ("Warning: preprocessor produced warnings:\n%s" % err_output)
+    if not isinstance(cpp_output, list):
+        cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')]
 
     cpp_output = filter(lambda y: len(y) > 1, cpp_output)
-    cpp_output = filter(lambda y: not y.startswith('#'), cpp_output)
-    cpp_output = list(cpp_output)
+    cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output))
 
     i = 1
     while 1:
@@ -66,10 +120,8 @@ def get_cpp_objects():
 
     return cpp_output, cpp_objects
 
-def generate_external(output_path):
-    os.mkdir(output_path)
-    sources_list = []
-    cpp_output, cpp_objects = get_cpp_objects()
+
+def analyze_cpp_output(cpp_objects, cpp_output):
     lib = {}
 
     for o in cpp_objects:
@@ -99,8 +151,9 @@ def generate_external(output_path):
                 else:
                     #print "no idea what to do about", fn
                     lib[shortname]['other'].append(fn)
+    return lib
 
-    """
+def print_cpp_output_results(lib, cpp_output):
     for fn in cpp_output:
         found = 0
         for o in lib:
@@ -108,7 +161,7 @@ def generate_external(output_path):
                 if fn in lib[o][family]:
                     found = 1
         if found == 0:
-            print "missing", fn
+            print ("missing", fn)
 
     for o in lib:
         for family in lib[o]:
@@ -116,14 +169,27 @@ def generate_external(output_path):
                 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )
             elif len(lib[o][family]) == 1:
                 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ) )
-            else:                                             
-                print ( "{:15s} {:10s} {:d}".format(o, family, len(lib[o][family]) ) )
-    """
+            else:
+                print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )
 
-    from .gen_code import MappedObject
+
+def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True):
+    if not os.path.isdir(output_path): os.mkdir(output_path)
+    elif not overwrite: return sorted(glob.glob(os.path.join(output_path, '*.c')))
+
+    cpp_output, cpp_objects = get_cpp_objects(header, usedouble=usedouble)
+
+    lib = analyze_cpp_output(cpp_objects, cpp_output)
+    # print_cpp_output_results(lib, cpp_output)
+
+    sources_list = []
+    try:
+        from .gen_code import MappedObject
+    except (SystemError, ValueError):
+        from gen_code import MappedObject
     for o in lib:
-        out = header
-        mapped = MappedObject(lib[o])
+        out = source_header
+        mapped = MappedObject(lib[o], usedouble = usedouble)
         out += mapped.gen_code()
         output_file = os.path.join(output_path, 'gen-%s.c' % o)
         with open(output_file, 'w') as f:
@@ -131,7 +197,7 @@ def generate_external(output_path):
             print ("wrote %s" % output_file )
             sources_list.append(output_file)
 
-    out = header
+    out = source_header
     out += "#include \"aubio-generated.h\""
     check_types = "\n     ||  ".join(["PyType_Ready(&Py_%sType) < 0" % o for o in lib])
     out += """
@@ -158,12 +224,19 @@ void add_generated_objects ( PyObject *m )
         f.write(out)
         print ("wrote %s" % output_file )
         sources_list.append(output_file)
-    
+
     objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])
-    out = """
-// generated list of objects created with gen_external.py
-#include <Python.h>
+    out = """// generated list of objects created with gen_external.py
 
+#include <Python.h>
+"""
+    if usedouble:
+        out += """
+#ifndef HAVE_AUBIO_DOUBLE
+#define HAVE_AUBIO_DOUBLE 1
+#endif
+"""
+    out += """
 {objlist}
 int generated_objects ( void );
 void add_generated_objects( PyObject *m );
@@ -178,5 +251,6 @@ void add_generated_objects( PyObject *m );
     return sources_list
 
 if __name__ == '__main__':
-    output_path = 'gen'
-    generate_external(output_path)
+    if len(sys.argv) > 1: header = sys.argv[1]
+    if len(sys.argv) > 2: output_path = sys.argv[2]
+    generate_external(header, output_path)