From 2675227fe5bfe31f666dfba6ebd6c6bf1d91863b Mon Sep 17 00:00:00 2001 From: Paul Brossier Date: Sat, 14 May 2016 21:45:03 +0200 Subject: [PATCH] setup.py: clean-up, add option to build libaubio inside python-aubio The setup script now attempts to build the _aubio extension as follows: - if src/aubio.h is found, use it to generate python/gen/ files - if build/src/ is found, use it to link python-aubio against libaubio - otherwise: - add all libaubio source (src/*) to the python module sources - look for optional dependencies using pkg-config - set flags accordingly - otherwise, look for aubio headers and libraries using pkg-config This should help building the python module in a virtualenv (#2), on windows (#55), and allow installing aubio directly with pip. --- python/lib/moresetuptools.py | 110 ++++++++++++++++++++++++++++++++++++++++++- setup.py | 48 ++++++++++--------- 2 files changed, 134 insertions(+), 24 deletions(-) diff --git a/python/lib/moresetuptools.py b/python/lib/moresetuptools.py index ac5c8fb9..3c2e4cb9 100644 --- a/python/lib/moresetuptools.py +++ b/python/lib/moresetuptools.py @@ -1,6 +1,114 @@ +""" A collection of function used from setup.py distutils script """ +# +import sys, os, glob, subprocess import distutils, distutils.command.clean, distutils.dir_util from .gen_external import generate_external, header, output_path +# inspired from https://gist.github.com/abergmeier/9488990 +def add_packages(packages, ext=None, **kw): + """ use pkg-config to search which of 'packages' are installed """ + flag_map = { + '-I': 'include_dirs', + '-L': 'library_dirs', + '-l': 'libraries'} + + # if a setuptools extension is passed, fill it with pkg-config results + if ext: + kw = {'include_dirs': ext.include_dirs, + 'extra_link_args': ext.extra_link_args, + 'library_dirs': ext.library_dirs, + 'libraries': ext.libraries, + } + + for package in packages: + try: + cmd = ['pkg-config', '--libs', '--cflags', package] + tokens = subprocess.check_output(cmd) + except subprocess.CalledProcessError: + print("{:s} could not be found".format(package)) + continue + tokens = tokens.decode('utf8').split() + for token in tokens: + key = token[:2] + try: + arg = flag_map[key] + value = token[2:] + except KeyError: + arg = 'extra_link_args' + value = token + kw.setdefault(arg, []).append(value) + for key, value in iter(kw.items()): # remove duplicated + kw[key] = list(set(value)) + return kw + +def add_local_aubio_header(ext): + """ use local "src/aubio.h", not """ + ext.define_macros += [('USE_LOCAL_AUBIO', 1)] + ext.include_dirs += ['src'] # aubio.h + +def add_local_aubio_lib(ext): + """ add locally built libaubio from build/src """ + print("Info: using locally built libaubio") + ext.library_dirs += [os.path.join('build', 'src')] + ext.libraries += ['aubio'] + +def add_local_aubio_sources(ext): + """ build aubio inside python module instead of linking against libaubio """ + print("Warning: libaubio was not built with waf, adding src/") + # create an empty header, macros will be passed on the command line + fake_config_header = os.path.join('python', 'ext', 'config.h') + distutils.file_util.write_file(fake_config_header, "") + aubio_sources = glob.glob(os.path.join('src', '**.c')) + aubio_sources += glob.glob(os.path.join('src', '*', '**.c')) + ext.sources += aubio_sources + # define macros (waf puts them in build/src/config.h) + for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H', + 'HAVE_MATH_H', 'HAVE_STRING_H', + 'HAVE_LIMITS_H', 'HAVE_MEMCPY_HACKS']: + ext.define_macros += [(define_macro, 1)] + + # loof for additional packages + print("Info: looking for *optional* additional packages") + packages = ['libavcodec', 'libavformat', 'libavutil', 'libavresample', + 'jack', + 'sndfile', 'samplerate', + #'fftw3f', + ] + add_packages(packages, ext=ext) + if 'avcodec' in ext.libraries \ + and 'avformat' in ext.libraries \ + and 'avutil' in ext.libraries \ + and 'avresample' in ext.libraries: + ext.define_macros += [('HAVE_LIBAV', 1)] + if 'jack' in ext.libraries: + ext.define_macros += [('HAVE_JACK', 1)] + if 'sndfile' in ext.libraries: + ext.define_macros += [('HAVE_SNDFILE', 1)] + if 'samplerate' in ext.libraries: + ext.define_macros += [('HAVE_SAMPLERATE', 1)] + if 'fftw3f' in ext.libraries: + ext.define_macros += [('HAVE_FFTW3F', 1)] + ext.define_macros += [('HAVE_FFTW3', 1)] + + # add accelerate on darwin + if sys.platform.startswith('darwin'): + ext.extra_link_args += ['-framework', 'accelerate'] + ext.define_macros += [('HAVE_ACCELERATE', 1)] + + ext.define_macros += [('HAVE_WAVWRITE', 1)] + ext.define_macros += [('HAVE_WAVREAD', 1)] + # TODO: + # add cblas + if 0: + ext.libraries += ['cblas'] + ext.define_macros += [('HAVE_ATLAS_CBLAS_H', 1)] + +def add_system_aubio(ext): + # use pkg-config to find aubio's location + add_packages(['aubio'], ext) + if 'aubio' not in ext.libraries: + print("Error: libaubio not found") + class CleanGenerated(distutils.command.clean.clean): def run(self): distutils.dir_util.remove_tree(output_path) @@ -24,4 +132,4 @@ class GenerateCommand(distutils.cmd.Command): def run(self): self.announce( 'Generating code', level=distutils.log.INFO) - generated_object_files = generate_external(header, output_path, usedouble = self.enable_double) + generated_object_files = generate_external(header, output_path, usedouble=self.enable_double) diff --git a/setup.py b/setup.py index 269f15c9..9535a3be 100755 --- a/setup.py +++ b/setup.py @@ -1,10 +1,9 @@ #! /usr/bin/env python -import sys -import os.path +import sys, os.path, glob import numpy from setuptools import setup, Extension -from python.lib.moresetuptools import CleanGenerated, GenerateCommand +from python.lib.moresetuptools import * # function to generate gen/*.{c,h} from python.lib.gen_external import generate_external, header, output_path @@ -26,30 +25,33 @@ include_dirs += [ numpy.get_include() ] if sys.platform.startswith('darwin'): extra_link_args += ['-framework','CoreFoundation', '-framework','AudioToolbox'] -if os.path.isfile('src/aubio.h'): - define_macros += [('USE_LOCAL_AUBIO', 1)] - include_dirs += ['src'] # aubio.h - library_dirs += ['build/src'] +sources = glob.glob(os.path.join('python', 'ext', '*.c')) -aubio_extension = Extension("aubio._aubio", [ - "python/ext/aubiomodule.c", - "python/ext/aubioproxy.c", - "python/ext/ufuncs.c", - "python/ext/py-musicutils.c", - "python/ext/py-cvec.c", - "python/ext/py-filter.c", - "python/ext/py-filterbank.c", - "python/ext/py-fft.c", - "python/ext/py-phasevoc.c", - "python/ext/py-source.c", - "python/ext/py-sink.c", - # generate files if they don't exit - ] + generate_external(header, output_path, overwrite = False), +aubio_extension = Extension("aubio._aubio", + sources, include_dirs = include_dirs, library_dirs = library_dirs, extra_link_args = extra_link_args, - define_macros = define_macros, - libraries=['aubio']) + define_macros = define_macros) + +if os.path.isfile('src/aubio.h'): + # if aubio headers are found in this directory + add_local_aubio_header(aubio_extension) + # was waf used to build the shared lib? + if os.path.isdir(os.path.join('build','src')): + # link against build/src/libaubio, built with waf + add_local_aubio_lib(aubio_extension) + else: + # add libaubio sources and look for optional deps with pkg-config + add_local_aubio_sources(aubio_extension) + __version__ += '_libaubio' +else: + # look for aubio headers and lib using pkg-config + add_system_aubio(aubio_extension) + + +# generate files if they don't exit +aubio_extension.sources += generate_external(header, output_path, overwrite = False) classifiers = [ 'Development Status :: 4 - Beta', -- 2.11.0