diff --git a/SConstruct b/SConstruct index de176f194..ee9d22089 100644 --- a/SConstruct +++ b/SConstruct @@ -287,6 +287,30 @@ opts.AddVariables( default on Windows), then the Package will be installed to the system default 'site-packages' directory.""", defaults.python_prefix, PathVariable.PathAccept), + EnumVariable( + 'python3_package', + """Controls whether or not the Python 3 module will be built. By + default, the module will be built if the Python 3 interpreter can + be found""", + 'default', ('y','n','default')), + PathVariable( + 'python3_cmd', + """ The name (full path if necessary) of the Python 3 interpreter. + Required to build the Python 3 module.""", + 'python3', PathVariable.PathAccept), + PathVariable( + 'python3_array_home', + """"If numpy was installed to a custom location (e.g. using the --home + option, set this to the directory for numpy""", + '', PathVariable.PathAccept), + PathVariable( + 'python3_prefix', + """Use this option if you want to install the Cantera Python 3 package to + an alternate location. On Unix-like systems, the default is the same + as the $prefix option. If this option is set to the empty string (the + default on Windows), then the Package will be installed to the system + default 'site-packages' directory.""", + defaults.python_prefix, PathVariable.PathAccept), EnumVariable( 'matlab_toolbox', """This variable controls whether the Matlab toolbox will be built. If @@ -739,6 +763,8 @@ int main(int argc, char** argv) { env = conf.Finish() + +# Python 2 Package Settings if env['python_package'] in ('full','default'): # Test to see if we can import the specified array module warnNoPython = False @@ -769,6 +795,27 @@ else: env['python_array_include'] = '' +# Python 3 Package Settings +if env['python3_package'] in ('y', 'default'): + # See if we can execute the Python 3 interpreter + try: + ret = subprocess.check_call([env['python3_cmd'],'-c','""']) + except OSError: + ret = -1 + + if ret: + if env['python3_package'] == 'default': + print ('INFO: Not building the Python 3 package because the Python ' + '3 interpreter %r could not be found' % env['python3_cmd']) + env['python3_package'] = 'n' + else: + print ('ERROR: Could not execute the Python 3 interpreter %r' % + env['python3_cmd']) + sys.exit(1) + else: + env['python3_package'] = 'y' + + # Matlab Toolbox settings if env['matlab_path'] != '' and env['matlab_toolbox'] == 'default': env['matlab_toolbox'] = 'y' @@ -1154,6 +1201,10 @@ if env['python_package'] in ('full','minimal'): VariantDir('build/src/python', 'src/python', duplicate=0) SConscript('build/src/python/SConscript') +if env['python3_package'] == 'y': + VariantDir('build/python3', 'interfaces/cython', duplicate=0) + SConscript('build/cython/SConscript') + SConscript('build/src/apps/SConscript') if env['OS'] != 'Windows': diff --git a/interfaces/cython/SConscript b/interfaces/cython/SConscript new file mode 100644 index 000000000..c48de9dac --- /dev/null +++ b/interfaces/cython/SConscript @@ -0,0 +1,49 @@ +""" Cython-based Python Module for Python 3 """ +from buildutils import * + +Import('env', 'build', 'install') + +localenv = env.Clone() + +libDirs = ('../../build/lib', localenv['sundials_libdir'], + localenv['boost_lib_dir']) +localenv['py_cantera_libs'] = repr(localenv['cantera_libs']) +localenv['py_libdirs'] = repr([x for x in libDirs if x]) + +if localenv['CC'] == 'cl': + localenv['py_extra_compiler_args'] = repr(['/EHsc']) +else: + localenv['py_extra_compiler_args'] = repr([]) + +dataFiles = localenv.RecursiveInstall('#interfaces/cython/cantera/data', + '#data/inputs', ['\\.inp$', '\\.in$']) +build(dataFiles) + +if localenv['python3_package'] == 'y': + info = getCommandOutput(localenv['python3_cmd'], '-c', + '\n'.join(("from distutils.sysconfig import *", + "import numpy", + "print(get_config_var('SO'))", + "print(numpy.get_include())"))) + + module_extension, localenv['python3_array_include'] = info.split('\n') + + incDirs = (".", "../../include", localenv['python3_array_include'], + localenv['sundials_include'], localenv['boost_inc_dir']) + localenv['py_include_dirs'] = repr([x for x in incDirs if x]) + + + make_setup = localenv.SubstFile('#interfaces/cython/setup.py', + '#interfaces/cython/setup.py.in') + build(make_setup) + + opts = '--build-lib=../../build/python3 --build-temp=../../build/temp' + mod = build(localenv.Command('cantera/_cantera%s' % module_extension, + 'setup.py', + 'cd interfaces/cython && ' + '$python3_cmd $SOURCE.name build %s' % opts)) + + localenv.AddPreAction(mod, Delete('interfaces/cython/cantera/_cantera.cpp')) + + for f in mglob(localenv, 'cantera', 'pyx', 'pxd'): + localenv.Depends(mod, f) diff --git a/site_scons/buildutils.py b/site_scons/buildutils.py index 9249ddbc5..5de95690e 100644 --- a/site_scons/buildutils.py +++ b/site_scons/buildutils.py @@ -545,3 +545,18 @@ def getSpawn(env): return rv return ourSpawn + +def getCommandOutput(cmd, *args): + """ + Run a command with arguments and return its output. + Substitute for subprocess.check_output which is only available + in Python >= 2.7 + """ + proc = subprocess.Popen([cmd] + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + data, err = proc.communicate() + if proc.returncode: + raise OSError(err) + + return data.strip() diff --git a/src/python/SConscript b/src/python/SConscript index 79f085cea..c18709425 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -105,30 +105,3 @@ elif localenv['PYTHON_INSTALLER'] == 'binary': if localenv['python_package'] == 'full': install(localenv.RecursiveInstall, pjoin('$inst_sampledir', 'python'), '#samples/python') - -################################# -### New Python Module: Cython ### -################################# - -localenv = env.Clone() -incDirs = (".", "../../include", localenv['python_array_include'], - localenv['sundials_include'], localenv['boost_inc_dir']) -libDirs = ('../../build/lib', localenv['sundials_libdir'], - localenv['boost_lib_dir']) - -localenv['py_include_dirs'] = repr([x for x in incDirs if x]) -localenv['py_cantera_libs'] = repr(localenv['cantera_libs']) -localenv['py_libdirs'] = repr([x for x in libDirs if x]) - -if localenv['CC'] == 'cl': - localenv['py_extra_compiler_args'] = repr(['/EHsc']) -else: - localenv['py_extra_compiler_args'] = repr([]) - -dataFiles = localenv.RecursiveInstall('#interfaces/cython/cantera/data', - '#data/inputs', ['\\.inp$', '\\.in$']) -build(dataFiles) - -make_setup = localenv.SubstFile('#interfaces/cython/setup.py', - '#interfaces/cython/setup.py.in') -build(make_setup)