Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix build issues - macOS rpath, wheel paths, missing pytest #1229

Merged
merged 10 commits into from Mar 30, 2022
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Expand Up @@ -202,7 +202,7 @@ jobs:
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
run: |
python3 -m pip install ruamel.yaml scons numpy cython sphinx\<4.0 \
python3 -m pip install ruamel.yaml scons numpy cython sphinx\<4.0 jinja2\<3.1.0 \
sphinxcontrib-katex sphinxcontrib-matlabdomain sphinxcontrib-doxylink
- name: Build Cantera with documentation
run: python3 `which scons` build -j2 doxygen_docs=y sphinx_docs=y debug=n optimize=n use_pch=n
Expand Down Expand Up @@ -492,7 +492,7 @@ jobs:
architecture: x64
- name: Install Python dependencies
run: |
python -m pip install -U pip 'setuptools>=43.0.0' wheel
python -m pip install -U pip setuptools wheel
python -m pip install scons pypiwin32 numpy ruamel.yaml cython h5py pandas pytest pytest-github-actions-annotate-failures
- name: Restore Boost cache
uses: actions/cache@v2
Expand Down
71 changes: 43 additions & 28 deletions SConstruct
Expand Up @@ -1612,6 +1612,19 @@ if env['python_package'] != 'none':
f"Building the full Python package for Python {python_version}")
env["python_package"] = "full"

if env["python_package"] == "full" and env["OS"] == "Darwin":
# We need to know the macOS deployment target in advance to be able to determine
# the name of the wheel file for the Python module. If this is not specified by the
# MACOSX_DEPLOYMENT_TARGET environment variable, get the value from the Python
# installation and use that.
if not env["ENV"].get("MACOSX_DEPLOYMENT_TARGET", False):
info = get_command_output(
env["python_cmd"],
"-c",
"import sysconfig; print(sysconfig.get_platform())"
)
env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = info.split("-")[1]

# Matlab Toolbox settings
if env["matlab_path"] != "" and env["matlab_toolbox"] == "default":
env["matlab_toolbox"] = "y"
Expand Down Expand Up @@ -1805,9 +1818,14 @@ 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
env["RPATHPREFIX"] = "-Wl,-rpath,"

if env["OS"] == "Darwin" and env["use_rpath_linkage"] and not env.subst("$__RPATH"):
# SCons doesn't want to specify RPATH on macOS, so circumvent that behavior by
# specifying this directly as part of LINKFLAGS
env.Append(LINKFLAGS=[env.subst(f'$RPATHPREFIX{x}$RPATHSUFFIX')
for x in env['RPATH']])

configh['CT_SUNDIALS_VERSION'] = env['sundials_version'].replace('.','')

