""" Cython-based Python Module for Python 3 """ from buildutils import * import Cython.Build Import('env', 'build', 'install') localenv = env.Clone() def configure_python(env, python_command): script = '\n'.join(("from distutils.sysconfig import *", "import numpy", "print(get_config_var('EXT_SUFFIX') or get_config_var('SO'))", "print(get_config_var('INCLUDEPY'))", "print(get_config_var('LDLIBRARY'))", "print(get_config_var('prefix'))", "print(get_python_version())", "print(numpy.get_include())")) info = getCommandOutput(python_command, '-c', script) module_ext, inc, pylib, prefix, py_version, numpy_include = info.splitlines()[-6:] env.Prepend(CPPPATH=[Dir('#include'), inc, numpy_include]) env.Prepend(LIBS=env['cantera_libs']) if env['OS'] == 'Darwin': env.Append(LINKFLAGS='-undefined dynamic_lookup') elif env['OS'] == 'Windows': env.Append(LIBPATH=prefix+'/libs') if env['toolchain'] == 'mingw': env.Append(LIBS='python%s' % py_version.replace('.','')) if env['OS_BITS'] == 64: env.Append(CPPDEFINES='MS_WIN64') # fix for http://bugs.python.org/issue11566 env.Append(CPPDEFINES={'_hypot':'hypot'}) elif env['OS'] == 'Cygwin': # extract 'pythonX.Y' from 'libpythonX.Y.dll.a' env.Append(LIBS=pylib[3:-6]) return module_ext, py_version def add_dependencies(mod, ext): localenv.Depends(mod, ext) localenv.Depends(mod, dataFiles + testFiles) localenv.Depends(ext, localenv['cantera_staticlib']) for f in (mglob(localenv, 'cantera', 'py') + mglob(localenv, 'cantera/test', 'py') + mglob(localenv, 'cantera/mixmaster', 'py') + mglob(localenv, 'cantera/mixmaster/Units', 'py') + mglob(localenv, 'cantera/examples/tutorial', 'py') + mglob(localenv, 'cantera/examples/equilibrium', 'py') + mglob(localenv, 'cantera/examples/kinetics', 'py') + mglob(localenv, 'cantera/examples/transport', 'py') + mglob(localenv, 'cantera/examples/reactors', 'py') + mglob(localenv, 'cantera/examples/onedim', 'py') + mglob(localenv, 'cantera/examples/surface_chemistry', 'py') + mglob(localenv, 'cantera/examples/misc', 'py')): localenv.Depends(mod, f) def cythonize(target, source, env): Cython.Build.cythonize([f.abspath for f in source]) cythonized = localenv.Command('cantera/_cantera.cpp', ['cantera/_cantera.pyx'], cythonize) for f in mglob(localenv, 'cantera', 'pyx', 'pxd'): localenv.Depends(cythonized, f) for line in open('cantera/_cantera.pxd'): m = re.search(r'from "(cantera.*?)"', line) if m: localenv.Depends('cantera/_cantera.cpp', '#include/' + m.group(1)) def install_module(prefix, python_version): major = python_version[0] minor = python_version.split('.')[1] ver = '3' if major == '3' else '' dummy = 'dummy' + major if prefix == 'USER': # Install to the OS-dependent user site-packages directory extra = '--user' if localenv['OS'] == 'Darwin': extra += ' --prefix=""' elif prefix: # A specific location for the Cantera python module has been given if localenv['debian'] and localenv.subst(prefix) == '/usr/local': # Installation to /usr/local is the default on Debian-based distributions extra = '' elif localenv['OS'] == 'Darwin': extra = localenv.subst(' --prefix=${python%s_prefix}' % ver) elif localenv['libdirname'] == 'lib64': # 64-bit RHEL / Fedora extra = localenv.subst( ' --prefix=${python%s_prefix} --install-lib=${python%s_prefix}/lib64/python%s.%s/site-packages' % (ver, ver, major, minor)) else: extra = '--user' localenv.AppendENVPath( 'PYTHONUSERBASE', normpath(localenv.subst('$python%s_prefix' % ver))) else: # Install Python module in the default location extra = '' if localenv['PYTHON_INSTALLER'] == 'direct': mod_inst = install(localenv.Command, dummy, mod, build_cmd + ' install %s' % extra + ' --record=../../build/python%s-installed-files.txt' % major + ' --single-version-externally-managed') global_env = env def find_module_dir(target, source, env): check = pjoin('cantera','__init__.py') for filename in open('build/python%s-installed-files.txt' % major).readlines(): filename = filename.strip() if filename.endswith(check): filename = filename.replace(check,'') global_env['python%s_module_loc' % ver] = normpath(filename) break localenv.AlwaysBuild(localenv.AddPostAction(mod_inst, find_module_dir)) if not ver: env['install_python2_action'] = mod_inst elif localenv['PYTHON_INSTALLER'] == 'debian': install(localenv.Command, dummy, mod, build_cmd + ' install --install-layout=deb --no-compile %s' % extra) env['python%s_module_loc' % ver] = '' elif localenv['PYTHON_INSTALLER'] == 'binary': install(localenv.Command, dummy, mod, build_cmd + ' bdist_msi --dist-dir=../..' + ' --target-version=%s' % python_version) env['python%s_module_loc' % ver] = '' dataFiles = localenv.RecursiveInstall('#interfaces/cython/cantera/data', '#build/data') build(dataFiles) testFiles = localenv.RecursiveInstall('#interfaces/cython/cantera/test/data', '#test/data') build(testFiles) # Cython module for Python 3.x if localenv['python3_package'] == 'y': py3env = localenv.Clone() module_ext, py3_version = configure_python(py3env, py3env['python3_cmd']) obj = py3env.SharedObject('#build/temp-py/_cantera3', 'cantera/_cantera.cpp') ext = py3env.LoadableModule('#build/python3/cantera/_cantera%s' % module_ext, obj, LIBPREFIX='', SHLIBSUFFIX=module_ext, SHLIBPREFIX='', LIBSUFFIXES=[module_ext]) py3env['py_extension'] = ext[0].name py3env.SubstFile('setup3.py', 'setup.py.in') build_cmd = ('cd interfaces/cython &&' ' $python3_cmd setup3.py build --build-lib=../../build/python3') mod = build(py3env.Command('#build/python3/cantera/__init__.py', 'setup3.py', build_cmd)) env['python3_module'] = mod env['python3_extension'] = ext add_dependencies(mod, ext) install_module(py3env['python3_prefix'], py3_version) # Cython module for Python 2.x if localenv['python_package'] == 'full': py2env = localenv.Clone() module_ext, py2_version = configure_python(py2env, py2env['python_cmd']) obj = py2env.SharedObject('#build/temp-py/_cantera2', 'cantera/_cantera.cpp') ext = py2env.LoadableModule('#build/python2/cantera/_cantera%s' % module_ext, obj, LIBPREFIX='', SHLIBSUFFIX=module_ext, SHLIBPREFIX='', LIBSUFFIXES=[module_ext]) py2env['py_extension'] = ext[0].name py2env.SubstFile('setup2.py', 'setup.py.in') build_cmd = ('cd interfaces/cython &&' ' $python_cmd_esc setup2.py build --build-lib=../../build/python2') mod = build(py2env.Command('#build/python2/cantera/__init__.py', 'setup2.py', build_cmd)) env['python2_module'] = mod env['python2_extension'] = ext # Use 3to2 to convert examples from Python 3 syntax if env['python_convert_examples']: def convert_example(target, source, env): shutil.copyfile(source[0].abspath, target[0].abspath) if env['OS'] == 'Windows': python_dir = os.path.dirname(which(env['python_cmd'])) threetotwo_cmd = pjoin(python_dir, 'Scripts', '3to2') subprocess.call([env['python_cmd'], threetotwo_cmd, '--no-diff', '-n', '-w','-x', 'str', '-f', 'all', '-f', 'printfunction', '-x', 'print', '-x', 'open', target[0].abspath]) else: subprocess.call(['3to2', '--no-diff', '-n', '-w','-x', 'str', '-f', 'all', '-f', 'printfunction', '-x', 'print', '-x', 'open', target[0].abspath]) for subdir in subdirs('cantera/examples'): dirpath = pjoin('cantera', 'examples', subdir) for filename in os.listdir(dirpath): if not filename.endswith('.py'): continue targetdir = '../../build/python2/cantera/examples' a = build(py2env.Command(pjoin(targetdir, subdir, filename), pjoin(dirpath, filename), convert_example)) py2env.Depends(a, mod) add_dependencies(mod, ext) install_module(py2env['python_prefix'], py2_version)