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

Add pypi package #1075

Merged
merged 11 commits into from
Nov 26, 2021
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 21 additions & 8 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -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'):
Expand All @@ -106,8 +107,12 @@ 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/python_minimal/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.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")
Expand Down Expand Up @@ -239,6 +244,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
Expand Down Expand Up @@ -817,6 +826,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:
Expand Down Expand Up @@ -1349,7 +1360,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
Expand Down Expand Up @@ -1868,6 +1879,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']
Expand Down Expand Up @@ -1896,12 +1911,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.")
Copy link
Member

Choose a reason for hiding this comment

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

Should this be issued by logger? (Although the same applies to the surrounding lines, so replacing things just here would be odd.)

Copy link
Member Author

@bryanwweber bryanwweber Nov 22, 2021

Choose a reason for hiding this comment

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

Yes, ideally it should. I just couldn't help fixing this typo 🤪 I'm still hoping to eventually be able to refactor this file to use those features I added in buildutils.py. I think the changes from #1137 are a great step in the right direction too!

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)
Expand Down
90 changes: 59 additions & 31 deletions ext/SConscript
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
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()
Expand Down Expand Up @@ -42,40 +44,54 @@ 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'))
license_files["fmtlib"] = File("#ext/fmt/LICENSE.rst")
localenv = prep_default(env)
localenv.Prepend(CPPPATH=Dir('#ext/fmt/include'))
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)
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 = {}
if env['OS'] != 'Windows':
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',
'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"))
)
bryanwweber marked this conversation as resolved.
Show resolved Hide resolved

# Compile Sundials source files. Skip files related to the Sundials Fortran
# interface, which start with 'fsun'.
Expand All @@ -93,14 +109,18 @@ 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'):
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'):
Expand All @@ -109,10 +129,11 @@ 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)
ext_copies.extend(h)

# Google Test: Used internally for Cantera unit tests.
if env['googletest'] == 'submodule':
Expand All @@ -123,28 +144,35 @@ if env['googletest'] == 'submodule':
gmock = build(localenv.Library('../lib/gmock',
source=['googletest/googlemock/src/gmock-all.cc']))

# Create license file containing licenses for Cantera and all included packages
env["ext_include_copies_target"] = build(ext_copies)


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))
1 change: 1 addition & 0 deletions include/cantera/base/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
8 changes: 3 additions & 5 deletions interfaces/cython/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
59 changes: 41 additions & 18 deletions interfaces/cython/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -32,36 +33,55 @@ 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 *",
"import numpy",
"print(get_config_var('EXT_SUFFIX') or get_config_var('SO'))",
"print(get_config_var('INCLUDEPY'))",
"print(get_config_var('LDLIBRARY'))",
"print(get_config_var('prefix'))",
"print(get_python_version())",
"print(numpy.get_include())"))
info = 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':
localenv.Append(LINKFLAGS='-undefined dynamic_lookup')
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 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 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'
localenv.Append(LIBS=pylib[3:-6])
Expand All @@ -73,16 +93,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') +
Expand Down
2 changes: 1 addition & 1 deletion interfaces/cython/cantera/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Loading