if env.get('has_sundials_lapack') and env['use_lapack']:
Expand Down Expand Up @@ -1888,13 +1906,16 @@ env.Prepend(CPPPATH=[],

# preprocess input files (cti -> xml)
convertedInputFiles = set()
convertEnv = env.Clone()
convertEnv["ENV"]["CT_NO_XML_WARNINGS"] = "1"
for cti in multi_glob(env, 'data/inputs', 'cti'):
build(env.Command('build/data/%s' % cti.name, cti.path,
Copy('$TARGET', '$SOURCE')))
build(convertEnv.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'))
build(convertEnv.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 multi_glob(env, 'data/inputs', 'xml'):
Expand Down Expand Up @@ -1923,12 +1944,6 @@ if addInstallActions:
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'):
Expand All @@ -1941,27 +1956,27 @@ if env['system_sundials'] == 'y':
else:
env['sundials_libs'] = []

linkLibs.extend(env['sundials_libs'])
linkSharedLibs.extend(env['sundials_libs'])
# External libraries to link to
env["external_libs"] = []
env["external_libs"].extend(env["sundials_libs"])

if env["system_fmt"]:
env["external_libs"].append("fmt")

if env['system_fmt']:
linkLibs.append('fmt')
linkSharedLibs.append('fmt')
if env["system_yamlcpp"]:
env["external_libs"].append("yaml-cpp")

if env['system_yamlcpp']:
linkLibs.append('yaml-cpp')
linkSharedLibs.append('yaml-cpp')
if env["blas_lapack_libs"]:
env["external_libs"].extend(env["blas_lapack_libs"])

# 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'])
# List of static libraries needed to link to Cantera
env["cantera_libs"] = ["cantera"] + env["external_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
# List of shared libraries needed to link to Cantera
if env["renamed_shared_libraries"]:
env["cantera_shared_libs"] = ["cantera_shared"] + env["external_libs"]
else:
env["cantera_shared_libs"] = ["cantera"] + env["external_libs"]

# Add targets from the SConscript files in the various subdirectories
Export('env', 'build', 'libraryTargets', 'install', 'buildSample')
Expand Down
14 changes: 0 additions & 14 deletions include/cantera/base/config.h.in
Expand Up @@ -59,20 +59,6 @@ typedef int ftnlen; // Fortran hidden string length type
// with a native compiler
{SOLARIS!s}

//---------- C++ Compiler Variations ------------------------------

// This define is needed to account for the variability for how
// static variables in templated classes are defined. Right now
// this is only turned on for the SunPro compiler on Solaris.
// in that system , you need to declare the static storage variable.
// with the following line in the include file
//
// template<class M> Cabinet<M>* Cabinet<M>::s_storage;
//
// Note, on other systems that declaration is treated as a definition
// and this leads to multiple defines at link time
{NEEDS_GENERIC_TEMPL_STATIC_DECL!s}

//-------------- Optional Cantera Capabilities ----------------------

// Enable Sundials to use an external BLAS/LAPACK library if it was
Expand Down
4 changes: 2 additions & 2 deletions include/cantera/kinetics/MultiRate.h
Expand Up @@ -33,13 +33,13 @@ class MultiRate final : public MultiRateBase
return m_rxn_rates.at(0).second.type();
}

virtual void add(const size_t rxn_index, ReactionRate& rate) override {
virtual void add(size_t rxn_index, ReactionRate& rate) override {
m_indices[rxn_index] = m_rxn_rates.size();
m_rxn_rates.emplace_back(rxn_index, dynamic_cast<RateType&>(rate));
m_shared.invalidateCache();
}

virtual bool replace(const size_t rxn_index, ReactionRate& rate) override {
virtual bool replace(size_t rxn_index, ReactionRate& rate) override {
if (!m_rxn_rates.size()) {
throw CanteraError("MultiRate::replace",
"Invalid operation: cannot replace rate object "
Expand Down
5 changes: 3 additions & 2 deletions interfaces/cython/SConscript
Expand Up @@ -102,10 +102,11 @@ ext = localenv.LoadableModule(f"cantera/_cantera{module_ext}",
obj, LIBPREFIX="", SHLIBSUFFIX=module_ext,
SHLIBPREFIX="", LIBSUFFIXES=[module_ext])

build_cmd = ("$python_cmd_esc -m pip wheel --no-build-isolation --no-deps "
build_cmd = ("$python_cmd_esc -m pip wheel -v --no-build-isolation --no-deps "
"--wheel-dir=build/python/dist build/python")
plat = info['plat'].replace('-', '_').replace('.', '_')
wheel_name = (f"Cantera-{env['cantera_version']}-cp{py_version_nodot}"
f"-cp{py_version_nodot}-{info['plat'].replace('-', '_')}.whl")
f"-cp{py_version_nodot}-{plat}.whl")
mod = build(localenv.Command(f"#build/python/dist/{wheel_name}", "setup.cfg",
build_cmd))
env['python_module'] = mod
Expand Down
10 changes: 6 additions & 4 deletions interfaces/cython/cantera/ctml_writer.py
Expand Up @@ -30,6 +30,7 @@

import sys
import re
import os

# Python 2/3 compatibility
try:
Expand Down Expand Up @@ -2761,10 +2762,11 @@ def convert(filename=None, outName=None, text=None):
def main():
if len(sys.argv) not in (2,3):
raise ValueError('Incorrect number of command line arguments.')
print("WARNING: The CTI and XML input file formats are deprecated and will be\n"
"removed in Cantera 3.0. Use 'cti2yaml.py' to convert CTI input files to\n"
"the YAML format. See https://cantera.org/tutorials/legacy2yaml.html for\n"
"more information.")
if os.environ.get("CT_NO_XML_WARNINGS") != "1":
print("WARNING: The CTI and XML input file formats are deprecated and will be\n"
"removed in Cantera 3.0. Use 'cti2yaml.py' to convert CTI input files\n"
"to the YAML format. See https://cantera.org/tutorials/legacy2yaml.html\n"
"for more information.")

convert(*sys.argv[1:])

Expand Down
11 changes: 8 additions & 3 deletions interfaces/cython/setup.py
@@ -1,5 +1,10 @@
from setuptools import setup, Extension
from setuptools import setup
from setuptools.dist import Distribution

extension = Extension("cantera._cantera", sources=[])
class BinaryDistribution(Distribution):
def has_ext_modules(self):
return True
Comment on lines +4 to +6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my new favorite Python class ❤️


setup(ext_modules=[extension])
setup(
distclass=BinaryDistribution,
)
11 changes: 2 additions & 9 deletions src/SConscript
Expand Up @@ -78,15 +78,8 @@ localenv.Depends(lib, localenv['config_h_target'])
install('$inst_libdir', lib)
env['cantera_staticlib'] = lib

if (localenv['system_sundials'] == 'y'):
localenv.Append(LIBS=localenv['sundials_libs'],
LIBPATH=localenv['sundials_libdir'])
if localenv['blas_lapack_libs']:
localenv.Append(LIBS=localenv['blas_lapack_libs'],
LIBPATH=localenv['blas_lapack_dir'])

if localenv['system_yamlcpp']:
localenv.Append(LIBS=['yaml-cpp'])
localenv.Append(LIBS=localenv['external_libs'],
LIBPATH=localenv['sundials_libdir'] + localenv['blas_lapack_dir'])

# Build the Cantera shared library
if localenv['layout'] != 'debian':
Expand Down
9 changes: 0 additions & 9 deletions src/clib/Cabinet.h
Expand Up @@ -205,13 +205,4 @@ class Cabinet
std::vector<M*> m_table;
};

//! Declaration stating that the storage for the static member
//! of each instantiated template will exist
/*!
* The actual storage will be allocated in .cpp files
*/
#ifdef NEEDS_GENERIC_TEMPL_STATIC_DECL
template<class M, bool canDelete> Cabinet<M, canDelete>* Cabinet<M, canDelete>::s_storage;
#endif

#endif
1 change: 1 addition & 0 deletions src/clib/ct.cpp
Expand Up @@ -36,6 +36,7 @@ typedef Cabinet<XML_Node, false> XmlCabinet;
template<> ThermoCabinet* ThermoCabinet::s_storage = 0;
template<> KineticsCabinet* KineticsCabinet::s_storage = 0;
template<> TransportCabinet* TransportCabinet::s_storage = 0;
template<> XmlCabinet* XmlCabinet::s_storage; // defined in ctxml.cpp

/**
* Exported functions.
Expand Down
5 changes: 4 additions & 1 deletion src/clib/ctmultiphase.cpp
Expand Up @@ -17,7 +17,10 @@ using namespace std;
using namespace Cantera;

typedef Cabinet<MultiPhase> mixCabinet;
typedef Cabinet<ThermoPhase> ThermoCabinet;

template<> mixCabinet* mixCabinet::s_storage = 0;
template<> ThermoCabinet* ThermoCabinet::s_storage; // defined in ct.cpp

extern "C" {

Expand Down Expand Up @@ -54,7 +57,7 @@ extern "C" {
int mix_addPhase(int i, int j, double moles)
{
try {
mixCabinet::item(i).addPhase(&Cabinet<ThermoPhase>::item(j), moles);
mixCabinet::item(i).addPhase(&ThermoCabinet::item(j), moles);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
Expand Down
3 changes: 3 additions & 0 deletions src/clib/ctonedim.cpp
Expand Up @@ -29,6 +29,9 @@ template<> DomainCabinet* DomainCabinet::s_storage = 0;
typedef Cabinet<ThermoPhase> ThermoCabinet;
typedef Cabinet<Kinetics> KineticsCabinet;
typedef Cabinet<Transport> TransportCabinet;
template<> ThermoCabinet* ThermoCabinet::s_storage; // defined in ct.cpp
template<> KineticsCabinet* KineticsCabinet::s_storage; // defined in ct.cpp
template<> TransportCabinet* TransportCabinet::s_storage; // defined in ct.cpp

extern "C" {

Expand Down
3 changes: 3 additions & 0 deletions src/clib/ctreactor.cpp
Expand Up @@ -32,6 +32,9 @@ template<> NetworkCabinet* NetworkCabinet::s_storage = 0;
template<> FlowDeviceCabinet* FlowDeviceCabinet::s_storage = 0;
template<> WallCabinet* WallCabinet::s_storage = 0;
template<> ReactorSurfaceCabinet* ReactorSurfaceCabinet::s_storage = 0;
template<> FuncCabinet* FuncCabinet::s_storage; // defined in ctfunc.cpp
template<> ThermoCabinet* ThermoCabinet::s_storage; // defined in ct.cpp
template<> KineticsCabinet* KineticsCabinet::s_storage; // defined in ct.cpp

extern "C" {

Expand Down
1 change: 1 addition & 0 deletions src/clib/ctrpath.cpp
Expand Up @@ -23,6 +23,7 @@ template<> DiagramCabinet* DiagramCabinet::s_storage = 0;
template<> BuilderCabinet* BuilderCabinet::s_storage = 0;

typedef Cabinet<Kinetics> KineticsCabinet;
template<> KineticsCabinet* KineticsCabinet::s_storage; // defined in ct.cpp

extern "C" {

Expand Down
2 changes: 2 additions & 0 deletions src/clib/ctsurf.cpp
Expand Up @@ -17,6 +17,8 @@ using namespace std;
using namespace Cantera;

typedef Cabinet<ThermoPhase> ThermoCabinet;
template<> ThermoCabinet* ThermoCabinet::s_storage; // defined in ct.cpp


extern "C" {

Expand Down
6 changes: 6 additions & 0 deletions test/SConscript
Expand Up @@ -157,6 +157,12 @@ def addPythonTest(testname, subdir, script, interpreter,
# Test was successful
with open(target[0].path, 'w') as passed_file:
passed_file.write(time.asctime()+'\n')
elif code == 21: # Special error code from runCythonTests.py for pytest errors
msg = (f"{testname}: Test suite requires the Python package 'pytest',"
" which can be installed using pip or conda"
)
test_results.failed[msg] = 100
return
elif env["fast_fail_tests"]:
sys.exit(1)

Expand Down
13 changes: 5 additions & 8 deletions test/python/runCythonTests.py
Expand Up @@ -28,19 +28,16 @@
try:
import pytest
except ImportError:
pytest = None
import cantera
import cantera.test


if __name__ == "__main__":
if pytest is None:
print("\n* ERROR: The Cantera Python test suite requires "
"the Python package 'pytest'.")
print("* ERROR: Use pip or conda to install 'pytest', "
"which will enable this feature.")
sys.exit(1)
sys.exit(21) # test/SConscript has special handling for this error code

import cantera
import cantera.test

if __name__ == "__main__":
print("\n* INFO: using Cantera module found at this location:")
print(f"* '{cantera.__file__}'")
print(f"* INFO: Cantera version: {cantera.__version__}")
Expand Down