From de9f068795db00f9892f77696398cf3411f560ff Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Tue, 13 Jul 2021 21:55:11 -0400 Subject: [PATCH 01/11] [SCons] Add a target for files copied from ext --- ext/SConscript | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/ext/SConscript b/ext/SConscript index 309f1159dd..f046b6a719 100644 --- a/ext/SConscript +++ b/ext/SConscript @@ -42,6 +42,8 @@ for subdir, extensions, prepFunction in libs: objects = localenv.SharedObject(multi_glob(localenv, subdir, *extensions)) libraryTargets.extend(objects) +ext_copies = [] + if not env['system_fmt']: license_files.append(('fmtlib', 'fmt/LICENSE.rst')) localenv = prep_default(env) @@ -49,9 +51,11 @@ if not env['system_fmt']: libraryTargets.extend( localenv.SharedObject(multi_glob(localenv, 'fmt/src', 'cc'))) for name in ('format.h', 'ostream.h', 'printf.h', 'core.h', 'format-inl.h'): - build(copyenv.Command("#include/cantera/ext/fmt/" + name, - "#ext/fmt/include/fmt/" + name, - Copy('$TARGET', '$SOURCE'))) + ext_copies.extend( + copyenv.Command("#include/cantera/ext/fmt/" + name, + "#ext/fmt/include/fmt/" + name, + Copy("$TARGET", "$SOURCE")) + ) if env['system_sundials'] == 'n': localenv = prep_default(env) @@ -73,9 +77,11 @@ if env['system_sundials'] == 'n': for subdir in ('sundials', 'nvector', 'cvodes', 'ida', 'sunmatrix', 'sunlinsol', 'sunnonlinsol'): for header in multi_glob(env, 'sundials/include/'+subdir, 'h'): - build(copyenv.Command('#include/cantera/ext/%s/%s' % (subdir, header.name), - '#ext/sundials/include/%s/%s' % (subdir, header.name), - Copy('$TARGET', '$SOURCE'))) + ext_copies.extend( + copyenv.Command(f"#include/cantera/ext/{subdir}/{header.name}", + f"#ext/sundials/include/{subdir}/{header.name}", + Copy("$TARGET", "$SOURCE")) + ) # Compile Sundials source files. Skip files related to the Sundials Fortran # interface, which start with 'fsun'. @@ -98,9 +104,13 @@ if not env['system_yamlcpp']: # Copy header files into common include directory for subdir in ('', 'contrib', 'node', 'node/detail'): for header in multi_glob(env, 'yaml-cpp/include/yaml-cpp/'+subdir, 'h'): - h = build(localenv.Command('#include/cantera/ext/yaml-cpp/{}/{}'.format(subdir, header.name), - '#ext/yaml-cpp/include/yaml-cpp/{}/{}'.format(subdir, header.name), - Copy('$TARGET', '$SOURCE'))) + ext_copies.extend( + localenv.Command( + f"#include/cantera/ext/yaml-cpp/{subdir}/{header.name}", + f"#ext/yaml-cpp/include/yaml-cpp/{subdir}/{header.name}", + Copy("$TARGET", "$SOURCE") + ) + ) # Compile yaml-cpp source files for subdir in ('', 'contrib'): @@ -113,6 +123,7 @@ if not env['system_eigen']: h = build(copyenv.Command('#include/cantera/ext/Eigen', '#ext/eigen/Eigen', Copy('$TARGET', '$SOURCE'))) copyenv.Depends(copyenv['config_h_target'], h) + ext_copies.extend(h) # Google Test: Used internally for Cantera unit tests. if env['googletest'] == 'submodule': @@ -123,6 +134,8 @@ if env['googletest'] == 'submodule': gmock = build(localenv.Library('../lib/gmock', source=['googletest/googlemock/src/gmock-all.cc'])) +env["ext_include_copies_target"] = build(ext_copies) + # Create license file containing licenses for Cantera and all included packages def generate_license(target, source, env): stars = '*'*50 + '\n' + '*'*50 + '\n' From 8445c8d997bd054e61254ed7214d1292ee1899d1 Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Fri, 16 Jul 2021 12:35:04 -0400 Subject: [PATCH 02/11] [SCons] Use pathlib to write combined License file Use a dictionary to collect the license file paths and names. This data structure is more natural for this mapping of packages to files. It also simplifies the loop to collect the files. --- ext/SConscript | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/ext/SConscript b/ext/SConscript index f046b6a719..0358cc072e 100644 --- a/ext/SConscript +++ b/ext/SConscript @@ -1,11 +1,12 @@ +from pathlib import Path from buildutils import * Import('env', 'build', 'install', 'libraryTargets') localenv = env.Clone() copyenv = localenv.Clone() # no CPPPATH addition, to avoid circular dependencies -license_files = [('Cantera', '#License.txt'), - ('Libexecstream', 'libexecstream/doc/license.txt')] +license_files = {"Cantera": File("#License.txt"), + "Libexecstream": File("#ext/libexecstream/doc/license.txt")} def prep_default(env): localenv = env.Clone() @@ -45,7 +46,7 @@ for subdir, extensions, prepFunction in libs: ext_copies = [] if not env['system_fmt']: - license_files.append(('fmtlib', 'fmt/LICENSE.rst')) + license_files["fmtlib"] = File("#ext/fmt/LICENSE.rst") localenv = prep_default(env) localenv.Prepend(CPPPATH=Dir('#ext/fmt/include')) libraryTargets.extend( @@ -61,7 +62,7 @@ if env['system_sundials'] == 'n': localenv = prep_default(env) localenv.Prepend(CPPPATH=[Dir('#include/cantera/ext'), Dir('#ext/sundials/src/sundials')]) - license_files.append(('Sundials', 'sundials/LICENSE')) + license_files["Sundials"] = File("#ext/sundials/LICENSE") # Generate sundials_config.h sundials_configh = {} @@ -99,7 +100,7 @@ if env['system_sundials'] == 'n': if not env['system_yamlcpp']: localenv = prep_default(env) localenv.Prepend(CPPPATH=Dir('#include/cantera/ext')) - license_files.append(('YAML-CPP', 'yaml-cpp/LICENSE')) + license_files["YAML-CPP"] = File("#ext/yaml-cpp/LICENSE") # Copy header files into common include directory for subdir in ('', 'contrib', 'node', 'node/detail'): @@ -119,7 +120,7 @@ if not env['system_yamlcpp']: if not env['system_eigen']: - license_files.append(('Eigen', 'eigen/COPYING.MPL2')) + license_files["Eigen"] = File("#ext/eigen/COPYING.MPL2") h = build(copyenv.Command('#include/cantera/ext/Eigen', '#ext/eigen/Eigen', Copy('$TARGET', '$SOURCE'))) copyenv.Depends(copyenv['config_h_target'], h) @@ -136,28 +137,33 @@ if env['googletest'] == 'submodule': env["ext_include_copies_target"] = build(ext_copies) -# Create license file containing licenses for Cantera and all included packages + def generate_license(target, source, env): - stars = '*'*50 + '\n' + '*'*50 + '\n' - tpl = stars + 'The following license applies to {}\n' + stars + '\n{}\n' + target = Path(target[0].abspath) + stars = "*" * 50 + "\n" + "*" * 50 + "\n" + tpl = stars + "The following license applies to {}\n" + stars + "\n{}\n" license = [] - for (package,_),filename in zip(license_files, source): - license.append(tpl.format(package, open(filename.path).read().strip())) + for package, license_file in env["license_files"].items(): + license_file = Path(license_file.abspath) + license.append(tpl.format(package, license_file.read_text().strip())) + license = "\n".join(license) + if target.suffix == ".rtf": + license = license.replace("\\", "\\\\").replace("{", "\\{").replace("}", "\\}") + license = license.replace("\n", " \\par\n") + license = (r"{\rtf1\ansi{\fonttbl\f0\fswiss Arial;}\f0\pard\fs16 " + + license + "}") - license = '\n'.join(license) - if target[0].path.endswith('.rtf'): - license = license.replace('\\', '\\\\').replace('{', '\\{').replace('}', '\\}') - license = license.replace('\n', ' \\par\n') - license = r'{\rtf1\ansi{\fonttbl\f0\fswiss Arial;}\f0\pard\fs16 ' + license + '}' + target.write_text(license) - open(target[0].path, 'w').write(license) -license = build(localenv.Command('LICENSE.txt', [x[1] for x in license_files], +localenv["license_files"] = license_files +license = build(localenv.Command("LICENSE.txt", license_files.values(), generate_license)) +env["license_target"] = license install('$inst_docdir', license) if env['OS'] == 'Windows': # RTF version is required for Windows installer - build(localenv.Command('LICENSE.rtf', [x[1] for x in license_files], + build(localenv.Command("LICENSE.rtf", license_files.values(), generate_license)) From c9745ee25ffbec1579863fa380f11fa199615601 Mon Sep 17 00:00:00 2001 From: "Bryan W. Weber" Date: Wed, 3 Feb 2021 10:15:57 -0500 Subject: [PATCH 03/11] [SCons/Python] Create the source distribution Add a new option for the Python package to build a source distribution. It is added as a new interface to simplify copying files. All of the Cantera plus external library source code is copied into the interface folder for building. Actually building the source distribution is handled by the 'build' package, which becomes a new dependency to build the sdist. 'build' handles installing all the build-time dependencies in an isolated environment using specifications in pyproject.toml. --- SConstruct | 21 ++- interfaces/cython/cantera/test/__init__.py | 2 +- interfaces/python_sdist/MANIFEST.in | 10 ++ interfaces/python_sdist/SConscript | 147 ++++++++++++++++++++ interfaces/python_sdist/pyproject.toml | 3 + interfaces/python_sdist/setup.cfg.in | 69 ++++++++++ interfaces/python_sdist/setup.py | 148 +++++++++++++++++++++ 7 files changed, 393 insertions(+), 7 deletions(-) create mode 100644 interfaces/python_sdist/MANIFEST.in create mode 100644 interfaces/python_sdist/SConscript create mode 100644 interfaces/python_sdist/pyproject.toml create mode 100644 interfaces/python_sdist/setup.cfg.in create mode 100644 interfaces/python_sdist/setup.py diff --git a/SConstruct b/SConstruct index 46b26d2bd2..5354852fb8 100644 --- a/SConstruct +++ b/SConstruct @@ -88,7 +88,8 @@ if os.name not in ["nt", "posix"]: sys.exit(1) valid_commands = ("build", "clean", "install", "uninstall", - "help", "msi", "samples", "sphinx", "doxygen", "dump") + "help", "msi", "samples", "sphinx", "doxygen", "dump", + "sdist") for command in COMMAND_LINE_TARGETS: if command not in valid_commands and not command.startswith('test'): @@ -239,6 +240,10 @@ config_options = [ Cython) are installed. Note: 'y' is a synonym for 'full' and 'n' is a synonym for 'none'.""", "default", ("full", "minimal", "none", "n", "y", "default")), + BoolOption( + "python_sdist", + """Setting this option to True builds the Python sdist.""", + False), PathOption( "python_cmd", """Cantera needs to know where to find the Python interpreter. If @@ -817,6 +822,8 @@ if 'doxygen' in COMMAND_LINE_TARGETS: env['doxygen_docs'] = True if 'sphinx' in COMMAND_LINE_TARGETS: env['sphinx_docs'] = True +if "sdist" in COMMAND_LINE_TARGETS: + env["python_sdist"] = True for arg in ARGUMENTS: if arg not in config: @@ -1349,7 +1356,7 @@ if env['VERBOSE']: env['python_cmd_esc'] = quoted(env['python_cmd']) # Python Package Settings -python_min_version = parse_version('3.5') +python_min_version = parse_version("3.6") # 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 @@ -1868,6 +1875,10 @@ if env['matlab_toolbox'] == 'y': if env['doxygen_docs'] or env['sphinx_docs']: SConscript('doc/SConscript') +if env["python_sdist"]: + VariantDir("build/python_sdist", "interfaces/python_sdist", duplicate=1) + SConscript("interfaces/python_sdist/SConscript", variant_dir="build/python_sdist") + # Sample programs (also used from test_problems/SConscript) VariantDir('build/samples', 'samples', duplicate=0) sampledir_excludes = ['\\.o$', '^~$', '\\.in', 'SConscript'] @@ -1896,12 +1907,10 @@ def postBuildMessage(target, source, env): 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.") + print(" WARNING: You set the 'googletest' to 'none' and all its tests will be skipped.") + print("- To install, type 'scons install'.") 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) diff --git a/interfaces/cython/cantera/test/__init__.py b/interfaces/cython/cantera/test/__init__.py index 4476c62b0f..8fb72ee1bf 100644 --- a/interfaces/cython/cantera/test/__init__.py +++ b/interfaces/cython/cantera/test/__init__.py @@ -15,5 +15,5 @@ from .test_transport import * from .test_utils import * -cantera.add_directory(Path(__file__) / "data") +cantera.add_directory(Path(__file__).parent / "data") cantera.add_directory(Path(__file__).parents[1] / "examples" / "surface_chemistry") diff --git a/interfaces/python_sdist/MANIFEST.in b/interfaces/python_sdist/MANIFEST.in new file mode 100644 index 0000000000..060d90ecf3 --- /dev/null +++ b/interfaces/python_sdist/MANIFEST.in @@ -0,0 +1,10 @@ +include cantera/_cantera.cpp +include cantera/_cantera.h +recursive-include cantera *.pyx +include sundials_config.h.in +graft include +recursive-include ext *.h +recursive-include src *.h +recursive-include ext/libexecstream/posix *.cpp +recursive-include ext/libexecstream/win *.cpp +exclude include/cantera/ext/sundials/sundials_config.h diff --git a/interfaces/python_sdist/SConscript b/interfaces/python_sdist/SConscript new file mode 100644 index 0000000000..79dfb55b61 --- /dev/null +++ b/interfaces/python_sdist/SConscript @@ -0,0 +1,147 @@ +"""SDist of the Python Module""" +from pathlib import Path +import re +import shutil +from textwrap import dedent + +from build import ProjectBuilder +from build.env import IsolatedEnvBuilder + +from buildutils import logger + +Import("env") + +localenv = env.Clone() + +sdist_targets = [] + + +def sdist(targets): + sdist_targets.extend(targets) + return targets + + +def copy_ext_src(target, source, env): + target_ext = Path(target[0].abspath) + source_ext = Path(source[0].abspath) + if target_ext.is_dir(): + shutil.rmtree(target_ext) + + # fmt library + FMT_ROOT = target_ext / "fmt" + FMT_ROOT.mkdir(parents=True) # Needed to be able to use copy2 + for cc_file in (source_ext / "fmt" / "src").glob("*.cc"): + shutil.copy2(cc_file, FMT_ROOT) + shutil.copytree(source_ext / "fmt" / "include" / "fmt", FMT_ROOT / "fmt") + + # libexecstream + EXECSTREAM_ROOT = target_ext / "libexecstream" + EXECSTREAM_ROOT.mkdir(parents=True) # Needed to be able to use copy2 + shutil.copy2(source_ext / "libexecstream" / "exec-stream.cpp", EXECSTREAM_ROOT) + shutil.copy2(source_ext / "libexecstream" / "exec-stream.h", EXECSTREAM_ROOT) + shutil.copytree(source_ext / "libexecstream" / "win", EXECSTREAM_ROOT / "win") + shutil.copytree(source_ext / "libexecstream" / "posix", EXECSTREAM_ROOT / "posix") + + # yaml-cpp library + YAML_ROOT = target_ext / "yaml-cpp" + shutil.copytree(source_ext / "yaml-cpp" / "src", YAML_ROOT) + shutil.copytree(source_ext / "yaml-cpp" / "include" / "yaml-cpp", + YAML_ROOT / "yaml-cpp") + + # SUNDIALS library + SUNDIALS_ROOT = target_ext / "sundials" + subdirs = ["sundials", "nvector/serial", "cvodes", "ida", "sunmatrix/band", + "sunmatrix/dense", "sunmatrix/sparse", "sunlinsol/dense", + "sunlinsol/band", "sunlinsol/spgmr", "sunnonlinsol/newton"] + ignores = shutil.ignore_patterns("fsun*", "CMake*", "fmod", "fcmix") + for subdir in subdirs: + shutil.copytree( + source_ext / "sundials" / "src" / subdir, + SUNDIALS_ROOT / subdir, + ignore=ignores, + ) + + +def replace_git_hash(target, source, env): + # Avoid having to set a C preprocessor define at compile time, since + # the git commit is unknown from the sdist + target = Path(target[0].abspath) + source = Path(source[0].abspath) + git_commit_replaced = re.sub("#ifdef GIT_COMMIT.*?#endif", + f""" return "{env['git_commit']}";""", + source.read_text(), + flags=re.DOTALL) + target.write_text(git_commit_replaced) + + +# Use RecursiveInstall to be able to exclude files and folders. +sdist(localenv.RecursiveInstall( + "src", + "#src", + exclude=["fortran", "matlab", r"global\.cpp", "SCons.*"], +)) + +sdist(localenv.Command("src/base/global.cpp", "#src/base/global.cpp", + replace_git_hash)) + +include_target = sdist(localenv.Command("include", "#include", + Copy("$TARGET", "$SOURCE"))) +localenv.Depends(include_target, env["config_h_target"]) +localenv.Depends(include_target, env["ext_include_copies_target"]) + +sdist(localenv.Command("ext", "#ext", copy_ext_src)) + +# Use RecursiveInstall to make sure that files are not overwritten during the copy. +# A normal Copy Action would fail because of the existing directories. +sdist(localenv.RecursiveInstall("cantera", + "#interfaces/cython/cantera", + exclude=["__pycache__"])) +sdist(localenv.RecursiveInstall("cantera/data", + "#build/data")) +sdist(localenv.RecursiveInstall("cantera/test/data", + "#test/data")) + +# Copy the minimal Sundials configuration template into the sdist so that +# it can be filled in at compile time on the user's machine +sdist(localenv.Command("sundials_config.h.in", "#ext/sundials_config.h.in", + Copy("$TARGET", "$SOURCE"))) + +license = sdist(localenv.Command("LICENSE.txt", "#build/ext/LICENSE.txt", + Copy("$TARGET", "$SOURCE"))) +localenv.Depends(license, localenv["license_target"]) + +sdist(localenv.SubstFile("setup.cfg", "setup.cfg.in")) +sdist(localenv.Command("README.rst", "#README.rst", Copy("$TARGET", "$SOURCE"))) + + +def build_sdist(target, source, env): + build_dir = Path(source[0].abspath).parent + builder = ProjectBuilder(str(build_dir)) + with IsolatedEnvBuilder() as build_env: + builder.python_executable = build_env.executable + builder.scripts_dir = build_env.scripts_dir + # first install the build dependencies + build_env.install(builder.build_system_requires) + # then get the extra required dependencies from the backend + build_env.install(builder.get_requires_for_build("sdist")) + builder.build("sdist", str(build_dir / "dist"), {}) + + +def finish_sdist_message(target, source, env): + sdist = Path(source[0].path).name + message = dedent(f""" + ******************************************************* + Python sdist '{sdist}' created successfully. + The sdist file is in the 'build/python_sdist/dist' + directory. + ******************************************************* + """) + logger.info(message, print_level=False) + + +sdist_target = f"dist/Cantera-{env['cantera_version']}.tar.gz" +sdist_sources = ("setup.py", "pyproject.toml", "MANIFEST.in") +built_sdist = localenv.Command(sdist_target, sdist_sources, build_sdist) +finish_sdist = localenv.Command("finish_sdist", sdist_target, finish_sdist_message) +localenv.Depends(built_sdist, sdist_targets) +env.Alias("sdist", finish_sdist) diff --git a/interfaces/python_sdist/pyproject.toml b/interfaces/python_sdist/pyproject.toml new file mode 100644 index 0000000000..a272bb9561 --- /dev/null +++ b/interfaces/python_sdist/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=43.0.0", "wheel", "oldest-supported-numpy", "Cython>=0.29.12"] +build-backend = "setuptools.build_meta" diff --git a/interfaces/python_sdist/setup.cfg.in b/interfaces/python_sdist/setup.cfg.in new file mode 100644 index 0000000000..ccf42785c0 --- /dev/null +++ b/interfaces/python_sdist/setup.cfg.in @@ -0,0 +1,69 @@ +[metadata] +name = Cantera +version = @cantera_version@ +description = Cantera is an open-source suite of tools for problems involving chemical kinetics, thermodynamics, and transport processes. +long_description = file: README.rst +long_description_content_type = text/x-rst +license_files = LICENSE.txt +url = https://cantera.org +author = Cantera Developers +author_email = developers@cantera.org +keywords = chemistry physics +license = BSD 3-Clause License +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Education + Intended Audience :: Science/Research + License :: OSI Approved :: BSD License + Operating System :: MacOS :: MacOS X + Operating System :: Microsoft :: Windows + Operating System :: POSIX :: Linux + Programming Language :: C + Programming Language :: C++ + Programming Language :: Cython + Programming Language :: Fortran + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Topic :: Scientific/Engineering :: Chemistry + Topic :: Scientific/Engineering :: Physics +project_urls = + Documentation = https://cantera.org/documentation + Funding = https://numfocus.org/donate-to-cantera + Source = https://github.com/Cantera/cantera + Tracker = https://github.com/Cantera/cantera/issues + +[options] +zip_safe = False +include_package_data = True +install_requires = + numpy >= 1.12.0 + ruamel.yaml >= 0.15.34 +python_requires = >=@py_min_ver_str@ +packages = + cantera + cantera.data + cantera.test + cantera.test.data + cantera.examples + +[options.package_data] +cantera.data = *.*, */*.* +cantera.test.data = *.*, */*.* +cantera.examples = */*.* +cantera = *.pxd + +[options.extras_require] +hdf5 = h5py +pandas = pandas + +[options.entry_points] +console_scripts = + ck2cti = cantera.ck2cti:script_entry_point + ctml_writer = cantera.ctml_writer:main + ck2yaml = cantera.ck2yaml:script_entry_point + cti2yaml = cantera.cti2yaml:main + ctml2yaml = cantera.ctml2yaml:main diff --git a/interfaces/python_sdist/setup.py b/interfaces/python_sdist/setup.py new file mode 100644 index 0000000000..4bd16ab71e --- /dev/null +++ b/interfaces/python_sdist/setup.py @@ -0,0 +1,148 @@ +import sys +import os +from setuptools import setup, Extension +from setuptools.command.install import install +from setuptools.command.develop import develop +from pathlib import Path +import numpy +import shutil + +HERE = Path(__file__).parent +CT_SRC = HERE / "src" +EXT_SRC = HERE / "ext" +CT_INCLUDE = HERE / "include" +BOOST_INCLUDE = None +FORCE_CYTHON_COMPILE = False + +CYTHON_BUILT_FILES = [HERE / "cantera" / f"_cantera.{ext}" for ext in ("cpp", "h")] + + +class CanteraOptionsMixin: + """Custom options for the install and develop commands. + + Modeled after https://stackoverflow.com/a/53833930 + """ + user_options = [ + ("force-cython-compile", None, "Force compilation of .pyx files via Cython"), + ("boost-include", None, "Location of the Boost header files."), + ] + + def initialize_options(self): + super().initialize_options() + self.force_cython_compile = False + self.boost_include = None + + def finalize_options(self): + if self.boost_include is not None: + if not Path(self.boost_include).is_dir(): + raise TypeError(f"The path {self.boost_include!r} is not a directory.") + super().finalize_options() + + def run(self): + global BOOST_INCLUDE, FORCE_CYTHON_COMPILE + BOOST_INCLUDE = self.boost_include + FORCE_CYTHON_COMPILE = self.force_cython_compile + super().run() + + +class InstallCommand(CanteraOptionsMixin, install): + user_options = (getattr(install, "user_options", []) + + CanteraOptionsMixin.user_options) + + +class DevelopCommand(CanteraOptionsMixin, develop): + user_options = (getattr(develop, "user_options", []) + + CanteraOptionsMixin.user_options) + + +if ( + not all(p.exists() for p in CYTHON_BUILT_FILES) + or "sdist" in sys.argv + or FORCE_CYTHON_COMPILE + or os.environ.get("FORCE_CYTHON_COMPILE", False) +): + from Cython.Build import cythonize + CYTHON_EXT = ".pyx" + for p in CYTHON_BUILT_FILES: + if p.exists(): + p.unlink() +else: + CYTHON_EXT = ".cpp" + + def cythonize(extensions): + """Define a no-op for when we're not using Cython.""" + return extensions + +source_files = ["cantera/_cantera" + CYTHON_EXT] +source_files += list(map(str, CT_SRC.glob("**/*.cpp"))) +sundials_sources = list(map(str, EXT_SRC.glob("sundials/**/*.c"))) +yaml_cpp_sources = list(map(str, EXT_SRC.glob("yaml-cpp/**/*.cpp"))) +fmt_sources = list(map(str, EXT_SRC.glob("fmt/*.cc"))) +libexecstream_sources = [str(EXT_SRC / "libexecstream" / "exec-stream.cpp")] + +include_dirs = [ + str(CT_INCLUDE), + str(CT_INCLUDE / "cantera" / "ext"), + str(CT_SRC), + numpy.get_include() +] + +if "BOOST_INCLUDE" in os.environ: + include_dirs.append(os.environ["BOOST_INCLUDE"]) +elif BOOST_INCLUDE is not None: + include_dirs.append(BOOST_INCLUDE) + +if sys.platform != "win32": + extra_compile_flags = ["-std=c++11"] + sundials_configh = { + "SUNDIALS_USE_GENERIC_MATH": "#define SUNDIALS_USE_GENERIC_MATH 1", + "SUNDIALS_BLAS_LAPACK": "/* #undef SUNDIALS_BLAS_LAPACK */" + } + sundials_cflags = ["-w"] + sundials_macros = [] +else: + extra_compile_flags = [] + sundials_macros = [("_CRT_SECURE_NO_WARNINGS", None)] + sundials_configh = { + "SUNDIALS_USE_GENERIC_MATH": "/* #undef SUNDIALS_USE_GENERIC_MATH */", + "SUNDIALS_BLAS_LAPACK": "/* #undef SUNDIALS_BLAS_LAPACK */" + } + sundials_cflags = [] + +config_h_in = (HERE / "sundials_config.h.in").read_text() +config_h = HERE / "sundials_config.h" +config_h.write_text(config_h_in.format_map(sundials_configh)) +shutil.copy2(config_h, EXT_SRC / "sundials" / "sundials") +shutil.copy2(config_h, CT_INCLUDE / "cantera" / "ext" / "sundials") + +extensions = cythonize([ + Extension( + "cantera._cantera", + source_files, + include_dirs=include_dirs, + extra_compile_args=extra_compile_flags, + language="c++", + ), +]) + + +def lib_def(sources, cflags, include_dirs, macros): + """Convenience factory to create the dictionary for a Setuptools library build.""" + return dict(sources=sources, cflags=cflags, include_dirs=include_dirs, + macros=macros) + + +sundials_inc_dir = include_dirs + [str(EXT_SRC / "sundials" / "sundials")] +libraries = [ + ("sundials", lib_def(sundials_sources, sundials_cflags, sundials_inc_dir, + sundials_macros)), + ("yaml-cpp", lib_def(yaml_cpp_sources, extra_compile_flags, include_dirs, [])), + ("fmtlib", lib_def(fmt_sources, extra_compile_flags, include_dirs, [])), + ("libexecstream", {"sources": libexecstream_sources, "include_dirs": include_dirs}) +] + +setup( + ext_modules=extensions, + libraries=libraries, + cmdclass={"install": InstallCommand, "develop": DevelopCommand}, +) From 924fc08cfdf657d769d0d4e56760d9fdb6ac202b Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Tue, 13 Jul 2021 22:01:48 -0400 Subject: [PATCH 04/11] [SCons] Build sundials_config and copy separately This is the same mechanism as employed for config.h in the main SConstruct. It's needed here to avoid repeatedly copying the include directory into the sdist, which fails due to the existing directory. --- ext/SConscript | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ext/SConscript b/ext/SConscript index 0358cc072e..e66f7e3c17 100644 --- a/ext/SConscript +++ b/ext/SConscript @@ -8,6 +8,7 @@ copyenv = localenv.Clone() # no CPPPATH addition, to avoid circular dependencies license_files = {"Cantera": File("#License.txt"), "Libexecstream": File("#ext/libexecstream/doc/license.txt")} + def prep_default(env): localenv = env.Clone() @@ -70,9 +71,17 @@ if env['system_sundials'] == 'n': sundials_configh['SUNDIALS_USE_GENERIC_MATH'] = 1 if env['use_lapack']: sundials_configh['SUNDIALS_BLAS_LAPACK'] = 1 - localenv.AlwaysBuild(env.Command('#include/cantera/ext/sundials/sundials_config.h', - 'sundials_config.h.in', - ConfigBuilder(sundials_configh))) + sundials_configh_build = env.Command("#build/ext/sundials_config.h.build", + "sundials_config.h.in", + ConfigBuilder(sundials_configh)) + # This separate copy operation, which SCons will skip if sundials_config.h.build is + # unmodified, prevents unnecessary re-copies of files in the #include directory + localenv.AlwaysBuild(sundials_configh_build) + ext_copies.extend( + localenv.Command("#include/cantera/ext/sundials/sundials_config.h", + "#build/ext/sundials_config.h.build", + Copy("$TARGET", "$SOURCE")) + ) # Copy sundials header files into common include directory for subdir in ('sundials', 'nvector', 'cvodes', 'ida', 'sunmatrix', From 8e8e42c8316a93e41a7bc57b2dea9ad23a073760 Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Fri, 16 Jul 2021 14:35:55 -0400 Subject: [PATCH 05/11] Deprecate global canteraRoot function The function is not used anywhere within Cantera. When this function is removed, the CANTERA_ROOT define in config.h can be removed as well. --- include/cantera/base/global.h | 1 + src/base/global.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/cantera/base/global.h b/include/cantera/base/global.h index fcc76a370f..908efabd5d 100644 --- a/include/cantera/base/global.h +++ b/include/cantera/base/global.h @@ -113,6 +113,7 @@ std::string gitCommit(); * @returns a string containing the name of the base directory where %Cantera is * installed. If the environmental variable CANTERA_ROOT is defined, this * function will return its value, preferentially. + * @deprecated Unused within Cantera. To be removed after Cantera 2.6 * * @ingroup inputfiles */ diff --git a/src/base/global.cpp b/src/base/global.cpp index 14f8e2d25a..d0a0377640 100644 --- a/src/base/global.cpp +++ b/src/base/global.cpp @@ -169,6 +169,8 @@ doublereal actEnergyToSI(const std::string& unit) string canteraRoot() { + warn_deprecated("canteraRoot", + "Unused in Cantera. To be removed after Cantera 2.6"); char* ctroot = getenv("CANTERA_ROOT"); if (ctroot != 0) { return string(ctroot); From 121011d252939d55cce7d79882bfa168d347b657 Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Fri, 16 Jul 2021 15:07:54 -0400 Subject: [PATCH 06/11] [Cython] Import sysconfig directly The sysconfig module is available since Python 3.2. Distutils is deprecated and slated for removal from Python in the next few years. Likewise, the "SO" config variable has been deprecated for some time and was slated for removal in Python 3.8, although that seems not to have happened. --- interfaces/cython/SConscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/SConscript b/interfaces/cython/SConscript index c55debb2fb..1477d32b2d 100644 --- a/interfaces/cython/SConscript +++ b/interfaces/cython/SConscript @@ -32,9 +32,9 @@ testFiles = localenv.RecursiveInstall('#interfaces/cython/cantera/test/data', build(testFiles) # Get information needed to build the Python module -script = '\n'.join(("from distutils.sysconfig import *", +script = '\n'.join(("from sysconfig import *", "import numpy", - "print(get_config_var('EXT_SUFFIX') or get_config_var('SO'))", + "print(get_config_var('EXT_SUFFIX'))", "print(get_config_var('INCLUDEPY'))", "print(get_config_var('LDLIBRARY'))", "print(get_config_var('prefix'))", From d7d315d7c3edfd2912cde7c23f58132c7f468428 Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Fri, 16 Jul 2021 15:08:48 -0400 Subject: [PATCH 07/11] [Cython] Scope hypot redefinition The bug was fixed in Python 3.7.3, so it no longer required. --- interfaces/cython/SConscript | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/SConscript b/interfaces/cython/SConscript index 1477d32b2d..f1226311e0 100644 --- a/interfaces/cython/SConscript +++ b/interfaces/cython/SConscript @@ -60,8 +60,10 @@ elif localenv['OS'] == 'Windows': localenv.Append(LIBS='python{}'.format(py_version.replace('.',''))) if localenv['OS_BITS'] == 64: localenv.Append(CPPDEFINES='MS_WIN64') - # fix for http://bugs.python.org/issue11566 - localenv.Append(CPPDEFINES={'_hypot':'hypot'}) + # fix for https://bugs.python.org/issue11566. Fixed in 3.7.3 and higher. + # See https://github.com/python/cpython/pull/11283 + if parse_version(py_version) < parse_version("3.7.3"): + localenv.Append(CPPDEFINES={"_hypot": "hypot"}) elif localenv['OS'] == 'Cygwin': # extract 'pythonX.Y' from 'libpythonX.Y.dll.a' localenv.Append(LIBS=pylib[3:-6]) From 757002983c0ab727f116edf674d97c6690cf31bd Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Fri, 16 Jul 2021 15:14:16 -0400 Subject: [PATCH 08/11] [Cython] Use setup.cfg to configure the package This change mirrors the one for the source distribution. This change results in a non-executable template file (setup.cfg.in), which makes edits easier. This change removes the requirement to template the Python extension into setup.py, and adds a Setuptools extension module. This results in correct builds when bdist_wheel is specified and uses package_data properly. --- SConstruct | 4 +- interfaces/cython/.gitignore | 8 +-- interfaces/cython/SConscript | 11 ++-- interfaces/cython/setup.cfg.in | 69 ++++++++++++++++++++++ interfaces/cython/setup.py | 7 +++ interfaces/cython/setup.py.in | 103 --------------------------------- 6 files changed, 89 insertions(+), 113 deletions(-) create mode 100644 interfaces/cython/setup.cfg.in create mode 100644 interfaces/cython/setup.py delete mode 100644 interfaces/cython/setup.py.in diff --git a/SConstruct b/SConstruct index 5354852fb8..8cb6302284 100644 --- a/SConstruct +++ b/SConstruct @@ -107,7 +107,9 @@ if "clean" in COMMAND_LINE_TARGETS: remove_directory("include/cantera/ext") remove_file("interfaces/cython/cantera/_cantera.cpp") remove_file("interfaces/cython/cantera/_cantera.h") - remove_file("interfaces/cython/setup.py") + remove_file("interfaces/cython/setup.cfg") + remove_file("interfaces/cython/LICENSE.txt") + remove_file("interfaces/cython/README.rst") remove_file("interfaces/python_minimal/setup.py") remove_file("config.log") remove_directory("doc/sphinx/matlab/examples") diff --git a/interfaces/cython/.gitignore b/interfaces/cython/.gitignore index c76ec86cfa..2a5acec62d 100644 --- a/interfaces/cython/.gitignore +++ b/interfaces/cython/.gitignore @@ -2,10 +2,8 @@ cantera/*.cpp cantera/*.c cantera/data cantera/test/data -setup.py -scripts/ctml_writer.py -scripts/ctml_writer -scripts/ck2cti.py -scripts/ck2cti Cantera.egg-info dist +setup.cfg +LICENSE.txt +README.rst diff --git a/interfaces/cython/SConscript b/interfaces/cython/SConscript index f1226311e0..30ff12d171 100644 --- a/interfaces/cython/SConscript +++ b/interfaces/cython/SConscript @@ -75,16 +75,19 @@ ext = localenv.LoadableModule('#build/python/cantera/_cantera{}'.format(module_e SHLIBPREFIX='', LIBSUFFIXES=[module_ext]) localenv['py_extension'] = ext[0].name -localenv.SubstFile('setup.py', 'setup.py.in') +setup_cfg = localenv.SubstFile("setup.cfg", "setup.cfg.in") +readme = localenv.Command("README.rst", "#README.rst", Copy("$TARGET", "$SOURCE")) +license = localenv.Command("LICENSE.txt", "#build/ext/LICENSE.txt", + Copy("$TARGET", "$SOURCE")) +localenv.Depends(license, localenv["license_target"]) build_cmd = ('cd interfaces/cython &&' ' $python_cmd_esc setup.py build --build-lib=../../build/python') -mod = build(localenv.Command('#build/python/cantera/__init__.py', 'setup.py', +mod = build(localenv.Command("#build/python/cantera/__init__.py", "setup.cfg", build_cmd)) env['python_module'] = mod env['python_extension'] = ext -localenv.Depends(mod, ext) -localenv.Depends(mod, dataFiles + testFiles) +localenv.Depends(mod, [ext, dataFiles, testFiles, setup_cfg, readme, license]) localenv.Depends(ext, localenv['cantera_staticlib']) for f in (multi_glob(localenv, 'cantera', 'py') + diff --git a/interfaces/cython/setup.cfg.in b/interfaces/cython/setup.cfg.in new file mode 100644 index 0000000000..ccf42785c0 --- /dev/null +++ b/interfaces/cython/setup.cfg.in @@ -0,0 +1,69 @@ +[metadata] +name = Cantera +version = @cantera_version@ +description = Cantera is an open-source suite of tools for problems involving chemical kinetics, thermodynamics, and transport processes. +long_description = file: README.rst +long_description_content_type = text/x-rst +license_files = LICENSE.txt +url = https://cantera.org +author = Cantera Developers +author_email = developers@cantera.org +keywords = chemistry physics +license = BSD 3-Clause License +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Education + Intended Audience :: Science/Research + License :: OSI Approved :: BSD License + Operating System :: MacOS :: MacOS X + Operating System :: Microsoft :: Windows + Operating System :: POSIX :: Linux + Programming Language :: C + Programming Language :: C++ + Programming Language :: Cython + Programming Language :: Fortran + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Topic :: Scientific/Engineering :: Chemistry + Topic :: Scientific/Engineering :: Physics +project_urls = + Documentation = https://cantera.org/documentation + Funding = https://numfocus.org/donate-to-cantera + Source = https://github.com/Cantera/cantera + Tracker = https://github.com/Cantera/cantera/issues + +[options] +zip_safe = False +include_package_data = True +install_requires = + numpy >= 1.12.0 + ruamel.yaml >= 0.15.34 +python_requires = >=@py_min_ver_str@ +packages = + cantera + cantera.data + cantera.test + cantera.test.data + cantera.examples + +[options.package_data] +cantera.data = *.*, */*.* +cantera.test.data = *.*, */*.* +cantera.examples = */*.* +cantera = *.pxd + +[options.extras_require] +hdf5 = h5py +pandas = pandas + +[options.entry_points] +console_scripts = + ck2cti = cantera.ck2cti:script_entry_point + ctml_writer = cantera.ctml_writer:main + ck2yaml = cantera.ck2yaml:script_entry_point + cti2yaml = cantera.cti2yaml:main + ctml2yaml = cantera.ctml2yaml:main diff --git a/interfaces/cython/setup.py b/interfaces/cython/setup.py new file mode 100644 index 0000000000..a930748fe1 --- /dev/null +++ b/interfaces/cython/setup.py @@ -0,0 +1,7 @@ +from setuptools import setup, Extension + +extension = Extension("cantera._cantera", sources=[]) + +setup( + ext_modules=[extension], +) diff --git a/interfaces/cython/setup.py.in b/interfaces/cython/setup.py.in deleted file mode 100644 index 85e9a01f74..0000000000 --- a/interfaces/cython/setup.py.in +++ /dev/null @@ -1,103 +0,0 @@ -import os -from setuptools import setup - -# Monkey patch to prevent bdist_msi from incorrectly overwriting the value of -# build-lib specified on the command line. -# See http://bugs.python.org/issue1109963 - -# This patch works by returning False the first time that the -# 'has_ext_modules' method is called in bdist_msi.run, which is where the -# replacement of build_lib happens. Subsequent calls to 'has_ext_modules' -# should use the correct value so that the resulting installer is specific to -# this Python version. Known to affect Python versions 2.6 through 3.7. If -# this bug is ever fixed, this patch should be made conditional on the Python -# version. -if os.name == 'nt': - from distutils.command.bdist_msi import bdist_msi - bdist_run_orig = bdist_msi.run - def bdist_run_new(self): - has_ext_modules_orig = self.distribution.has_ext_modules - self._first_call = True - def has_ext_modules(): - if self._first_call: - self._first_call = False - return False - else: - return has_ext_modules_orig() - - self.distribution.has_ext_modules = has_ext_modules - return bdist_run_orig(self) - - bdist_msi.run = bdist_run_new - -# Monkey patch to resolve https://bugs.python.org/issue34251 -# (Affects Python 3.7.0) -if os.name == 'nt': - import msilib - msilib.Win64 = msilib.AMD64 - -# Copy the long_description from docs/sphinx/index.rst -long_description = """ -Cantera is a suite of object-oriented software tools for problems involving -chemical kinetics, thermodynamics, and/or transport processes. - -Cantera provides types (or classes) of objects representing phases of -matter, interfaces between these phases, reaction managers, time-dependent -reactor networks, and steady one-dimensional reacting flows. Cantera is -currently used for applications including combustion, detonations, -electrochemical energy conversion and storage, fuel cells, batteries, aqueous -electrolyte solutions, plasmas, and thin film deposition. - -Cantera can be used from Python and Matlab, or in applications written -in C++ and Fortran 90. -""" - -setup( - name="Cantera", - version="@cantera_version@", - description="The Cantera Python Interface", - long_description=long_description, - author="Raymond Speth", - author_email="speth@mit.edu", - url="https://cantera.org", - packages=[ - 'cantera', - 'cantera.data', - 'cantera.test', - 'cantera.test.data', - 'cantera.examples', - ], - entry_points={ - 'console_scripts': [ - 'ck2cti=cantera.ck2cti:script_entry_point', - 'ctml_writer=cantera.ctml_writer:main', - 'ck2yaml=cantera.ck2yaml:script_entry_point', - 'cti2yaml=cantera.cti2yaml:main', - 'ctml2yaml=cantera.ctml2yaml:main', - ], - }, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Education', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: C++', - 'Programming Language :: Fortran', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Topic :: Scientific/Engineering :: Chemistry', - ], - package_data={ - 'cantera.data': ['*.*', '*/*.*'], - 'cantera.test.data': ['*.*', '*/*.*'], - 'cantera.examples': ['*/*.*'], - 'cantera': ["@py_extension@", '*.pxd'], - }, - zip_safe=False, - python_requires=">=@py_min_ver_str@", -) From 172620e655914a1a0a55e7c5ac3841811e6c2c45 Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Fri, 16 Jul 2021 15:36:57 -0400 Subject: [PATCH 09/11] [Python Minimal] Use setup.cfg configuration This change provides similar benefits as for the other two Python distributions. It should allow easier uploading to PyPI and now includes the README and License files. --- SConstruct | 4 ++- interfaces/python_minimal/.gitignore | 5 +-- interfaces/python_minimal/SConscript | 23 +++++++----- interfaces/python_minimal/setup.cfg.in | 49 ++++++++++++++++++++++++++ interfaces/python_minimal/setup.py | 3 ++ interfaces/python_minimal/setup.py.in | 20 ----------- 6 files changed, 73 insertions(+), 31 deletions(-) create mode 100644 interfaces/python_minimal/setup.cfg.in create mode 100644 interfaces/python_minimal/setup.py delete mode 100644 interfaces/python_minimal/setup.py.in diff --git a/SConstruct b/SConstruct index 8cb6302284..3eb0fd07f1 100644 --- a/SConstruct +++ b/SConstruct @@ -110,7 +110,9 @@ if "clean" in COMMAND_LINE_TARGETS: remove_file("interfaces/cython/setup.cfg") remove_file("interfaces/cython/LICENSE.txt") remove_file("interfaces/cython/README.rst") - remove_file("interfaces/python_minimal/setup.py") + remove_file("interfaces/python_minimal/setup.cfg") + remove_file("interfaces/python_minimal/LICENSE.txt") + remove_file("interfaces/python_minimal/README.rst") remove_file("config.log") remove_directory("doc/sphinx/matlab/examples") remove_file("doc/sphinx/matlab/examples.rst") diff --git a/interfaces/python_minimal/.gitignore b/interfaces/python_minimal/.gitignore index 5ca4392ca9..8a8b5c9255 100644 --- a/interfaces/python_minimal/.gitignore +++ b/interfaces/python_minimal/.gitignore @@ -1,5 +1,3 @@ -setup*.py -scripts/* cantera/ck2cti.py cantera/ctml_writer.py cantera/ck2yaml.py @@ -8,3 +6,6 @@ cantera/ctml2yaml.py build dist Cantera_minimal.egg-info +LICENSE.txt +README.rst +setup.cfg diff --git a/interfaces/python_minimal/SConscript b/interfaces/python_minimal/SConscript index 9c3ab3e78c..2047a06ef9 100644 --- a/interfaces/python_minimal/SConscript +++ b/interfaces/python_minimal/SConscript @@ -7,23 +7,30 @@ Import('env', 'build', 'install') localenv = env.Clone() -make_setup = build(localenv.SubstFile('setup.py', 'setup.py.in')) +make_setup = build(localenv.SubstFile("setup.cfg", "setup.cfg.in")) # copy scripts from the full Cython module -for script in ['ctml_writer', 'ck2cti', 'ck2yaml', 'cti2yaml', 'ctml2yaml']: +for script in ["ctml_writer", "ck2cti", "ck2yaml", "cti2yaml", "ctml2yaml"]: # The actual script - s = build(env.Command('cantera/{}.py'.format(script), - '#interfaces/cython/cantera/{}.py'.format(script), - Copy('$TARGET', '$SOURCE'))) + s = build(env.Command(f"cantera/{script}.py", + f"#interfaces/cython/cantera/{script}.py", + Copy("$TARGET", "$SOURCE"))) localenv.Depends(make_setup, s) -build_cmd = ('cd interfaces/python_minimal &&' - ' $python_cmd_esc setup.py build --build-lib=../../build/python') +build_cmd = ("cd interfaces/python_minimal && " + "$python_cmd_esc setup.py build --build-base=../../build/python") -mod = build(localenv.Command('#build/python/cantera/__init__.py', 'setup.py', +mod = build(localenv.Command("#build/python/cantera/__init__.py", "setup.cfg", build_cmd)) env['python_module'] = mod +readme = localenv.Command("README.rst", "#README.rst", Copy("$TARGET", "$SOURCE")) +# The target of this command must match the file listed in setup.cfg.in +license = localenv.Command("LICENSE.txt", "#License.txt", + Copy("$TARGET", "$SOURCE")) +localenv.Depends(license, localenv["license_target"]) +localenv.Depends(mod, [make_setup, readme, license]) + if localenv['PYTHON_INSTALLER'] == 'direct': if localenv['python_prefix'] == 'USER': # Install to the OS-dependent user site-packages directory diff --git a/interfaces/python_minimal/setup.cfg.in b/interfaces/python_minimal/setup.cfg.in new file mode 100644 index 0000000000..d656b87220 --- /dev/null +++ b/interfaces/python_minimal/setup.cfg.in @@ -0,0 +1,49 @@ +[metadata] +name = Cantera_minimal +version = @cantera_version@ +description = A minimal Cantera interface, containing only the input file conversion scripts +long_description = This Cantera interface contains the conversion scripts for input files into the Cantera format. For the full Python interface, please see [Cantera](https://pypi.org/project/cantera). +long_description_content_type = text/markdown +license_files = LICENSE.txt +url = https://cantera.org +author = Cantera Developers +author_email = developers@cantera.org +keywords = chemistry physics +license = BSD 3-Clause License +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Education + Intended Audience :: Science/Research + License :: OSI Approved :: BSD License + Operating System :: MacOS :: MacOS X + Operating System :: Microsoft :: Windows + Operating System :: POSIX :: Linux + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Topic :: Scientific/Engineering :: Chemistry + Topic :: Scientific/Engineering :: Physics +project_urls = + Documentation = https://cantera.org/documentation + Funding = https://numfocus.org/donate-to-cantera + Source = https://github.com/Cantera/cantera + Tracker = https://github.com/Cantera/cantera/issues + +[options] +zip_safe = True +install_requires = + numpy >= 1.12.0 + ruamel.yaml >= 0.15.34 +python_requires = >=@py_min_ver_str@ +packages = + cantera + +[options.entry_points] +console_scripts = + ck2cti = cantera.ck2cti:script_entry_point + ctml_writer = cantera.ctml_writer:main + ck2yaml = cantera.ck2yaml:script_entry_point + cti2yaml = cantera.cti2yaml:main + ctml2yaml = cantera.ctml2yaml:main diff --git a/interfaces/python_minimal/setup.py b/interfaces/python_minimal/setup.py new file mode 100644 index 0000000000..606849326a --- /dev/null +++ b/interfaces/python_minimal/setup.py @@ -0,0 +1,3 @@ +from setuptools import setup + +setup() diff --git a/interfaces/python_minimal/setup.py.in b/interfaces/python_minimal/setup.py.in deleted file mode 100644 index ead7760440..0000000000 --- a/interfaces/python_minimal/setup.py.in +++ /dev/null @@ -1,20 +0,0 @@ -from setuptools import setup - -setup(name="Cantera_minimal", - version="@cantera_version@", - description="The Minimal Cantera Python Interface", - long_description="", - author="Raymond Speth", - author_email="speth@mit.edu", - url="http://www.cantera.org", - packages = ['cantera'], - entry_points={ - 'console_scripts': [ - 'ck2cti=cantera.ck2cti:script_entry_point', - 'ctml_writer=cantera.ctml_writer:main', - 'ck2yaml=cantera.ck2yaml:script_entry_point', - 'cti2yaml=cantera.cti2yaml:main', - 'ctml2yaml=cantera.ctml2yaml:main', - ], - }, -) From 03023632f55b6d524e1b79059e690d8b711d2475 Mon Sep 17 00:00:00 2001 From: Bryan Weber Date: Fri, 16 Jul 2021 18:57:20 -0400 Subject: [PATCH 10/11] [CI] Relax setuptools requirement on Windows --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a007f3ab76..7898fd57b3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -344,7 +344,7 @@ jobs: architecture: x64 - name: Install Python dependencies run: | - python -m pip install -U pip 'setuptools>=47.0.0,<48' + python -m pip install -U pip 'setuptools>=43.0.0' 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 From 0e8791a813305e3aaee5014449ec61704fa57f45 Mon Sep 17 00:00:00 2001 From: "Bryan W. Weber" Date: Sat, 17 Jul 2021 09:42:01 -0400 Subject: [PATCH 11/11] [Cython] Fix Windows builds For some versions of Python, the DLL module extension included in the sysconfig module is not correct. In that case, the file extension needs to be constructed manually. To make this information easier to get to, the JSON module is used to parse data returned by sysconfig from the Python which will be building the module. --- interfaces/cython/SConscript | 46 +++++++++++++++++++++++++----------- interfaces/cython/setup.py | 4 +--- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/interfaces/cython/SConscript b/interfaces/cython/SConscript index 30ff12d171..068abf87d9 100644 --- a/interfaces/cython/SConscript +++ b/interfaces/cython/SConscript @@ -4,6 +4,7 @@ from os.path import join as pjoin from os.path import normpath from pathlib import Path from pkg_resources import parse_version +import json from buildutils import * Import('env', 'build', 'install') @@ -32,24 +33,41 @@ testFiles = localenv.RecursiveInstall('#interfaces/cython/cantera/test/data', build(testFiles) # Get information needed to build the Python module -script = '\n'.join(("from sysconfig import *", - "import numpy", - "print(get_config_var('EXT_SUFFIX'))", - "print(get_config_var('INCLUDEPY'))", - "print(get_config_var('LDLIBRARY'))", - "print(get_config_var('prefix'))", - "print(get_python_version())", - "print(numpy.get_include())")) -info = get_command_output(localenv["python_cmd"], "-c", script) -module_ext, inc, pylib, prefix, py_version, numpy_include = info.splitlines()[-6:] +script = """\ +from sysconfig import * +import numpy +import json +vars = get_config_vars() +vars["plat"] = get_platform() +vars["numpy_include"] = numpy.get_include() +print(json.dumps(vars)) +""" +info = json.loads(get_command_output(localenv["python_cmd"], "-c", script)) +module_ext = info["EXT_SUFFIX"] +inc = info["INCLUDEPY"] +pylib = info.get("LDLIBRARY") +prefix = info["prefix"] +py_version_short = parse_version(info["py_version_short"]) +py_version_full = parse_version(info["py_version"]) +numpy_include = info["numpy_include"] localenv.Prepend(CPPPATH=[Dir('#include'), inc, numpy_include]) localenv.Prepend(LIBS=localenv['cantera_libs']) +# Fix the module extension for Windows from the sysconfig library. +# See https://github.com/python/cpython/pull/22088 and +# https://bugs.python.org/issue39825 +if ( + py_version_full < parse_version("3.8.7") + and localenv["OS"] == "Windows" + and module_ext == ".pyd" +): + module_ext = f".cp{info['py_version_nodot']}-{info['plat'].replace('-', '_')}.pyd" + # Don't print deprecation warnings for internal Python changes. # Only applies to Python 3.8. The field that is deprecated in Python 3.8 # and causes the warnings to appear will be removed in Python 3.9 so no # further warnings should be issued. -if localenv['HAS_CLANG'] and parse_version(py_version) == parse_version('3.8'): +if localenv["HAS_CLANG"] and py_version_short == parse_version("3.8"): localenv.Append(CXXFLAGS='-Wno-deprecated-declarations') if localenv['OS'] == 'Darwin': @@ -57,12 +75,12 @@ if localenv['OS'] == 'Darwin': elif localenv['OS'] == 'Windows': localenv.Append(LIBPATH=prefix+'/libs') if localenv['toolchain'] == 'mingw': - localenv.Append(LIBS='python{}'.format(py_version.replace('.',''))) + localenv.Append(LIBS=f"python{info['py_version_nodot']}") if localenv['OS_BITS'] == 64: localenv.Append(CPPDEFINES='MS_WIN64') - # fix for https://bugs.python.org/issue11566. Fixed in 3.7.3 and higher. + # Fix for https://bugs.python.org/issue11566. Fixed in 3.7.3 and higher. # See https://github.com/python/cpython/pull/11283 - if parse_version(py_version) < parse_version("3.7.3"): + if py_version_full < parse_version("3.7.3"): localenv.Append(CPPDEFINES={"_hypot": "hypot"}) elif localenv['OS'] == 'Cygwin': # extract 'pythonX.Y' from 'libpythonX.Y.dll.a' diff --git a/interfaces/cython/setup.py b/interfaces/cython/setup.py index a930748fe1..667310b861 100644 --- a/interfaces/cython/setup.py +++ b/interfaces/cython/setup.py @@ -2,6 +2,4 @@ extension = Extension("cantera._cantera", sources=[]) -setup( - ext_modules=[extension], -) +setup(ext_modules=[extension])