mirror of
https://github.com/Cantera/cantera.git
synced 2025-02-25 18:55:29 -06:00
1974 lines
79 KiB
Python
1974 lines
79 KiB
Python
"""
|
|
SCons build script for Cantera
|
|
|
|
Basic usage:
|
|
'scons help' - print a description of user-specifiable options.
|
|
|
|
'scons build' - Compile Cantera and the language interfaces using
|
|
default options.
|
|
|
|
'scons clean' - Delete files created while building Cantera.
|
|
|
|
'scons install' - Install Cantera.
|
|
|
|
'scons uninstall' - Uninstall Cantera.
|
|
|
|
'scons test' - Run all tests which did not previously pass or for which the
|
|
results may have changed.
|
|
|
|
'scons test-reset' - Reset the passing status of all tests.
|
|
|
|
'scons test-clean' - Delete files created while running the tests.
|
|
|
|
'scons test-help' - List available tests.
|
|
|
|
'scons test-NAME' - Run the test named "NAME".
|
|
|
|
'scons <command> dump' - Dump the state of the SCons environment to the
|
|
screen instead of doing <command>, e.g.
|
|
'scons build dump'. For debugging purposes.
|
|
|
|
'scons samples' - Compile the C++ and Fortran samples.
|
|
|
|
'scons msi' - Build a Windows installer (.msi) for Cantera.
|
|
|
|
'scons sphinx' - Build the Sphinx documentation
|
|
|
|
'scons doxygen' - Build the Doxygen documentation
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
from buildutils import *
|
|
|
|
if not COMMAND_LINE_TARGETS:
|
|
# Print usage help
|
|
print(__doc__)
|
|
sys.exit(0)
|
|
|
|
valid_commands = ('build','clean','install','uninstall',
|
|
'help','msi','samples','sphinx','doxygen','dump')
|
|
|
|
for command in COMMAND_LINE_TARGETS:
|
|
if command not in valid_commands and not command.startswith('test'):
|
|
print('ERROR: unrecognized command line target: %r' % command)
|
|
sys.exit(0)
|
|
|
|
extraEnvArgs = {}
|
|
|
|
if 'clean' in COMMAND_LINE_TARGETS:
|
|
removeDirectory('build')
|
|
removeDirectory('stage')
|
|
removeDirectory('.sconf_temp')
|
|
removeDirectory('test/work')
|
|
removeFile('.sconsign.dblite')
|
|
removeFile('include/cantera/base/config.h')
|
|
removeFile('src/pch/system.h.gch')
|
|
removeDirectory('include/cantera/ext')
|
|
removeFile('interfaces/cython/cantera/_cantera.cpp')
|
|
removeFile('interfaces/cython/cantera/_cantera.h')
|
|
removeFile('interfaces/cython/setup.py')
|
|
removeFile('interfaces/python_minimal/setup.py')
|
|
removeFile('config.log')
|
|
removeDirectory('doc/sphinx/matlab/examples')
|
|
removeFile('doc/sphinx/matlab/examples.rst')
|
|
for name in os.listdir('doc/sphinx/matlab/'):
|
|
if name.endswith('.rst') and name != 'index.rst':
|
|
removeFile('doc/sphinx/matlab/' + name)
|
|
removeDirectory('doc/sphinx/cython/examples')
|
|
removeFile('doc/sphinx/cython/examples.rst')
|
|
removeDirectory('interfaces/cython/Cantera.egg-info')
|
|
removeDirectory('interfaces/python_minimal/Cantera_minimal.egg-info')
|
|
for name in os.listdir('interfaces/cython/cantera/data/'):
|
|
if os.path.isdir('interfaces/cython/cantera/data/' + name):
|
|
removeDirectory('interfaces/cython/cantera/data/' + name)
|
|
elif name != '__init__.py':
|
|
removeFile('interfaces/cython/cantera/data/' + name)
|
|
removeDirectory('interfaces/cython/cantera/test/data/test_subdir')
|
|
for name in os.listdir('interfaces/cython/cantera/test/data/'):
|
|
if name != '__init__.py':
|
|
removeFile('interfaces/cython/cantera/test/data/' + name)
|
|
for name in os.listdir('.'):
|
|
if name.endswith('.msi'):
|
|
removeFile(name)
|
|
for name in os.listdir('site_scons/'):
|
|
if name.endswith('.pyc'):
|
|
removeFile('site_scons/' + name)
|
|
for name in os.listdir('site_scons/site_tools/'):
|
|
if name.endswith('.pyc'):
|
|
removeFile('site_scons/site_tools/' + name)
|
|
for name in os.listdir('interfaces/python_minimal/cantera'):
|
|
if name != '__init__.py':
|
|
removeFile('interfaces/python_minimal/cantera/' + name)
|
|
removeFile('interfaces/matlab/toolbox/cantera_shared.dll')
|
|
removeFile('interfaces/matlab/Contents.m')
|
|
removeFile('interfaces/matlab/ctpath.m')
|
|
for name in os.listdir('interfaces/matlab/toolbox'):
|
|
if name.startswith('ctmethods.'):
|
|
removeFile('interfaces/matlab/toolbox/' + name)
|
|
|
|
print('Done removing output files.')
|
|
|
|
if COMMAND_LINE_TARGETS == ['clean']:
|
|
# Just exit if there's nothing else to do
|
|
sys.exit(0)
|
|
else:
|
|
Alias('clean', [])
|
|
|
|
if 'test-clean' in COMMAND_LINE_TARGETS:
|
|
removeDirectory('build/test')
|
|
removeDirectory('test/work')
|
|
removeDirectory('build/python_local')
|
|
|
|
# ******************************************************
|
|
# *** Set system-dependent defaults for some options ***
|
|
# ******************************************************
|
|
|
|
print('INFO: SCons is using the following Python interpreter:', sys.executable)
|
|
|
|
opts = Variables('cantera.conf')
|
|
|
|
windows_compiler_options = []
|
|
if os.name == 'nt':
|
|
# On Windows, target the same architecture as the current copy of Python,
|
|
# unless the user specified another option.
|
|
if '64 bit' in sys.version:
|
|
target_arch = 'amd64'
|
|
else:
|
|
target_arch = 'x86'
|
|
|
|
# Make an educated guess about the right default compiler
|
|
if which('g++') and not which('cl.exe'):
|
|
defaultToolchain = 'mingw'
|
|
else:
|
|
defaultToolchain = 'msvc'
|
|
|
|
windows_compiler_options.extend([
|
|
('msvc_version',
|
|
"""Version of Visual Studio to use. The default is the newest
|
|
installed version. Specify '12.0' for Visual Studio 2013 or '14.0'
|
|
for Visual Studio 2015.""",
|
|
''),
|
|
EnumVariable(
|
|
'target_arch',
|
|
"""Target architecture. The default is the same architecture as the
|
|
installed version of Python.""",
|
|
target_arch, ('amd64', 'x86'))
|
|
])
|
|
opts.AddVariables(*windows_compiler_options)
|
|
|
|
pickCompilerEnv = Environment()
|
|
opts.Update(pickCompilerEnv)
|
|
|
|
if pickCompilerEnv['msvc_version']:
|
|
defaultToolchain = 'msvc'
|
|
|
|
windows_compiler_options.append(EnumVariable(
|
|
'toolchain',
|
|
"""The preferred compiler toolchain.""",
|
|
defaultToolchain, ('msvc', 'mingw', 'intel')))
|
|
opts.AddVariables(windows_compiler_options[-1])
|
|
opts.Update(pickCompilerEnv)
|
|
|
|
if pickCompilerEnv['toolchain'] == 'msvc':
|
|
toolchain = ['default']
|
|
if pickCompilerEnv['msvc_version']:
|
|
extraEnvArgs['MSVC_VERSION'] = pickCompilerEnv['msvc_version']
|
|
print('INFO: Compiling with MSVC', (pickCompilerEnv['msvc_version'] or
|
|
pickCompilerEnv['MSVC_VERSION']))
|
|
|
|
elif pickCompilerEnv['toolchain'] == 'mingw':
|
|
toolchain = ['mingw', 'f90']
|
|
extraEnvArgs['F77'] = None
|
|
# Next line fixes http://scons.tigris.org/issues/show_bug.cgi?id=2683
|
|
extraEnvArgs['WINDOWS_INSERT_DEF'] = 1
|
|
|
|
elif pickCompilerEnv['toolchain'] == 'intel':
|
|
toolchain = ['intelc'] # note: untested
|
|
|
|
extraEnvArgs['TARGET_ARCH'] = pickCompilerEnv['target_arch']
|
|
print('INFO: Compiling for architecture:', pickCompilerEnv['target_arch'])
|
|
print('INFO: Compiling using the following toolchain(s):', repr(toolchain))
|
|
|
|
else:
|
|
toolchain = ['default']
|
|
|
|
env = Environment(tools=toolchain+['textfile', 'subst', 'recursiveInstall', 'wix', 'gch'],
|
|
ENV={'PATH': os.environ['PATH']},
|
|
toolchain=toolchain,
|
|
**extraEnvArgs)
|
|
|
|
env['OS'] = platform.system()
|
|
env['OS_BITS'] = int(platform.architecture()[0][:2])
|
|
if 'cygwin' in env['OS'].lower():
|
|
env['OS'] = 'Cygwin' # remove Windows version suffix
|
|
|
|
# Fixes a linker error in Windows
|
|
if os.name == 'nt' and 'TMP' in os.environ:
|
|
env['ENV']['TMP'] = os.environ['TMP']
|
|
|
|
# Fixes issues with Python subprocesses. See http://bugs.python.org/issue13524
|
|
if os.name == 'nt':
|
|
env['ENV']['SystemRoot'] = os.environ['SystemRoot']
|
|
|
|
# Needed for Matlab to source ~/.matlab7rc.sh
|
|
if 'HOME' in os.environ:
|
|
env['ENV']['HOME'] = os.environ['HOME']
|
|
|
|
# Fix an issue with Unicode sneaking into the environment on Windows
|
|
if os.name == 'nt':
|
|
for key,val in env['ENV'].items():
|
|
env['ENV'][key] = str(val)
|
|
|
|
if 'FRAMEWORKS' not in env:
|
|
env['FRAMEWORKS'] = []
|
|
|
|
add_RegressionTest(env)
|
|
|
|
class defaults: pass
|
|
|
|
if os.name == 'posix':
|
|
defaults.prefix = '/usr/local'
|
|
defaults.boostIncDir = ''
|
|
env['INSTALL_MANPAGES'] = True
|
|
elif os.name == 'nt':
|
|
defaults.prefix = pjoin(os.environ['ProgramFiles'], 'Cantera')
|
|
defaults.boostIncDir = ''
|
|
env['INSTALL_MANPAGES'] = False
|
|
else:
|
|
print("Error: Unrecognized operating system '%s'" % os.name)
|
|
sys.exit(1)
|
|
|
|
compiler_options = [
|
|
('CXX',
|
|
"""The C++ compiler to use.""",
|
|
env['CXX']),
|
|
('CC',
|
|
"""The C compiler to use. This is only used to compile CVODE.""",
|
|
env['CC'])]
|
|
opts.AddVariables(*compiler_options)
|
|
opts.Update(env)
|
|
|
|
defaults.cxxFlags = ''
|
|
defaults.ccFlags = ''
|
|
defaults.noOptimizeCcFlags = '-O0'
|
|
defaults.optimizeCcFlags = '-O3'
|
|
defaults.debugCcFlags = '-g'
|
|
defaults.noDebugCcFlags = ''
|
|
defaults.debugLinkFlags = ''
|
|
defaults.noDebugLinkFlags = ''
|
|
defaults.warningFlags = '-Wall'
|
|
defaults.buildPch = False
|
|
defaults.sphinx_options = '-W --keep-going'
|
|
env['pch_flags'] = []
|
|
env['openmp_flag'] = ['-fopenmp'] # used to generate sample build scripts
|
|
|
|
env['using_apple_clang'] = False
|
|
# Check if this is actually Apple's clang on macOS
|
|
if env['OS'] == 'Darwin':
|
|
result = subprocess.check_output([env.subst('$CC'), '--version']).decode('utf-8')
|
|
if 'clang' in result.lower() and ('Xcode' in result or 'Apple' in result):
|
|
env['using_apple_clang'] = True
|
|
env['openmp_flag'].insert(0, '-Xpreprocessor')
|
|
|
|
if 'gcc' in env.subst('$CC') or 'gnu-cc' in env.subst('$CC'):
|
|
defaults.optimizeCcFlags += ' -Wno-inline'
|
|
if env['OS'] == 'Cygwin':
|
|
# See http://stackoverflow.com/questions/18784112
|
|
defaults.cxxFlags = '-std=gnu++0x'
|
|
else:
|
|
defaults.cxxFlags = '-std=c++0x'
|
|
defaults.buildPch = True
|
|
env['pch_flags'] = ['-include', 'src/pch/system.h']
|
|
|
|
elif env['CC'] == 'cl': # Visual Studio
|
|
defaults.cxxFlags = ['/EHsc']
|
|
defaults.ccFlags = ['/MD', '/nologo',
|
|
'/D_SCL_SECURE_NO_WARNINGS', '/D_CRT_SECURE_NO_WARNINGS']
|
|
defaults.debugCcFlags = '/Zi /Fd${TARGET}.pdb'
|
|
defaults.noOptimizeCcFlags = '/Od /Ob0'
|
|
defaults.optimizeCcFlags = '/O2'
|
|
defaults.debugLinkFlags = '/DEBUG'
|
|
defaults.warningFlags = '/W3'
|
|
defaults.buildPch = True
|
|
env['pch_flags'] = ['/FIpch/system.h']
|
|
env['openmp_flag'] = ['/openmp']
|
|
|
|
elif 'icc' in env.subst('$CC'):
|
|
defaults.cxxFlags = '-std=c++0x'
|
|
defaults.ccFlags = '-vec-report0 -diag-disable 1478'
|
|
defaults.warningFlags = '-Wcheck'
|
|
env['openmp_flag'] = ['-openmp']
|
|
|
|
elif 'clang' in env.subst('$CC'):
|
|
defaults.ccFlags = '-fcolor-diagnostics'
|
|
defaults.cxxFlags = '-std=c++11'
|
|
defaults.buildPch = True
|
|
env['pch_flags'] = ['-include-pch', 'src/pch/system.h.gch']
|
|
|
|
else:
|
|
print("WARNING: Unrecognized C compiler '%s'" % env['CC'])
|
|
|
|
if env['OS'] in ('Windows', 'Darwin'):
|
|
defaults.threadFlags = ''
|
|
else:
|
|
defaults.threadFlags = '-pthread'
|
|
|
|
# InstallVersionedLib only fully functional in SCons >= 2.4.0
|
|
# SHLIBVERSION fails with MinGW: http://scons.tigris.org/issues/show_bug.cgi?id=3035
|
|
if (env['toolchain'] == 'mingw'
|
|
or parse_version(SCons.__version__) < parse_version('2.4.0')):
|
|
defaults.versionedSharedLibrary = False
|
|
else:
|
|
defaults.versionedSharedLibrary = True
|
|
|
|
defaults.fsLayout = 'compact' if env['OS'] == 'Windows' else 'standard'
|
|
defaults.env_vars = 'PATH,LD_LIBRARY_PATH,PYTHONPATH'
|
|
|
|
defaults.python_prefix = '$prefix' if env['OS'] != 'Windows' else ''
|
|
|
|
# Transform lists into strings to keep cantera.conf clean
|
|
for key,value in defaults.__dict__.items():
|
|
if isinstance(value, (list, tuple)):
|
|
setattr(defaults, key, ' '.join(value))
|
|
|
|
# **************************************
|
|
# *** Read user-configurable options ***
|
|
# **************************************
|
|
|
|
config_options = [
|
|
PathVariable(
|
|
'prefix',
|
|
'Set this to the directory where Cantera should be installed.',
|
|
defaults.prefix, PathVariable.PathAccept),
|
|
PathVariable(
|
|
'libdirname',
|
|
"""Set this to the directory where Cantera libraries should be installed.
|
|
Some distributions (for example, Fedora/RHEL) use 'lib64' instead of 'lib' on 64-bit systems
|
|
or could use some other library directory name instead of 'lib' depends
|
|
on architecture and profile (for example, Gentoo 'libx32' on x32 profile).
|
|
If user didn't set 'libdirname' configuration variable set it to default value 'lib'""",
|
|
'lib', PathVariable.PathAccept),
|
|
EnumVariable(
|
|
'python_package',
|
|
"""If you plan to work in Python, then you need the ``full`` Cantera Python
|
|
package. If, on the other hand, you will only use Cantera from some
|
|
other language (for example, MATLAB or Fortran 90/95) and only need Python
|
|
to process CTI files, then you only need a ``minimal`` subset of the
|
|
package and Cython and NumPy are not necessary. The ``none`` option
|
|
doesn't install any components of the Python interface. The default
|
|
behavior is to build the full Python module for whichever version of
|
|
Python is running SCons if the required prerequisites (NumPy and
|
|
Cython) are installed. Note: ``y`` is a synonym for ``full`` and ``n``
|
|
is a synonym for ``none``.""",
|
|
'default', ('full', 'minimal', 'none', 'n', 'y', 'default')),
|
|
PathVariable(
|
|
'python_cmd',
|
|
"""Cantera needs to know where to find the Python interpreter. If
|
|
PYTHON_CMD is not set, then the configuration process will use the
|
|
same Python interpreter being used by SCons.""",
|
|
sys.executable, PathVariable.PathAccept),
|
|
PathVariable(
|
|
'python_prefix',
|
|
"""Use this option if you want to install the Cantera Python package to
|
|
an alternate location. On Unix-like systems, the default is the same
|
|
as the 'prefix' option. If the 'python_prefix' option is set to
|
|
the empty string or the 'prefix' option is not set, then the package
|
|
will be installed to the system default 'site-packages' directory.
|
|
To install to the current user's 'site-packages' directory, use
|
|
'python_prefix=USER'.""",
|
|
defaults.python_prefix, PathVariable.PathAccept),
|
|
EnumVariable(
|
|
'matlab_toolbox',
|
|
"""This variable controls whether the MATLAB toolbox will be built. If
|
|
set to 'y', you will also need to set the value of the 'matlab_path'
|
|
variable. If set to 'default', the MATLAB toolbox will be built if
|
|
'matlab_path' is set.""",
|
|
'default', ('y', 'n', 'default')),
|
|
PathVariable(
|
|
'matlab_path',
|
|
"""Path to the MATLAB install directory. This should be the directory
|
|
containing the 'extern', 'bin', etc. subdirectories. Typical values
|
|
are: "C:/Program Files/MATLAB/R2011a" on Windows,
|
|
"/Applications/MATLAB_R2011a.app" on OS X, or
|
|
"/opt/MATLAB/R2011a" on Linux.""",
|
|
'', PathVariable.PathAccept),
|
|
EnumVariable(
|
|
'f90_interface',
|
|
"""This variable controls whether the Fortran 90/95 interface will be
|
|
built. If set to 'default', the builder will look for a compatible
|
|
Fortran compiler in the 'PATH' environment variable, and compile
|
|
the Fortran 90 interface if one is found.""",
|
|
'default', ('y', 'n', 'default')),
|
|
PathVariable(
|
|
'FORTRAN',
|
|
"""The Fortran (90) compiler. If unspecified, the builder will look for
|
|
a compatible compiler (pgfortran, gfortran, ifort, g95) in the 'PATH' environment
|
|
variable. Used only for compiling the Fortran 90 interface.""",
|
|
'', PathVariable.PathAccept),
|
|
('FORTRANFLAGS',
|
|
'Compilation options for the Fortran (90) compiler.',
|
|
'-O3'),
|
|
BoolVariable(
|
|
'coverage',
|
|
"""Enable collection of code coverage information with gcov.
|
|
Available only when compiling with gcc.""",
|
|
False),
|
|
BoolVariable(
|
|
'doxygen_docs',
|
|
"""Build HTML documentation for the C++ interface using Doxygen.""",
|
|
False),
|
|
BoolVariable(
|
|
'sphinx_docs',
|
|
"""Build HTML documentation for Cantera using Sphinx.""",
|
|
False),
|
|
PathVariable(
|
|
'sphinx_cmd',
|
|
"""Command to use for building the Sphinx documentation.""",
|
|
'sphinx-build', PathVariable.PathAccept),
|
|
(
|
|
"sphinx_options",
|
|
"""Options passed to the 'sphinx_cmd' command line. Separate multiple
|
|
options with spaces, for example, "-W --keep-going".""",
|
|
defaults.sphinx_options,
|
|
),
|
|
EnumVariable(
|
|
'system_eigen',
|
|
"""Select whether to use Eigen from a system installation ('y'), from a
|
|
Git submodule ('n'), or to decide automatically ('default'). If Eigen
|
|
is not installed directly into a system include directory, for example, it is
|
|
installed in '/opt/include/eigen3/Eigen', then you will need to add
|
|
'/opt/include/eigen3' to 'extra_inc_dirs'.
|
|
""",
|
|
'default', ('default', 'y', 'n')),
|
|
EnumVariable(
|
|
'system_fmt',
|
|
"""Select whether to use the fmt library from a system installation
|
|
('y'), from a Git submodule ('n'), or to decide automatically
|
|
('default'). If you do not want to use the Git submodule and fmt
|
|
is not installed directly into system include and library
|
|
directories, then you will need to add those directories to
|
|
'extra_inc_dirs' and 'extra_lib_dirs'. This installation of fmt
|
|
must include the shared version of the library, for example,
|
|
'libfmt.so'.""",
|
|
'default', ('default', 'y', 'n')),
|
|
EnumVariable(
|
|
'system_yamlcpp',
|
|
"""Select whether to use the yaml-cpp library from a system installation
|
|
('y'), from a Git submodule ('n'), or to decide automatically
|
|
('default'). If yaml-cpp is not installed directly into system
|
|
include and library directories, then you will need to add those
|
|
directories to 'extra_inc_dirs' and 'extra_lib_dirs'.""",
|
|
'default', ('default', 'y', 'n')),
|
|
EnumVariable(
|
|
'system_sundials',
|
|
"""Select whether to use SUNDIALS from a system installation ('y'), from
|
|
a Git submodule ('n'), or to decide automatically ('default').
|
|
Specifying 'sundials_include' or 'sundials_libdir' changes the
|
|
default to 'y'.""",
|
|
'default', ('default', 'y', 'n')),
|
|
PathVariable(
|
|
'sundials_include',
|
|
"""The directory where the SUNDIALS header files are installed. This
|
|
should be the directory that contains the "cvodes", "nvector", etc.
|
|
subdirectories. Not needed if the headers are installed in a
|
|
standard location, for example, '/usr/include'.""",
|
|
'', PathVariable.PathAccept),
|
|
PathVariable(
|
|
'sundials_libdir',
|
|
"""The directory where the SUNDIALS static libraries are installed.
|
|
Not needed if the libraries are installed in a standard location,
|
|
for example, '/usr/lib'.""",
|
|
'', PathVariable.PathAccept),
|
|
(
|
|
'blas_lapack_libs',
|
|
"""Cantera can use BLAS and LAPACK libraries available on your system if
|
|
you have optimized versions available (for example, Intel MKL). Otherwise,
|
|
Cantera will use Eigen for linear algebra support. To use BLAS
|
|
and LAPACK, set 'blas_lapack_libs' to the the list of libraries
|
|
that should be passed to the linker, separated by commas, for example,
|
|
"lapack,blas" or "lapack,f77blas,cblas,atlas". Eigen is required
|
|
whether or not BLAS/LAPACK are used.""",
|
|
''),
|
|
PathVariable(
|
|
'blas_lapack_dir',
|
|
"""Directory containing the libraries specified by 'blas_lapack_libs'. Not
|
|
needed if the libraries are installed in a standard location, for example,
|
|
``/usr/lib``.""",
|
|
'', PathVariable.PathAccept),
|
|
EnumVariable(
|
|
'lapack_names',
|
|
"""Set depending on whether the procedure names in the specified
|
|
libraries are lowercase or uppercase. If you don't know, run 'nm' on
|
|
the library file (for example, 'nm libblas.a').""",
|
|
'lower', ('lower','upper')),
|
|
BoolVariable(
|
|
'lapack_ftn_trailing_underscore',
|
|
"""Controls whether the LAPACK functions have a trailing underscore
|
|
in the Fortran libraries.""",
|
|
True),
|
|
BoolVariable(
|
|
'lapack_ftn_string_len_at_end',
|
|
"""Controls whether the LAPACK functions have the string length
|
|
argument at the end of the argument list ('yes') or after
|
|
each argument ('no') in the Fortran libraries.""",
|
|
True),
|
|
EnumVariable(
|
|
'googletest',
|
|
"""Select whether to use gtest/gmock from system
|
|
installation ('system'), from a Git submodule ('submodule'), to decide
|
|
automatically ('default') or don't look for gtest/gmock ('none')
|
|
and don't run tests that depend on gtest/gmock.""",
|
|
'default', ('default', 'system', 'submodule', 'none')),
|
|
(
|
|
'env_vars',
|
|
"""Environment variables to propagate through to SCons. Either the
|
|
string "all" or a comma separated list of variable names, for example,
|
|
'LD_LIBRARY_PATH,HOME'.""",
|
|
defaults.env_vars),
|
|
BoolVariable(
|
|
'use_pch',
|
|
"""Use a precompiled-header to speed up compilation""",
|
|
defaults.buildPch),
|
|
(
|
|
'cxx_flags',
|
|
"""Compiler flags passed to the C++ compiler only. Separate multiple
|
|
options with spaces, for example, "cxx_flags='-g -Wextra -O3 --std=c++11'"
|
|
""",
|
|
defaults.cxxFlags),
|
|
(
|
|
'cc_flags',
|
|
"""Compiler flags passed to both the C and C++ compilers, regardless of optimization level.""",
|
|
defaults.ccFlags),
|
|
(
|
|
'thread_flags',
|
|
"""Compiler and linker flags for POSIX multithreading support.""",
|
|
defaults.threadFlags),
|
|
BoolVariable(
|
|
'optimize',
|
|
"""Enable extra compiler optimizations specified by the
|
|
'optimize_flags' variable, instead of the flags specified by the
|
|
'no_optimize_flags' variable.""",
|
|
True),
|
|
(
|
|
'optimize_flags',
|
|
"""Additional compiler flags passed to the C/C++ compiler when 'optimize=yes'.""",
|
|
defaults.optimizeCcFlags),
|
|
(
|
|
'no_optimize_flags',
|
|
"""Additional compiler flags passed to the C/C++ compiler when 'optimize=no'.""",
|
|
defaults.noOptimizeCcFlags),
|
|
BoolVariable(
|
|
'debug',
|
|
"""Enable compiler debugging symbols.""",
|
|
True),
|
|
(
|
|
'debug_flags',
|
|
"""Additional compiler flags passed to the C/C++ compiler when 'debug=yes'.""",
|
|
defaults.debugCcFlags),
|
|
(
|
|
'no_debug_flags',
|
|
"""Additional compiler flags passed to the C/C++ compiler when 'debug=no'.""",
|
|
defaults.noDebugCcFlags),
|
|
(
|
|
'debug_linker_flags',
|
|
"""Additional options passed to the linker when 'debug=yes'.""",
|
|
defaults.debugLinkFlags),
|
|
(
|
|
'no_debug_linker_flags',
|
|
"""Additional options passed to the linker when 'debug=no'.""",
|
|
defaults.noDebugLinkFlags),
|
|
(
|
|
'warning_flags',
|
|
"""Additional compiler flags passed to the C/C++ compiler to enable
|
|
extra warnings. Used only when compiling source code that is part
|
|
of Cantera (for example, excluding code in the 'ext' directory).""",
|
|
defaults.warningFlags),
|
|
(
|
|
'extra_inc_dirs',
|
|
"""Additional directories to search for header files, with multiple
|
|
directories separated by colons (*nix, macOS) or semicolons (Windows)""",
|
|
''),
|
|
(
|
|
'extra_lib_dirs',
|
|
"""Additional directories to search for libraries, with multiple
|
|
directories separated by colons (*nix, macOS) or semicolons (Windows)""",
|
|
''),
|
|
PathVariable(
|
|
'boost_inc_dir',
|
|
"""Location of the Boost header files. Not needed if the headers are
|
|
installed in a standard location, for example, '/usr/include'.""",
|
|
defaults.boostIncDir, PathVariable.PathAccept),
|
|
PathVariable(
|
|
'stage_dir',
|
|
"""Directory relative to the Cantera source directory to be
|
|
used as a staging area for building for example, a Debian
|
|
package. If specified, 'scons install' will install files
|
|
to 'stage_dir/prefix/...'.""",
|
|
'',
|
|
PathVariable.PathAccept),
|
|
BoolVariable(
|
|
'VERBOSE',
|
|
"""Create verbose output about what SCons is doing.""",
|
|
False),
|
|
(
|
|
'gtest_flags',
|
|
"""Additional options passed to each GTest test suite, for example,
|
|
'--gtest_filter=*pattern*'. Separate multiple options with spaces.""",
|
|
''),
|
|
BoolVariable(
|
|
'renamed_shared_libraries',
|
|
"""If this option is turned on, the shared libraries that are created
|
|
will be renamed to have a '_shared' extension added to their base name.
|
|
If not, the base names will be the same as the static libraries.
|
|
In some cases this simplifies subsequent linking environments with
|
|
static libraries and avoids a bug with using valgrind with
|
|
the '-static' linking flag.""",
|
|
True),
|
|
BoolVariable(
|
|
'versioned_shared_library',
|
|
"""If enabled, create a versioned shared library, with symlinks to the
|
|
more generic library name, for example, 'libcantera_shared.so.2.5.0' as the
|
|
actual library and 'libcantera_shared.so' and 'libcantera_shared.so.2'
|
|
as symlinks.
|
|
""",
|
|
defaults.versionedSharedLibrary),
|
|
BoolVariable(
|
|
'use_rpath_linkage',
|
|
"""If enabled, link to all shared libraries using 'rpath', i.e., a fixed
|
|
run-time search path for dynamic library loading.""",
|
|
True),
|
|
EnumVariable(
|
|
'layout',
|
|
"""The layout of the directory structure. 'standard' installs files to
|
|
several subdirectories under 'prefix', for example, 'prefix/bin',
|
|
'prefix/include/cantera', 'prefix/lib' etc. This layout is best used in
|
|
conjunction with "prefix'='/usr/local'". 'compact' puts all installed
|
|
files in the subdirectory defined by 'prefix'. This layout is best
|
|
with a prefix like '/opt/cantera'. 'debian' installs to the stage
|
|
directory in a layout used for generating Debian packages.""",
|
|
defaults.fsLayout, ('standard','compact','debian')),
|
|
BoolVariable(
|
|
"fast_fail_tests",
|
|
"""If enabled, tests will exit at the first failure.""",
|
|
False,
|
|
)
|
|
]
|
|
|
|
opts.AddVariables(*config_options)
|
|
opts.Update(env)
|
|
opts.Save('cantera.conf', env)
|
|
|
|
# Expand ~/ and environment variables used in cantera.conf (variables used on
|
|
# the command line will be expanded by the shell)
|
|
for option in opts.keys():
|
|
original = env[option]
|
|
if isinstance(original, str):
|
|
modified = os.path.expandvars(os.path.expanduser(env[option]))
|
|
if original != modified:
|
|
print('INFO: Expanding {!r} to {!r}'.format(original, modified))
|
|
env[option] = modified
|
|
|
|
if 'help' in COMMAND_LINE_TARGETS:
|
|
### Print help about configuration options and exit.
|
|
print("""
|
|
**************************************************
|
|
* Configuration options for building Cantera *
|
|
**************************************************
|
|
|
|
The following options can be passed to SCons to customize the Cantera
|
|
build process. They should be given in the form:
|
|
|
|
scons build option1=value1 option2=value2
|
|
|
|
Variables set in this way will be stored in the 'cantera.conf' file and reused
|
|
automatically on subsequent invocations of scons. Alternatively, the
|
|
configuration options can be entered directly into 'cantera.conf' before
|
|
running 'scons build'. The format of this file is:
|
|
|
|
option1 = 'value1'
|
|
option2 = 'value2'
|
|
|
|
**************************************************
|
|
""")
|
|
|
|
for opt in opts.options:
|
|
print('\n'.join(formatOption(env, opt)))
|
|
sys.exit(0)
|
|
|
|
if 'doxygen' in COMMAND_LINE_TARGETS:
|
|
env['doxygen_docs'] = True
|
|
if 'sphinx' in COMMAND_LINE_TARGETS:
|
|
env['sphinx_docs'] = True
|
|
|
|
valid_arguments = (set(opt[0] for opt in windows_compiler_options) |
|
|
set(opt[0] for opt in compiler_options) |
|
|
set(opt[0] for opt in config_options))
|
|
for arg in ARGUMENTS:
|
|
if arg not in valid_arguments:
|
|
print('Encountered unexpected command line argument: %r' % arg)
|
|
sys.exit(1)
|
|
|
|
env["cantera_version"] = "2.6.0a2"
|
|
# For use where pre-release tags are not permitted (MSI, sonames)
|
|
env['cantera_pure_version'] = re.match(r'(\d+\.\d+\.\d+)', env['cantera_version']).group(0)
|
|
env['cantera_short_version'] = re.match(r'(\d+\.\d+)', env['cantera_version']).group(0)
|
|
|
|
try:
|
|
env['git_commit'] = getCommandOutput('git', 'rev-parse', '--short', 'HEAD')
|
|
except Exception:
|
|
env['git_commit'] = 'unknown'
|
|
|
|
# Print values of all build options:
|
|
print("Configuration variables read from 'cantera.conf' and command line:")
|
|
for line in open('cantera.conf'):
|
|
print(' ', line.strip())
|
|
print()
|
|
|
|
# ********************************************
|
|
# *** Configure system-specific properties ***
|
|
# ********************************************
|
|
|
|
# Copy in external environment variables
|
|
if env['env_vars'] == 'all':
|
|
env['ENV'].update(os.environ)
|
|
if 'PYTHONHOME' in env['ENV']:
|
|
del env['ENV']['PYTHONHOME']
|
|
elif env['env_vars']:
|
|
for name in env['env_vars'].split(','):
|
|
if name in os.environ:
|
|
if name == 'PATH':
|
|
env.AppendENVPath('PATH', os.environ['PATH'])
|
|
else:
|
|
env['ENV'][name] = os.environ[name]
|
|
if env['VERBOSE']:
|
|
print('Propagating environment variable {0}={1}'.format(name, env['ENV'][name]))
|
|
elif name not in defaults.env_vars.split(','):
|
|
print('WARNING: failed to propagate environment variable', repr(name))
|
|
print(' Edit cantera.conf or the build command line to fix this.')
|
|
|
|
# @todo: Remove this Warning after Cantera 2.5
|
|
if os.pathsep == ';':
|
|
for dirs in (env['extra_inc_dirs'], env['extra_lib_dirs']):
|
|
if re.search(r':\w:', dirs):
|
|
print('ERROR: Multiple entries in "extra_inc_dirs" and "extra_lib_dirs" '
|
|
'should be separated by semicolons (;) on Windows. Use of OS-specific '
|
|
'path separator introduced in Cantera 2.5.')
|
|
sys.exit(1)
|
|
|
|
env['extra_inc_dirs'] = [d for d in env['extra_inc_dirs'].split(os.pathsep) if d]
|
|
env['extra_lib_dirs'] = [d for d in env['extra_lib_dirs'].split(os.pathsep) if d]
|
|
|
|
env.Append(CPPPATH=env['extra_inc_dirs'],
|
|
LIBPATH=env['extra_lib_dirs'])
|
|
|
|
if env['use_rpath_linkage']:
|
|
env.Append(RPATH=env['extra_lib_dirs'])
|
|
|
|
if env['CC'] == 'cl':
|
|
# embed manifest file
|
|
env['LINKCOM'] = [env['LINKCOM'],
|
|
'if exist ${TARGET}.manifest mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1']
|
|
env['SHLINKCOM'] = [env['SHLINKCOM'],
|
|
'if exist ${TARGET}.manifest mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2']
|
|
env['FORTRAN_LINK'] = 'link'
|
|
else:
|
|
env['FORTRAN_LINK'] = '$FORTRAN'
|
|
|
|
if env['boost_inc_dir']:
|
|
env.Append(CPPPATH=env['boost_inc_dir'])
|
|
|
|
if env['blas_lapack_dir']:
|
|
env.Append(LIBPATH=[env['blas_lapack_dir']])
|
|
if env['use_rpath_linkage']:
|
|
env.Append(RPATH=env['blas_lapack_dir'])
|
|
|
|
if env['system_sundials'] in ('y','default'):
|
|
if env['sundials_include']:
|
|
env.Append(CPPPATH=[env['sundials_include']])
|
|
env['system_sundials'] = 'y'
|
|
if env['sundials_libdir']:
|
|
env.Append(LIBPATH=[env['sundials_libdir']])
|
|
env['system_sundials'] = 'y'
|
|
if env['use_rpath_linkage']:
|
|
env.Append(RPATH=env['sundials_libdir'])
|
|
|
|
# BLAS / LAPACK configuration
|
|
if env['blas_lapack_libs'] != '':
|
|
env['blas_lapack_libs'] = env['blas_lapack_libs'].split(',')
|
|
env['use_lapack'] = True
|
|
elif env['OS'] == 'Darwin':
|
|
env['blas_lapack_libs'] = []
|
|
env['use_lapack'] = True
|
|
env.Append(FRAMEWORKS=['Accelerate'])
|
|
else:
|
|
env['blas_lapack_libs'] = []
|
|
env['use_lapack'] = False
|
|
|
|
# ************************************
|
|
# *** Compiler Configuration Tests ***
|
|
# ************************************
|
|
|
|
def CheckStatement(context, function, includes=""):
|
|
context.Message('Checking for %s... ' % function)
|
|
src = """
|
|
%(include)s
|
|
int main(int argc, char** argv) {
|
|
%(func)s;
|
|
return 0;
|
|
}
|
|
""" % {'func':function, 'include':includes}
|
|
result = context.TryCompile(src, '.cpp')
|
|
context.Result(result)
|
|
return result
|
|
|
|
conf = Configure(env, custom_tests={'CheckStatement': CheckStatement})
|
|
|
|
# Set up compiler options before running configuration tests
|
|
env['CXXFLAGS'] = listify(env['cxx_flags'])
|
|
env['CCFLAGS'] = listify(env['cc_flags']) + listify(env['thread_flags'])
|
|
env['LINKFLAGS'] += listify(env['thread_flags'])
|
|
env['CPPDEFINES'] = {}
|
|
|
|
env['warning_flags'] = listify(env['warning_flags'])
|
|
|
|
if env['optimize']:
|
|
env['CCFLAGS'] += listify(env['optimize_flags'])
|
|
env.Append(CPPDEFINES=['NDEBUG'])
|
|
else:
|
|
env['CCFLAGS'] += listify(env['no_optimize_flags'])
|
|
|
|
if env['debug']:
|
|
env['CCFLAGS'] += listify(env['debug_flags'])
|
|
env['LINKFLAGS'] += listify(env['debug_linker_flags'])
|
|
else:
|
|
env['CCFLAGS'] += listify(env['no_debug_flags'])
|
|
env['LINKFLAGS'] += listify(env['no_debug_linker_flags'])
|
|
|
|
if env['coverage']:
|
|
if 'gcc' in env.subst('$CC') or 'clang' in env.subst('$CC'):
|
|
env.Append(CCFLAGS=['-fprofile-arcs', '-ftest-coverage'])
|
|
env.Append(LINKFLAGS=['-fprofile-arcs', '-ftest-coverage'])
|
|
|
|
else:
|
|
print('Error: coverage testing is only available with GCC.')
|
|
exit(0)
|
|
|
|
if env['toolchain'] == 'mingw':
|
|
env.Append(LINKFLAGS=['-static-libgcc', '-static-libstdc++'])
|
|
|
|
def config_error(message):
|
|
print('ERROR:', message)
|
|
if env['VERBOSE']:
|
|
print('*' * 25, 'Contents of config.log:', '*' * 25)
|
|
print(open('config.log').read())
|
|
print('*' * 28, 'End of config.log', '*' * 28)
|
|
else:
|
|
print("See 'config.log' for details.")
|
|
sys.exit(1)
|
|
|
|
# First, a sanity check:
|
|
if not conf.CheckCXXHeader('cmath', '<>'):
|
|
config_error('The C++ compiler is not correctly configured.')
|
|
|
|
|
|
def get_expression_value(includes, expression, defines=()):
|
|
s = ['#define ' + d for d in defines]
|
|
s.extend('#include ' + i for i in includes)
|
|
s.extend(('#define Q(x) #x',
|
|
'#define QUOTE(x) Q(x)',
|
|
'#include <iostream>',
|
|
'#ifndef SUNDIALS_PACKAGE_VERSION', # name change in Sundials >= 3.0
|
|
'#define SUNDIALS_PACKAGE_VERSION SUNDIALS_VERSION',
|
|
'#endif',
|
|
'int main(int argc, char** argv) {',
|
|
' std::cout << %s << std::endl;' % expression,
|
|
' return 0;',
|
|
'}\n'))
|
|
return '\n'.join(s)
|
|
|
|
# Check for fmt library and checkout submodule if needed
|
|
# Test for 'ostream.h' to ensure that version >= 3.0.0 is available
|
|
if env['system_fmt'] in ('y', 'default'):
|
|
if conf.CheckCXXHeader('fmt/ostream.h', '""'):
|
|
env['system_fmt'] = True
|
|
print("""INFO: Using system installation of fmt library.""")
|
|
|
|
elif env['system_fmt'] == 'y':
|
|
config_error('Expected system installation of fmt library, but it '
|
|
'could not be found.')
|
|
|
|
if env['system_fmt'] in ('n', 'default'):
|
|
env['system_fmt'] = False
|
|
print("""INFO: Using private installation of fmt library.""")
|
|
if not os.path.exists('ext/fmt/include/fmt/ostream.h'):
|
|
if not os.path.exists('.git'):
|
|
config_error('fmt is missing. Install source in ext/fmt.')
|
|
|
|
try:
|
|
code = subprocess.call(['git','submodule','update','--init',
|
|
'--recursive','ext/fmt'])
|
|
except Exception:
|
|
code = -1
|
|
if code:
|
|
config_error('fmt submodule checkout failed.\n'
|
|
'Try manually checking out the submodule with:\n\n'
|
|
' git submodule update --init --recursive ext/fmt\n')
|
|
|
|
fmt_include = '<fmt/format.h>' if env['system_fmt'] else '"../ext/fmt/include/fmt/format.h"'
|
|
fmt_version_source = get_expression_value([fmt_include], 'FMT_VERSION', ['FMT_HEADER_ONLY'])
|
|
retcode, fmt_lib_version = conf.TryRun(fmt_version_source, '.cpp')
|
|
try:
|
|
fmt_lib_version = divmod(float(fmt_lib_version.strip()), 10000)
|
|
(fmt_maj, (fmt_min, fmt_pat)) = fmt_lib_version[0], divmod(fmt_lib_version[1], 100)
|
|
env['FMT_VERSION'] = '{major:.0f}.{minor:.0f}.{patch:.0f}'.format(major=fmt_maj, minor=fmt_min, patch=fmt_pat)
|
|
print('INFO: Found fmt version {}'.format(env['FMT_VERSION']))
|
|
except ValueError:
|
|
env['FMT_VERSION'] = '0.0.0'
|
|
print('INFO: Could not find version of fmt')
|
|
|
|
# Check for yaml-cpp library and checkout submodule if needed
|
|
if env['system_yamlcpp'] in ('y', 'default'):
|
|
if conf.CheckCXXHeader('yaml-cpp/yaml.h', '""'):
|
|
env['system_yamlcpp'] = True
|
|
print("""INFO: Using system installation of yaml-cpp library.""")
|
|
|
|
elif env['system_yamlcpp'] == 'y':
|
|
config_error('Expected system installation of yaml-cpp library, but it '
|
|
'could not be found.')
|
|
|
|
if env['system_yamlcpp'] in ('n', 'default'):
|
|
env['system_yamlcpp'] = False
|
|
print("""INFO: Using private installation of yaml-cpp library.""")
|
|
if not os.path.exists('ext/yaml-cpp/include/yaml-cpp/yaml.h'):
|
|
if not os.path.exists('.git'):
|
|
config_error('yaml-cpp is missing. Install source in ext/yaml-cpp.')
|
|
|
|
try:
|
|
code = subprocess.call(['git', 'submodule', 'update', '--init',
|
|
'--recursive', 'ext/yaml-cpp'])
|
|
except Exception:
|
|
code = -1
|
|
if code:
|
|
config_error('yaml-cpp submodule checkout failed.\n'
|
|
'Try manually checking out the submodule with:\n\n'
|
|
' git submodule update --init --recursive ext/yaml-cpp\n')
|
|
|
|
# Check for googletest and checkout submodule if needed
|
|
if env['googletest'] in ('system', 'default'):
|
|
has_gtest = conf.CheckCXXHeader('gtest/gtest.h', '""')
|
|
has_gmock = conf.CheckCXXHeader('gmock/gmock.h', '""')
|
|
if has_gtest and has_gmock:
|
|
env['googletest'] = 'system'
|
|
print("""INFO: Using system installation of Googletest""")
|
|
elif env['googletest'] == 'system':
|
|
config_error('Expected system installation of Googletest-1.8.0, but it '
|
|
'could not be found.')
|
|
|
|
if env['googletest'] in ('submodule', 'default'):
|
|
env['googletest'] = 'submodule'
|
|
has_gtest = os.path.exists('ext/googletest/googletest/include/gtest/gtest.h')
|
|
has_gmock = os.path.exists('ext/googletest/googlemock/include/gmock/gmock.h')
|
|
if not (has_gtest and has_gmock):
|
|
if not os.path.exists('.git'):
|
|
config_error('Googletest is missing. Install source in ext/googletest.')
|
|
|
|
try:
|
|
code = subprocess.call(['git','submodule','update','--init',
|
|
'--recursive','ext/googletest'])
|
|
except Exception:
|
|
code = -1
|
|
if code:
|
|
config_error('Googletest not found and submodule checkout failed.\n'
|
|
'Try manually checking out the submodule with:\n\n'
|
|
' git submodule update --init --recursive ext/googletest\n')
|
|
print("""INFO: Using Googletest from Git submodule""")
|
|
|
|
if env['googletest'] == 'none':
|
|
print("""INFO: Not using Googletest -- unable to run complete test suite""")
|
|
|
|
# Check for Eigen and checkout submodule if needed
|
|
if env['system_eigen'] in ('y', 'default'):
|
|
if conf.CheckCXXHeader('Eigen/Dense', '<>'):
|
|
env['system_eigen'] = True
|
|
print("""INFO: Using system installation of Eigen.""")
|
|
elif env['system_eigen'] == 'y':
|
|
config_error('Expected system installation of Eigen, but it '
|
|
'could not be found.')
|
|
|
|
if env['system_eigen'] in ('n', 'default'):
|
|
env['system_eigen'] = False
|
|
print("""INFO: Using private installation of Eigen.""")
|
|
if not os.path.exists('ext/eigen/Eigen/Dense'):
|
|
if not os.path.exists('.git'):
|
|
config_error('Eigen is missing. Install Eigen in ext/eigen.')
|
|
|
|
try:
|
|
code = subprocess.call(['git','submodule','update','--init',
|
|
'--recursive','ext/eigen'])
|
|
except Exception:
|
|
code = -1
|
|
if code:
|
|
config_error('Eigen not found and submodule checkout failed.\n'
|
|
'Try manually checking out the submodule with:\n\n'
|
|
' git submodule update --init --recursive ext/eigen\n')
|
|
|
|
eigen_include = '<Eigen/Core>' if env['system_eigen'] else '"../ext/eigen/Eigen/Core"'
|
|
eigen_versions = 'QUOTE(EIGEN_WORLD_VERSION) "." QUOTE(EIGEN_MAJOR_VERSION) "." QUOTE(EIGEN_MINOR_VERSION)'
|
|
eigen_version_source = get_expression_value([eigen_include], eigen_versions)
|
|
retcode, eigen_lib_version = conf.TryRun(eigen_version_source, '.cpp')
|
|
env['EIGEN_LIB_VERSION'] = eigen_lib_version.strip()
|
|
print('INFO: Found Eigen version {}'.format(env['EIGEN_LIB_VERSION']))
|
|
|
|
# Determine which standard library to link to when using Fortran to
|
|
# compile code that links to Cantera
|
|
if conf.CheckDeclaration('__GLIBCXX__', '#include <iostream>', 'C++'):
|
|
env['cxx_stdlib'] = ['stdc++']
|
|
elif conf.CheckDeclaration('_LIBCPP_VERSION', '#include <iostream>', 'C++'):
|
|
env['cxx_stdlib'] = ['c++']
|
|
else:
|
|
env['cxx_stdlib'] = []
|
|
|
|
env['HAS_CLANG'] = conf.CheckDeclaration('__clang__', '', 'C++')
|
|
env['HAS_OPENMP'] = conf.CheckLibWithHeader("omp", "omp.h", language="C++")
|
|
|
|
boost_version_source = get_expression_value(['<boost/version.hpp>'], 'BOOST_LIB_VERSION')
|
|
retcode, boost_lib_version = conf.TryRun(boost_version_source, '.cpp')
|
|
env['BOOST_LIB_VERSION'] = '.'.join(boost_lib_version.strip().split('_'))
|
|
if not env['BOOST_LIB_VERSION']:
|
|
config_error("Boost could not be found. Install Boost headers or set"
|
|
" 'boost_inc_dir' to point to the boost headers.")
|
|
else:
|
|
print('INFO: Found Boost version {0}'.format(env['BOOST_LIB_VERSION']))
|
|
# demangle is availble in Boost 1.55 or newer
|
|
env['has_demangle'] = conf.CheckDeclaration("boost::core::demangle",
|
|
'#include <boost/core/demangle.hpp>', 'C++')
|
|
|
|
import SCons.Conftest, SCons.SConf
|
|
context = SCons.SConf.CheckContext(conf)
|
|
|
|
# Check initially for Sundials<=3.2 and then for Sundials>=4.0
|
|
for cvode_call in ['CVodeCreate(CV_BDF, CV_NEWTON);','CVodeCreate(CV_BDF);']:
|
|
ret = SCons.Conftest.CheckLib(context,
|
|
['sundials_cvodes'],
|
|
header='#include "cvodes/cvodes.h"',
|
|
language='C++',
|
|
call=cvode_call,
|
|
autoadd=False,
|
|
extra_libs=env['blas_lapack_libs'])
|
|
# CheckLib returns False to indicate success
|
|
if not ret:
|
|
if env['system_sundials'] == 'default':
|
|
env['system_sundials'] = 'y'
|
|
break
|
|
|
|
# Execute if the cycle ends without 'break'
|
|
else:
|
|
if env['system_sundials'] == 'default':
|
|
env['system_sundials'] = 'n'
|
|
elif env['system_sundials'] == 'y':
|
|
config_error('Expected system installation of Sundials, but it could '
|
|
'not be found.')
|
|
|
|
# Checkout Sundials submodule if needed
|
|
if (env['system_sundials'] == 'n' and
|
|
not os.path.exists('ext/sundials/include/cvodes/cvodes.h')):
|
|
if not os.path.exists('.git'):
|
|
config_error('Sundials is missing. Install source in ext/sundials.')
|
|
|
|
try:
|
|
code = subprocess.call(['git','submodule','update','--init',
|
|
'--recursive','ext/sundials'])
|
|
except Exception:
|
|
code = -1
|
|
if code:
|
|
config_error('Sundials not found and submodule checkout failed.\n'
|
|
'Try manually checking out the submodule with:\n\n'
|
|
' git submodule update --init --recursive ext/sundials\n')
|
|
|
|
|
|
env['NEED_LIBM'] = not conf.CheckLibWithHeader(None, 'math.h', 'C',
|
|
'double x; log(x);', False)
|
|
env['LIBM'] = ['m'] if env['NEED_LIBM'] else []
|
|
|
|
if env['system_sundials'] == 'y':
|
|
for subdir in ('sundials', 'nvector', 'cvodes', 'ida', 'sunlinsol', 'sunmatrix'):
|
|
removeDirectory('include/cantera/ext/'+subdir)
|
|
|
|
# Determine Sundials version
|
|
sundials_version_source = get_expression_value(['"sundials/sundials_config.h"'],
|
|
'QUOTE(SUNDIALS_PACKAGE_VERSION)')
|
|
retcode, sundials_version = conf.TryRun(sundials_version_source, '.cpp')
|
|
if retcode == 0:
|
|
config_error("Failed to determine Sundials version.")
|
|
sundials_version = sundials_version.strip(' "\n')
|
|
|
|
# Ignore the minor version, e.g. 2.4.x -> 2.4
|
|
env['sundials_version'] = '.'.join(sundials_version.split('.')[:2])
|
|
sundials_ver = parse_version(env['sundials_version'])
|
|
if sundials_ver < parse_version('2.4') or sundials_ver >= parse_version('6.0'):
|
|
print("""ERROR: Sundials version %r is not supported.""" % env['sundials_version'])
|
|
sys.exit(1)
|
|
elif sundials_ver > parse_version('5.7'):
|
|
print("WARNING: Sundials version %r has not been tested." % env['sundials_version'])
|
|
|
|
print("""INFO: Using system installation of Sundials version %s.""" % sundials_version)
|
|
|
|
# Determine whether or not Sundials was built with BLAS/LAPACK
|
|
if sundials_ver < parse_version('2.6'):
|
|
# In Sundials 2.4 / 2.5, SUNDIALS_BLAS_LAPACK is either 0 or 1
|
|
sundials_blas_lapack = get_expression_value(['"sundials/sundials_config.h"'],
|
|
'SUNDIALS_BLAS_LAPACK')
|
|
retcode, has_sundials_lapack = conf.TryRun(sundials_blas_lapack, '.cpp')
|
|
if retcode == 0:
|
|
config_error("Failed to determine Sundials BLAS/LAPACK.")
|
|
env['has_sundials_lapack'] = int(has_sundials_lapack.strip())
|
|
elif sundials_ver < parse_version('5.5'):
|
|
# In Sundials 2.6-5.5, SUNDIALS_BLAS_LAPACK is either defined or undefined
|
|
env['has_sundials_lapack'] = conf.CheckDeclaration('SUNDIALS_BLAS_LAPACK',
|
|
'#include "sundials/sundials_config.h"', 'C++')
|
|
else:
|
|
# In Sundials 5.5 and higher, two defines are included specific to the
|
|
# SUNLINSOL packages indicating whether SUNDIALS has been built with LAPACK
|
|
lapackband = conf.CheckDeclaration(
|
|
"SUNDIALS_SUNLINSOL_LAPACKBAND",
|
|
'#include "sundials/sundials_config.h"',
|
|
"C++",
|
|
)
|
|
lapackdense = conf.CheckDeclaration(
|
|
"SUNDIALS_SUNLINSOL_LAPACKDENSE",
|
|
'#include "sundials/sundials_config.h"',
|
|
"C++",
|
|
)
|
|
env["has_sundials_lapack"] = lapackband and lapackdense
|
|
|
|
# In the case where a user is trying to link Cantera to an external BLAS/LAPACK
|
|
# library, but Sundials was configured without this support, print a Warning.
|
|
if not env['has_sundials_lapack'] and env['use_lapack']:
|
|
print('WARNING: External BLAS/LAPACK has been specified for Cantera '
|
|
'but Sundials was built without this support.')
|
|
else: # env['system_sundials'] == 'n'
|
|
print("""INFO: Using private installation of Sundials version 5.3.""")
|
|
env['sundials_version'] = '5.3'
|
|
env['has_sundials_lapack'] = int(env['use_lapack'])
|
|
|
|
|
|
# Try to find a working Fortran compiler:
|
|
def check_fortran(compiler, expected=False):
|
|
hello_world = '''
|
|
program main
|
|
write(*,'(a)') 'Hello, world!'
|
|
end program main
|
|
'''
|
|
if which(compiler) is not None:
|
|
env['F77'] = env['F90'] = env['F95'] = env['F03'] = env['FORTRAN'] = compiler
|
|
success, output = conf.TryRun(hello_world, '.f90')
|
|
if success and 'Hello, world!' in output:
|
|
return True
|
|
else:
|
|
print("WARNING: Unable to use '%s' to compile the Fortran "
|
|
"interface. See config.log for details." % compiler)
|
|
return False
|
|
elif expected:
|
|
print("ERROR: Couldn't find specified Fortran compiler: '%s'" % compiler)
|
|
sys.exit(1)
|
|
|
|
return False
|
|
|
|
env['F77FLAGS'] = env['F90FLAGS'] = env['F95FLAGS'] = env['F03FLAGS'] = env['FORTRANFLAGS']
|
|
|
|
if env['f90_interface'] in ('y','default'):
|
|
foundF90 = False
|
|
if env['FORTRAN']:
|
|
foundF90 = check_fortran(env['FORTRAN'], True)
|
|
|
|
for compiler in ('pgfortran', 'gfortran', 'ifort', 'g95'):
|
|
if foundF90:
|
|
break
|
|
foundF90 = check_fortran(compiler)
|
|
|
|
if foundF90:
|
|
print("INFO: Using '%s' to build the Fortran 90 interface" % env['FORTRAN'])
|
|
env['f90_interface'] = 'y'
|
|
else:
|
|
if env['f90_interface'] == 'y':
|
|
print("ERROR: Couldn't find a suitable Fortran compiler to build the Fortran 90 interface.")
|
|
sys.exit(1)
|
|
else:
|
|
env['f90_interface'] = 'n'
|
|
env['FORTRAN'] = ''
|
|
print("INFO: Skipping compilation of the Fortran 90 interface.")
|
|
|
|
if 'pgfortran' in env['FORTRAN']:
|
|
env['FORTRANMODDIRPREFIX'] = '-module '
|
|
elif 'gfortran' in env['FORTRAN']:
|
|
env['FORTRANMODDIRPREFIX'] = '-J'
|
|
elif 'g95' in env['FORTRAN']:
|
|
env['FORTRANMODDIRPREFIX'] = '-fmod='
|
|
elif 'ifort' in env['FORTRAN']:
|
|
env['FORTRANMODDIRPREFIX'] = '-module '
|
|
|
|
env['F77'] = env['F90'] = env['F95'] = env['F03'] = env['FORTRAN']
|
|
|
|
env['FORTRANMODDIR'] = '${TARGET.dir}'
|
|
|
|
env = conf.Finish()
|
|
|
|
if env['VERBOSE']:
|
|
print('-------------------- begin config.log --------------------')
|
|
print(open('config.log').read())
|
|
print('--------------------- end config.log ---------------------')
|
|
|
|
env['python_cmd_esc'] = quoted(env['python_cmd'])
|
|
|
|
# Python Package Settings
|
|
python_min_version = parse_version('3.5')
|
|
# The string is used to set python_requires in setup.py.in
|
|
env['py_min_ver_str'] = str(python_min_version)
|
|
# Note: cython_min_version is redefined below if the Python version is 3.8 or higher
|
|
cython_min_version = parse_version('0.23')
|
|
numpy_min_version = parse_version('1.12.0')
|
|
|
|
# We choose ruamel.yaml 0.15.34 as the minimum version
|
|
# since it is the highest version available in the Ubuntu
|
|
# 18.04 repositories and seems to work. Older versions such as
|
|
# 0.13.14 on CentOS7 and 0.10.23 on Ubuntu 16.04 raise an exception
|
|
# that they are missing the RoundTripRepresenter
|
|
ruamel_min_version = parse_version('0.15.34')
|
|
|
|
# Check for the minimum ruamel.yaml version, 0.15.34, at install and test
|
|
# time. The check happens at install and test time because ruamel.yaml is
|
|
# only required to run the Python interface, not to build it.
|
|
check_for_ruamel_yaml = any(
|
|
target in COMMAND_LINE_TARGETS
|
|
for target in ["install", "test", "test-python-convert"]
|
|
)
|
|
|
|
if env['python_package'] == 'y':
|
|
env['python_package'] = 'full' # Allow 'y' as a synonym for 'full'
|
|
elif env['python_package'] == 'n':
|
|
env['python_package'] = 'none' # Allow 'n' as a synonym for 'none'
|
|
|
|
env['install_python_action'] = ''
|
|
env['python_module_loc'] = ''
|
|
|
|
if env['python_package'] != 'none':
|
|
# Test to see if we can import numpy and Cython
|
|
script = textwrap.dedent("""\
|
|
import sys
|
|
print('{v.major}.{v.minor}'.format(v=sys.version_info))
|
|
err = ''
|
|
try:
|
|
import numpy
|
|
print(numpy.__version__)
|
|
except ImportError as np_err:
|
|
print('0.0.0')
|
|
err += str(np_err) + '\\n'
|
|
try:
|
|
import Cython
|
|
print(Cython.__version__)
|
|
except ImportError as cython_err:
|
|
print('0.0.0')
|
|
err += str(cython_err) + '\\n'
|
|
if err:
|
|
print(err)
|
|
""")
|
|
expected_output_lines = 3
|
|
if check_for_ruamel_yaml:
|
|
ru_script = textwrap.dedent("""\
|
|
try:
|
|
import ruamel_yaml as yaml
|
|
print(yaml.__version__)
|
|
except ImportError as ru_err:
|
|
try:
|
|
from ruamel import yaml
|
|
print(yaml.__version__)
|
|
except ImportError as ru_err_2:
|
|
print('0.0.0')
|
|
err += str(ru_err) + '\\n'
|
|
err += str(ru_err_2) + '\\n'
|
|
""").splitlines()
|
|
s = script.splitlines()
|
|
s[-2:-2] = ru_script
|
|
script = "\n".join(s)
|
|
expected_output_lines = 4
|
|
|
|
try:
|
|
info = getCommandOutput(env['python_cmd'], '-c', script).splitlines()
|
|
except OSError as err:
|
|
if env['VERBOSE']:
|
|
print('Error checking for Python:')
|
|
print(err)
|
|
warn_no_python = True
|
|
except subprocess.CalledProcessError as err:
|
|
if env['VERBOSE']:
|
|
print('Error checking for Python:')
|
|
print(err, err.output)
|
|
warn_no_python = True
|
|
else:
|
|
warn_no_python = False
|
|
python_version = parse_version(info[0])
|
|
numpy_version = parse_version(info[1])
|
|
cython_version = parse_version(info[2])
|
|
if check_for_ruamel_yaml:
|
|
ruamel_yaml_version = parse_version(info[3])
|
|
if ruamel_yaml_version == parse_version("0.0.0"):
|
|
print("ERROR: ruamel.yaml was not found. {} or newer is "
|
|
"required".format(ruamel_min_version))
|
|
sys.exit(1)
|
|
elif ruamel_yaml_version < ruamel_min_version:
|
|
print("ERROR: ruamel.yaml is an incompatible version: Found "
|
|
"{}, but {} or newer is required.".format(
|
|
ruamel_yaml_version, ruamel_min_version))
|
|
sys.exit(1)
|
|
|
|
if warn_no_python:
|
|
if env['python_package'] == 'default':
|
|
print('WARNING: Not building the Python package because the Python '
|
|
'interpreter {!r} could not be found'.format(env['python_cmd']))
|
|
env['python_package'] = 'none'
|
|
else:
|
|
print('ERROR: Could not execute the Python interpreter {!r}'.format(
|
|
env['python_cmd']))
|
|
sys.exit(1)
|
|
elif python_version < python_min_version:
|
|
msg = ("{}: Python version is incompatible. Found {} but {} "
|
|
"or newer is required")
|
|
if env["python_package"] in ("minimal", "full"):
|
|
print(msg.format("ERROR", python_version, python_min_version))
|
|
sys.exit(1)
|
|
elif env["python_package"] == "default":
|
|
print(msg.format("WARNING", python_version, python_min_version))
|
|
env["python_package"] = "none"
|
|
elif env['python_package'] == 'minimal':
|
|
# If the minimal package was specified, no further checking
|
|
# needs to be done
|
|
print('INFO: Building the minimal Python package for Python {}'.format(python_version))
|
|
else:
|
|
|
|
if len(info) > expected_output_lines:
|
|
print("WARNING: Unexpected output while checking Python "
|
|
"dependency versions:")
|
|
print('| ' + '\n| '.join(info[expected_output_lines:]))
|
|
|
|
warn_no_full_package = False
|
|
if python_version >= parse_version("3.8"):
|
|
# Reset the minimum Cython version if the Python version is 3.8 or higher
|
|
# Due to internal changes in the CPython API, more recent versions of
|
|
# Cython are necessary to build for Python 3.8. There is nothing Cantera
|
|
# can do about this, the changes in CPython are handled by Cython. This
|
|
# version bump is used to produce a more useful/actionable error message
|
|
# for users than the compilation errors that result from using
|
|
# Cython < 0.29.12.
|
|
cython_min_version = parse_version("0.29.12")
|
|
|
|
if numpy_version == parse_version('0.0.0'):
|
|
print("NumPy not found.")
|
|
warn_no_full_package = True
|
|
elif numpy_version < numpy_min_version:
|
|
print("WARNING: NumPy is an incompatible version: "
|
|
"Found {0} but {1} or newer is required".format(
|
|
numpy_version, numpy_min_version))
|
|
warn_no_full_package = True
|
|
else:
|
|
print('INFO: Using NumPy version {0}.'.format(numpy_version))
|
|
|
|
if cython_version == parse_version('0.0.0'):
|
|
print("Cython not found.")
|
|
warn_no_full_package = True
|
|
elif cython_version < cython_min_version:
|
|
print("WARNING: Cython is an incompatible version: "
|
|
"Found {0} but {1} or newer is required.".format(
|
|
cython_version, cython_min_version))
|
|
warn_no_full_package = True
|
|
else:
|
|
print('INFO: Using Cython version {0}.'.format(cython_version))
|
|
|
|
if warn_no_full_package:
|
|
msg = ('{}: Unable to build the full Python package because compatible '
|
|
'versions of Python, Numpy, and Cython could not be found.')
|
|
if env['python_package'] == 'default':
|
|
print(msg.format("WARNING"))
|
|
print('INFO: Building the minimal Python package for Python {}'.format(python_version))
|
|
env['python_package'] = 'minimal'
|
|
else:
|
|
print(msg.format("ERROR"))
|
|
sys.exit(1)
|
|
else:
|
|
print('INFO: Building the full Python package for Python {0}'.format(python_version))
|
|
env['python_package'] = 'full'
|
|
|
|
# Matlab Toolbox settings
|
|
if env['matlab_path'] != '' and env['matlab_toolbox'] == 'default':
|
|
env['matlab_toolbox'] = 'y'
|
|
|
|
if env['matlab_toolbox'] == 'y':
|
|
matPath = env['matlab_path']
|
|
if matPath == '':
|
|
print("ERROR: Unable to build the Matlab toolbox because 'matlab_path' has not been set.")
|
|
sys.exit(1)
|
|
|
|
if env['blas_lapack_libs']:
|
|
print('ERROR: The Matlab toolbox is incompatible with external BLAS '
|
|
'and LAPACK libraries. Unset blas_lapack_libs (e.g. "scons '
|
|
'build blas_lapack_libs=") in order to build the Matlab '
|
|
'toolbox, or set matlab_toolbox=n to use the specified BLAS/'
|
|
'LAPACK libraries and skip building the Matlab toolbox.')
|
|
sys.exit(1)
|
|
|
|
if env['system_sundials'] == 'y':
|
|
print('ERROR: The Matlab toolbox is incompatible with external '
|
|
'SUNDIALS libraries. Set system_sundials to no (e.g., "scons build '
|
|
'system_sundials=n") in order to build the Matlab '
|
|
'toolbox, or set matlab_toolbox=n to use the specified '
|
|
'SUNDIALS libraries and skip building the Matlab toolbox.')
|
|
sys.exit(1)
|
|
|
|
if not (os.path.isdir(matPath) and
|
|
os.path.isdir(pjoin(matPath, 'extern'))):
|
|
print("""ERROR: Path set for 'matlab_path' is not correct.""")
|
|
print("""ERROR: Path was: '%s'""" % matPath)
|
|
sys.exit(1)
|
|
|
|
|
|
# **********************************************
|
|
# *** Set additional configuration variables ***
|
|
# **********************************************
|
|
|
|
# On Debian-based systems, need to special-case installation to
|
|
# /usr/local because of dist-packages vs site-packages
|
|
env['debian'] = any(name.endswith('dist-packages') for name in sys.path)
|
|
|
|
# Directories where things will be after actually being installed. These
|
|
# variables are the ones that are used to populate header files, scripts, etc.
|
|
env['prefix'] = os.path.normpath(env['prefix'])
|
|
env['ct_installroot'] = env['prefix']
|
|
env['ct_libdir'] = pjoin(env['prefix'], env['libdirname'])
|
|
env['ct_bindir'] = pjoin(env['prefix'], 'bin')
|
|
env['ct_incdir'] = pjoin(env['prefix'], 'include', 'cantera')
|
|
env['ct_incroot'] = pjoin(env['prefix'], 'include')
|
|
|
|
if env['layout'] == 'compact':
|
|
env['ct_datadir'] = pjoin(env['prefix'], 'data')
|
|
env['ct_sampledir'] = pjoin(env['prefix'], 'samples')
|
|
env['ct_mandir'] = pjoin(env['prefix'], 'man1')
|
|
env['ct_matlab_dir'] = pjoin(env['prefix'], 'matlab', 'toolbox')
|
|
else:
|
|
env['ct_datadir'] = pjoin(env['prefix'], 'share', 'cantera', 'data')
|
|
env['ct_sampledir'] = pjoin(env['prefix'], 'share', 'cantera', 'samples')
|
|
env['ct_mandir'] = pjoin(env['prefix'], 'share', 'man', 'man1')
|
|
env['ct_matlab_dir'] = pjoin(env['prefix'], env['libdirname'],
|
|
'cantera', 'matlab', 'toolbox')
|
|
|
|
# Always set the stage directory before building an MSI installer
|
|
if 'msi' in COMMAND_LINE_TARGETS:
|
|
COMMAND_LINE_TARGETS.append('install')
|
|
env['stage_dir'] = 'stage'
|
|
env['prefix'] = '.'
|
|
env['PYTHON_INSTALLER'] = 'binary'
|
|
elif env['layout'] == 'debian':
|
|
COMMAND_LINE_TARGETS.append('install')
|
|
env['stage_dir'] = 'stage/cantera'
|
|
env['PYTHON_INSTALLER'] = 'debian'
|
|
env['INSTALL_MANPAGES'] = False
|
|
else:
|
|
env['PYTHON_INSTALLER'] = 'direct'
|
|
|
|
|
|
addInstallActions = ('install' in COMMAND_LINE_TARGETS or
|
|
'uninstall' in COMMAND_LINE_TARGETS)
|
|
|
|
# Directories where things will be staged for package creation. These
|
|
# variables should always be used by the Install(...) targets
|
|
if env['stage_dir']:
|
|
instRoot = pjoin(os.getcwd(), env['stage_dir'],
|
|
stripDrive(env['prefix']).strip('/\\'))
|
|
if env['python_prefix']:
|
|
env['python_prefix'] = pjoin(os.getcwd(), env['stage_dir'],
|
|
stripDrive(env['python_prefix']).strip('/\\'))
|
|
else:
|
|
env['python_prefix'] = pjoin(os.getcwd(), env['stage_dir'])
|
|
else:
|
|
instRoot = env['prefix']
|
|
|
|
# Prevent setting Cantera installation path to source directory
|
|
if os.path.abspath(instRoot) == Dir('.').abspath:
|
|
print('ERROR: cannot install Cantera into source directory.')
|
|
exit(1)
|
|
|
|
if env['layout'] == 'debian':
|
|
base = pjoin(os.getcwd(), 'debian')
|
|
|
|
env['inst_libdir'] = pjoin(base, 'cantera-dev', 'usr', env['libdirname'])
|
|
env['inst_incdir'] = pjoin(base, 'cantera-dev', 'usr', 'include', 'cantera')
|
|
env['inst_incroot'] = pjoin(base, 'cantera-dev', 'usr' 'include')
|
|
|
|
env['inst_bindir'] = pjoin(base, 'cantera-common', 'usr', 'bin')
|
|
env['inst_datadir'] = pjoin(base, 'cantera-common', 'usr', 'share', 'cantera', 'data')
|
|
env['inst_docdir'] = pjoin(base, 'cantera-common', 'usr', 'share', 'cantera', 'doc')
|
|
env['inst_sampledir'] = pjoin(base, 'cantera-common', 'usr', 'share', 'cantera', 'samples')
|
|
env['inst_mandir'] = pjoin(base, 'cantera-common', 'usr', 'share', 'man', 'man1')
|
|
|
|
env['inst_matlab_dir'] = pjoin(base, 'cantera-matlab', 'usr',
|
|
env['libdirname'], 'cantera', 'matlab', 'toolbox')
|
|
|
|
env['inst_python_bindir'] = pjoin(base, 'cantera-python', 'usr', 'bin')
|
|
env['python_prefix'] = pjoin(base, 'cantera-python3')
|
|
else:
|
|
env['inst_libdir'] = pjoin(instRoot, env['libdirname'])
|
|
env['inst_bindir'] = pjoin(instRoot, 'bin')
|
|
env['inst_python_bindir'] = pjoin(instRoot, 'bin')
|
|
env['inst_incdir'] = pjoin(instRoot, 'include', 'cantera')
|
|
env['inst_incroot'] = pjoin(instRoot, 'include')
|
|
|
|
if env['layout'] == 'compact':
|
|
env['inst_matlab_dir'] = pjoin(instRoot, 'matlab', 'toolbox')
|
|
env['inst_datadir'] = pjoin(instRoot, 'data')
|
|
env['inst_sampledir'] = pjoin(instRoot, 'samples')
|
|
env['inst_docdir'] = pjoin(instRoot, 'doc')
|
|
env['inst_mandir'] = pjoin(instRoot, 'man1')
|
|
else: # env['layout'] == 'standard'
|
|
env['inst_matlab_dir'] = pjoin(instRoot, env['libdirname'], 'cantera',
|
|
'matlab', 'toolbox')
|
|
env['inst_datadir'] = pjoin(instRoot, 'share', 'cantera', 'data')
|
|
env['inst_sampledir'] = pjoin(instRoot, 'share', 'cantera', 'samples')
|
|
env['inst_docdir'] = pjoin(instRoot, 'share', 'cantera', 'doc')
|
|
env['inst_mandir'] = pjoin(instRoot, 'share', 'man', 'man1')
|
|
|
|
# **************************************
|
|
# *** Set options needed in config.h ***
|
|
# **************************************
|
|
|
|
configh = {}
|
|
|
|
configh['CANTERA_VERSION'] = quoted(env['cantera_version'])
|
|
configh['CANTERA_SHORT_VERSION'] = quoted(env['cantera_short_version'])
|
|
|
|
# Conditional defines
|
|
def cdefine(definevar, configvar, comp=True, value=1):
|
|
if env.get(configvar) == comp:
|
|
configh[definevar] = value
|
|
else:
|
|
configh[definevar] = None
|
|
|
|
# Need to test all of these to see what platform.system() returns
|
|
configh['SOLARIS'] = 1 if env['OS'] == 'Solaris' else None
|
|
configh['DARWIN'] = 1 if env['OS'] == 'Darwin' else None
|
|
|
|
if env['OS'] == 'Solaris' or env['HAS_CLANG']:
|
|
configh['NEEDS_GENERIC_TEMPL_STATIC_DECL'] = 1
|
|
|
|
configh['CT_SUNDIALS_VERSION'] = env['sundials_version'].replace('.','')
|
|
|
|
if env.get('has_sundials_lapack') and env['use_lapack']:
|
|
configh['CT_SUNDIALS_USE_LAPACK'] = 1
|
|
else:
|
|
configh['CT_SUNDIALS_USE_LAPACK'] = 0
|
|
|
|
cdefine('LAPACK_FTN_STRING_LEN_AT_END', 'lapack_ftn_string_len_at_end')
|
|
cdefine('LAPACK_FTN_TRAILING_UNDERSCORE', 'lapack_ftn_trailing_underscore')
|
|
cdefine('FTN_TRAILING_UNDERSCORE', 'lapack_ftn_trailing_underscore')
|
|
cdefine('LAPACK_NAMES_LOWERCASE', 'lapack_names', 'lower')
|
|
cdefine('CT_USE_LAPACK', 'use_lapack')
|
|
cdefine('CT_USE_SYSTEM_EIGEN', 'system_eigen')
|
|
cdefine('CT_USE_SYSTEM_FMT', 'system_fmt')
|
|
cdefine('CT_USE_SYSTEM_YAMLCPP', 'system_yamlcpp')
|
|
cdefine('CT_USE_DEMANGLE', 'has_demangle')
|
|
|
|
config_h_build = env.Command('build/src/config.h.build',
|
|
'include/cantera/base/config.h.in',
|
|
ConfigBuilder(configh))
|
|
# This separate copy operation, which SCons will skip if config.h.build is
|
|
# unmodified, prevents unnecessary rebuilds of the precompiled header
|
|
config_h = env.Command('include/cantera/base/config.h',
|
|
'build/src/config.h.build',
|
|
Copy('$TARGET', '$SOURCE'))
|
|
env.AlwaysBuild(config_h_build)
|
|
env['config_h_target'] = config_h
|
|
|
|
# *********************
|
|
# *** Build Cantera ***
|
|
# *********************
|
|
|
|
# Some options to speed up SCons
|
|
env.SetOption('max_drift', 2)
|
|
env.SetOption('implicit_cache', True)
|
|
|
|
buildTargets = []
|
|
env['build_targets'] = buildTargets
|
|
libraryTargets = [] # objects that go in the Cantera library
|
|
installTargets = []
|
|
sampleTargets = []
|
|
|
|
def build(targets):
|
|
""" Wrapper to add target to list of build targets """
|
|
buildTargets.extend(targets)
|
|
return targets
|
|
|
|
def buildSample(*args, **kwargs):
|
|
""" Wrapper to add target to list of samples """
|
|
targets = args[0](*args[1:], **kwargs)
|
|
sampleTargets.extend(targets)
|
|
return targets
|
|
|
|
def install(*args, **kwargs):
|
|
""" Wrapper to add target to list of install targets """
|
|
if not addInstallActions:
|
|
return
|
|
if len(args) == 2:
|
|
inst = env.Install(*args, **kwargs)
|
|
else:
|
|
inst = args[0](*args[1:], **kwargs)
|
|
|
|
installTargets.extend(inst)
|
|
return inst
|
|
|
|
|
|
env.SConsignFile()
|
|
|
|
env.Prepend(CPPPATH=[],
|
|
LIBPATH=[Dir('build/lib')])
|
|
|
|
# preprocess input files (cti -> xml)
|
|
convertedInputFiles = set()
|
|
for cti in mglob(env, 'data/inputs', 'cti'):
|
|
build(env.Command('build/data/%s' % cti.name, cti.path,
|
|
Copy('$TARGET', '$SOURCE')))
|
|
outName = os.path.splitext(cti.name)[0] + '.xml'
|
|
convertedInputFiles.add(outName)
|
|
build(env.Command('build/data/%s' % outName, cti.path,
|
|
'$python_cmd_esc interfaces/cython/cantera/ctml_writer.py $SOURCE $TARGET'))
|
|
|
|
# Copy XML input files which are not present as cti:
|
|
for xml in mglob(env, 'data/inputs', 'xml'):
|
|
dest = pjoin('build','data',xml.name)
|
|
if xml.name not in convertedInputFiles:
|
|
build(env.Command(dest, xml.path, Copy('$TARGET', '$SOURCE')))
|
|
|
|
for yaml in mglob(env, "data", "yaml"):
|
|
dest = pjoin("build", "data", yaml.name)
|
|
build(env.Command(dest, yaml.path, Copy("$TARGET", "$SOURCE")))
|
|
for subdir in os.listdir('data'):
|
|
if os.path.isdir(pjoin('data', subdir)):
|
|
for yaml in mglob(env, pjoin("data", subdir), "yaml"):
|
|
dest = pjoin("build", "data", subdir, yaml.name)
|
|
if not os.path.exists(pjoin("build", "data", subdir)):
|
|
os.makedirs(pjoin("build", "data", subdir))
|
|
build(env.Command(dest, yaml.path, Copy("$TARGET", "$SOURCE")))
|
|
|
|
|
|
if addInstallActions:
|
|
# Put headers in place
|
|
headerBase = 'include/cantera'
|
|
install(env.RecursiveInstall, '$inst_incdir', 'include/cantera')
|
|
|
|
# Data files
|
|
install(env.RecursiveInstall, '$inst_datadir', 'build/data')
|
|
|
|
|
|
### List of libraries needed to link to Cantera ###
|
|
linkLibs = ['cantera']
|
|
|
|
### List of shared libraries needed to link applications to Cantera
|
|
linkSharedLibs = ['cantera_shared']
|
|
|
|
if env['system_sundials'] == 'y':
|
|
env['sundials_libs'] = ['sundials_cvodes', 'sundials_ida', 'sundials_nvecserial']
|
|
if env['use_lapack'] and sundials_ver >= parse_version('3.0'):
|
|
if env.get('has_sundials_lapack'):
|
|
env['sundials_libs'].extend(('sundials_sunlinsollapackdense',
|
|
'sundials_sunlinsollapackband'))
|
|
else:
|
|
env['sundials_libs'].extend(('sundials_sunlinsoldense',
|
|
'sundials_sunlinsolband'))
|
|
else:
|
|
env['sundials_libs'] = []
|
|
|
|
linkLibs.extend(env['sundials_libs'])
|
|
linkSharedLibs.extend(env['sundials_libs'])
|
|
|
|
if env['system_fmt']:
|
|
linkLibs.append('fmt')
|
|
linkSharedLibs.append('fmt')
|
|
|
|
if env['system_yamlcpp']:
|
|
linkLibs.append('yaml-cpp')
|
|
linkSharedLibs.append('yaml-cpp')
|
|
|
|
# Add LAPACK and BLAS to the link line
|
|
if env['blas_lapack_libs']:
|
|
linkLibs.extend(env['blas_lapack_libs'])
|
|
linkSharedLibs.extend(env['blas_lapack_libs'])
|
|
|
|
# Store the list of needed static link libraries in the environment
|
|
env['cantera_libs'] = linkLibs
|
|
env['cantera_shared_libs'] = linkSharedLibs
|
|
if not env['renamed_shared_libraries']:
|
|
env['cantera_shared_libs'] = linkLibs
|
|
|
|
# Add targets from the SConscript files in the various subdirectories
|
|
Export('env', 'build', 'libraryTargets', 'install', 'buildSample')
|
|
|
|
# ext needs to come before src so that libraryTargets is fully populated
|
|
VariantDir('build/ext', 'ext', duplicate=0)
|
|
SConscript('build/ext/SConscript')
|
|
|
|
# Fortran needs to come before src so that libraryTargets is fully populated
|
|
if env['f90_interface'] == 'y':
|
|
VariantDir('build/src/fortran/', 'src/fortran', duplicate=1)
|
|
SConscript('build/src/fortran/SConscript')
|
|
|
|
VariantDir('build/src', 'src', duplicate=0)
|
|
SConscript('build/src/SConscript')
|
|
|
|
if env['python_package'] == 'full':
|
|
SConscript('interfaces/cython/SConscript')
|
|
elif env['python_package'] == 'minimal':
|
|
SConscript('interfaces/python_minimal/SConscript')
|
|
|
|
if env['CC'] != 'cl':
|
|
VariantDir('build/platform', 'platform/posix', duplicate=0)
|
|
SConscript('build/platform/SConscript')
|
|
|
|
if env['matlab_toolbox'] == 'y':
|
|
SConscript('build/src/matlab/SConscript')
|
|
|
|
if env['doxygen_docs'] or env['sphinx_docs']:
|
|
SConscript('doc/SConscript')
|
|
|
|
# Sample programs (also used from test_problems/SConscript)
|
|
VariantDir('build/samples', 'samples', duplicate=0)
|
|
sampledir_excludes = ['\\.o$', '^~$', '\\.in', 'SConscript']
|
|
SConscript('build/samples/cxx/SConscript')
|
|
|
|
# Install C++ samples
|
|
install(env.RecursiveInstall, '$inst_sampledir/cxx',
|
|
'samples/cxx', exclude=sampledir_excludes)
|
|
|
|
if env['f90_interface'] == 'y':
|
|
SConscript('build/samples/f77/SConscript')
|
|
SConscript('build/samples/f90/SConscript')
|
|
|
|
# install F90 / F77 samples
|
|
install(env.RecursiveInstall, '$inst_sampledir/f77',
|
|
'samples/f77', sampledir_excludes)
|
|
install(env.RecursiveInstall, '$inst_sampledir/f90',
|
|
'samples/f90', sampledir_excludes)
|
|
|
|
### Meta-targets ###
|
|
build_samples = Alias('samples', sampleTargets)
|
|
|
|
def postBuildMessage(target, source, env):
|
|
print("*******************************************************")
|
|
print("Compilation completed successfully.\n")
|
|
print("- To run the test suite, type 'scons test'.")
|
|
print("- To list available tests, type 'scons test-help'.")
|
|
if env['googletest'] == 'none':
|
|
print(" WARNING: You set the 'googletest' to 'none' and all it's tests will be skipped.")
|
|
if os.name == 'nt':
|
|
print("- To install, type 'scons install'.")
|
|
print("- To create a Windows MSI installer, type 'scons msi'.")
|
|
else:
|
|
print("- To install, type 'scons install'.")
|
|
print("*******************************************************")
|
|
|
|
finish_build = env.Command('finish_build', [], postBuildMessage)
|
|
env.Depends(finish_build, buildTargets)
|
|
build_cantera = Alias('build', finish_build)
|
|
|
|
Default('build')
|
|
|
|
def postInstallMessage(target, source, env):
|
|
# Only needed because Python 2 doesn't support textwrap.indent
|
|
def indent(inp_str, indent):
|
|
return '\n'.join([indent + spl for spl in inp_str.splitlines()])
|
|
|
|
env_dict = env.Dictionary()
|
|
install_message = textwrap.dedent("""
|
|
Cantera has been successfully installed.
|
|
|
|
File locations:
|
|
|
|
applications {ct_bindir!s}
|
|
library files {ct_libdir!s}
|
|
C++ headers {ct_incroot!s}
|
|
samples {ct_sampledir!s}
|
|
data files {ct_datadir!s}""".format(**env_dict))
|
|
|
|
if env['sphinx_docs'] or env['doxygen_docs']:
|
|
install_message += indent(textwrap.dedent("""
|
|
HTML documentation {inst_docdir!s}""".format(**env_dict)), ' ')
|
|
|
|
if env['python_package'] == 'full':
|
|
env['python_example_loc'] = pjoin(env['python_module_loc'], 'cantera', 'examples')
|
|
install_message += indent(textwrap.dedent("""
|
|
Python package (cantera) {python_module_loc!s}
|
|
Python samples {python_example_loc!s}""".format(**env_dict)), ' ')
|
|
elif env['python_package'] == 'minimal':
|
|
install_message += indent(textwrap.dedent("""
|
|
minimal Python module {python_module_loc!s}""".format(**env_dict)), ' ')
|
|
|
|
if env['matlab_toolbox'] == 'y':
|
|
env['matlab_sample_loc'] = pjoin(env['ct_sampledir'], 'matlab')
|
|
env['matlab_ctpath_loc'] = pjoin(env['ct_matlab_dir'], 'ctpath.m')
|
|
install_message += textwrap.dedent("""
|
|
Matlab toolbox {ct_matlab_dir!s}
|
|
Matlab samples {matlab_sample_loc!s}
|
|
|
|
An m-file to set the correct matlab path for Cantera is at:
|
|
|
|
{matlab_ctpath_loc!s}
|
|
""".format(**env_dict))
|
|
|
|
if os.name != 'nt':
|
|
env['setup_cantera'] = pjoin(env['ct_bindir'], 'setup_cantera')
|
|
env['setup_cantera_csh'] = pjoin(env['ct_bindir'], 'setup_cantera.csh')
|
|
install_message += textwrap.dedent("""
|
|
|
|
Setup scripts to configure the environment for Cantera are at:
|
|
|
|
setup script (bash) {setup_cantera!s}
|
|
setup script (csh/tcsh) {setup_cantera_csh!s}
|
|
|
|
It is recommended that you run the script for your shell by typing:
|
|
|
|
source {setup_cantera!s}
|
|
|
|
before using Cantera, or else include its contents in your shell login script.
|
|
""".format(**env_dict))
|
|
|
|
print(install_message)
|
|
|
|
finish_install = env.Command('finish_install', [], postInstallMessage)
|
|
env.Depends(finish_install, installTargets)
|
|
install_cantera = Alias('install', finish_install)
|
|
|
|
### Uninstallation
|
|
def getParentDirs(path, top=True):
|
|
head,tail = os.path.split(path)
|
|
if head == os.path.abspath(env['prefix']):
|
|
return [path]
|
|
elif not tail:
|
|
if head.endswith(os.sep):
|
|
return []
|
|
else:
|
|
return [head]
|
|
elif top:
|
|
return getParentDirs(head, False)
|
|
else:
|
|
return getParentDirs(head, False) + [path]
|
|
|
|
# Files installed by SCons
|
|
allfiles = FindInstalledFiles()
|
|
|
|
# Files installed by the Python installer
|
|
if os.path.exists('build/python-installed-files.txt'):
|
|
with open('build/python-installed-files.txt', 'r') as f:
|
|
file_list = f.readlines()
|
|
|
|
install_base = os.path.dirname(file_list[0].strip())
|
|
if os.path.exists(install_base):
|
|
not_listed_files = [s for s in os.listdir(install_base) if not any(s in j for j in file_list)]
|
|
for f in not_listed_files:
|
|
f = pjoin(install_base, f)
|
|
if not os.path.isdir(f) and os.path.exists(f):
|
|
allfiles.append(File(f))
|
|
for f in file_list:
|
|
f = f.strip()
|
|
if not os.path.isdir(f) and os.path.exists(f):
|
|
allfiles.append(File(f))
|
|
|
|
# After removing files (which SCons keeps track of),
|
|
# remove any empty directories (which SCons doesn't track)
|
|
def removeDirectories(target, source, env):
|
|
# Get all directories where files are installed
|
|
alldirs = set()
|
|
for f in allfiles:
|
|
alldirs.update(getParentDirs(f.path))
|
|
if env['layout'] == 'compact':
|
|
alldirs.add(os.path.abspath(env['prefix']))
|
|
# Sort in order of decreasing directory length so that empty subdirectories
|
|
# will be removed before their parents are checked.
|
|
alldirs = sorted(alldirs, key=lambda x: -len(x))
|
|
|
|
# Don't remove directories that probably existed before installation,
|
|
# even if they are empty
|
|
keepDirs = ['local/share', 'local/lib', 'local/include', 'local/bin',
|
|
'man/man1', 'dist-packages', 'site-packages']
|
|
for d in alldirs:
|
|
if any(d.endswith(k) for k in keepDirs):
|
|
continue
|
|
if os.path.isdir(d) and not os.listdir(d):
|
|
os.rmdir(d)
|
|
|
|
uninstall = env.Command("uninstall", None, Delete(allfiles))
|
|
env.AddPostAction(uninstall, Action(removeDirectories))
|
|
|
|
### Windows MSI Installer ###
|
|
if 'msi' in COMMAND_LINE_TARGETS:
|
|
def build_wxs(target, source, env):
|
|
import wxsgen
|
|
wxs = wxsgen.WxsGenerator(env['stage_dir'],
|
|
short_version=env['cantera_short_version'],
|
|
full_version=env['cantera_pure_version'],
|
|
x64=env['TARGET_ARCH']=='amd64',
|
|
includeMatlab=env['matlab_toolbox']=='y')
|
|
wxs.make_wxs(str(target[0]))
|
|
|
|
wxs_target = env.Command('build/wix/cantera.wxs', [], build_wxs)
|
|
env.AlwaysBuild(wxs_target)
|
|
|
|
env.Append(WIXLIGHTFLAGS=['-ext', 'WixUIExtension'])
|
|
msi_target = env.WiX('cantera.msi', ['build/wix/cantera.wxs'])
|
|
env.Depends(wxs_target, installTargets)
|
|
env.Depends(msi_target, wxs_target)
|
|
build_msi = Alias('msi', msi_target)
|
|
|
|
### Tests ###
|
|
if any(target.startswith('test') for target in COMMAND_LINE_TARGETS):
|
|
env['testNames'] = []
|
|
env['test_results'] = env.Command('test_results', [], testResults.printReport)
|
|
|
|
if env['python_package'] == 'none':
|
|
# copy scripts from the full Cython module
|
|
test_py_int = env.Command('#build/python_local/cantera/__init__.py',
|
|
'#interfaces/python_minimal/cantera/__init__.py',
|
|
Copy('$TARGET', '$SOURCE'))
|
|
for script in ['ctml_writer', 'ck2cti', 'ck2yaml', 'ctml2yaml']:
|
|
s = env.Command('#build/python_local/cantera/{}.py'.format(script),
|
|
'#interfaces/cython/cantera/{}.py'.format(script),
|
|
Copy('$TARGET', '$SOURCE'))
|
|
env.Depends(test_py_int, s)
|
|
|
|
env.Depends(env['test_results'], test_py_int)
|
|
|
|
env['python_cmd'] = sys.executable
|
|
env.PrependENVPath('PYTHONPATH', Dir('build/python_local').abspath)
|
|
else:
|
|
env.PrependENVPath('PYTHONPATH', Dir('build/python').abspath)
|
|
|
|
env['ENV']['PYTHON_CMD'] = env.subst('$python_cmd')
|
|
|
|
# Tests written using the gtest framework, the Python unittest module,
|
|
# or the Matlab xunit package.
|
|
VariantDir('build/test', 'test', duplicate=0)
|
|
SConscript('build/test/SConscript')
|
|
|
|
# Regression tests
|
|
SConscript('test_problems/SConscript')
|
|
|
|
if 'test-help' in COMMAND_LINE_TARGETS:
|
|
print('\n*** Available tests ***\n')
|
|
for name in env['testNames']:
|
|
print('test-%s' % name)
|
|
sys.exit(0)
|
|
|
|
Alias('test', env['test_results'])
|
|
|
|
### Dump (debugging SCons)
|
|
if 'dump' in COMMAND_LINE_TARGETS:
|
|
import pprint
|
|
# Typical usage: 'scons build dump'
|
|
print('os.environ:\n', pprint.pprint(dict(os.environ)))
|
|
print('env.Dump():\n', env.Dump())
|
|
sys.exit(0)
|