From 529d6dabe8eaf33d93656275af5fd1a296c8957a Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 30 Oct 2021 14:24:37 -0500 Subject: [PATCH 01/11] [SCons] Update/fix SConstruct formatting --- SConstruct | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/SConstruct b/SConstruct index 6907cadd46..6aec28b3a0 100644 --- a/SConstruct +++ b/SConstruct @@ -156,11 +156,12 @@ if os.name == 'nt': 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.""", - ''), + ( + "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 @@ -356,28 +357,29 @@ config_options = [ 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'""", + 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 the user didn't set the 'libdirname' configuration + variable, set it to the default value 'lib'""", 'lib', PathVariable.PathAccept), EnumVariable( 'python_package', - """If you plan to work in Python, then you need the ``full`` Cantera Python + """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 + to process YAML 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``.""", + 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 + 'PYTHON_CMD' is not set, then the configuration process will use the same Python interpreter being used by SCons.""", sys.executable, PathVariable.PathAccept), PathVariable( @@ -418,9 +420,10 @@ config_options = [ 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'), + ( + "FORTRANFLAGS", + """Compilation options for the Fortran (90) compiler.""", + "-O3"), BoolVariable( 'coverage', """Enable collection of code coverage information with gcov. @@ -506,13 +509,13 @@ config_options = [ '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``.""", + '/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').""", + the library file (for example, "nm libblas.a").""", 'lower', ('lower','upper')), BoolVariable( 'lapack_ftn_trailing_underscore', @@ -535,7 +538,7 @@ config_options = [ ( 'env_vars', """Environment variables to propagate through to SCons. Either the - string "all" or a comma separated list of variable names, for example, + string 'all' or a comma separated list of variable names, for example, 'LD_LIBRARY_PATH,HOME'.""", defaults.env_vars), BoolVariable( @@ -655,7 +658,7 @@ config_options = [ """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 + 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.""", From 4331ecd50dee70e81feaf31f771a1cf9ecf0f35b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 30 Oct 2021 13:24:25 -0500 Subject: [PATCH 02/11] [SCons] Implement script to extract options from SConstruct --- site_scons/scons2rst.py | 263 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 site_scons/scons2rst.py diff --git a/site_scons/scons2rst.py b/site_scons/scons2rst.py new file mode 100644 index 0000000000..af72435f98 --- /dev/null +++ b/site_scons/scons2rst.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 +# encoding: utf-8 + +# This file is part of Cantera. See License.txt in the top-level directory or +# at https://cantera.org/license.txt for license and copyright information. + +""" +scons2rst.py: Extract SCons configuration options from SConstruct +""" + +import ast +import re +import argparse +import pathlib + + +def deblank(string): + """ + Remove whitespace before and after line breaks + """ + out = [s.strip() for s in string.split("\n")] + if not len(out[-1]): + out = out[:-1] + return "\n".join(out) + + +def convert(item): + """ + Convert AST objects to human-readable strings (or list of strings) + """ + if isinstance(item, list): + return [convert(sub) for sub in item] + + if isinstance(item, (ast.Tuple, ast.List)): + return [convert(sub) for sub in item.elts] + + if isinstance(item, ast.Str): + return deblank(item.s) + + if isinstance(item, (ast.NameConstant)): + if isinstance(item.value, bool): + return "True" if item.value else "False" + raise ValueError(f"Unable to process constant '{item}'") + + if isinstance(item, ast.Attribute): + if isinstance(item.value, ast.Name): + return f"{item.value.id}.{item.attr}" + raise ValueError(f"Unable to process attribute '{item}'") + + if isinstance(item, ast.Subscript): + if isinstance(item.value, ast.Name) and isinstance(item.slice.value, ast.Str): + return f"{item.value.id}[{item.slice.value.s}]" + raise ValueError(f"Unable to process subscript '{item}'") + + if isinstance(item, ast.Name): + return item.id + + raise ValueError(f"Unable to process item of type '{type(item)}'") + + +class Option: + """ + Object corresponding to SCons configuration option + """ + + def __init__(self, item): + if isinstance(item, (ast.Tuple, ast.List)): + self.func = None + self._assign(convert(item)) + return + + if isinstance(item, ast.Call): + self.func = item.func.id + self._assign(convert(item.args)) + return + + raise ValueError(f"Unable to process item of type '{type(item)}'") + + def _assign(self, args): + self.name = args[0] + self.description = args[1] + self.default = args[2] + self.choices = args[3] if len(args) > 3 else None + + @property + def args(self): + """ + Arguments of the SCons configuration option + """ + if self.choices is None: + return self.name, self.description, self.default + return self.name, self.description, self.default, self.choices + + def to_rest(self, dev=False, indent=3): + """ + Convert option to restructured text + """ + tag = self.name.replace("_", "-").lower() + if dev: + tag += "-dev" + + if self.func == "PathVariable": + choices = f"``path/to/{self.name}``" + default = self.default + elif isinstance(self.choices, list): + choices = self.choices + for yes_no in ["n", "y"]: + if yes_no in choices: + # ensure correct order + choices.remove(yes_no) + choices = [yes_no] + choices + choices = " | ".join([f"``{c}``" for c in choices]) + default = self.default + elif self.default in ["True", "False"]: + choices = "``yes`` | ``no``" + default = "yes" if self.default == "True" else "no" + elif self.func == "BoolVariable": + choices = "``yes`` | ``no``" + default = self.default + else: + choices = "``string``" + default = self.default + + if default.startswith("defaults.") or default.startswith("sys."): + default = "''" + else: + default = f"'{default}'" + + # assemble description + tab = " " * indent + linebreak = "\n" + tab + description = linebreak.join(self.description.split("\n")) + pat = r'"([a-zA-Z0-9\-\+_.,: =/\'\\]+)"' + double_quoted = [] + for item in re.findall(pat, description): + # enclose double-quoted strings in '``' + found = f'"{item}"' + double_quoted += [found] + replacement = f"``{found}``" + description = description.replace(found, replacement) + pat = r"\'([a-zA-Z0-9\-\+_.,:=/\\]+)\'" + for item in re.findall(pat, description): + # replace "'" for single-quoted words by '``'; do not replace "'" when + # whitespace is enclosed or if word is part of double-quoted string + if any([item in dq for dq in double_quoted]): + continue + found = f"'{item}'" + replacement = found.replace("'", "``") + description = description.replace(found, replacement) + pat = r"\*([a-zA-Z0-9\-\+_., :=/\'\\]+)" + asterisks = re.findall(pat, description) + if len(asterisks) == 1: + # catch unbalanced '*', for example in '*nix' + found = f"*{asterisks[0]}" + replacement = f"\{found}" + description = description.replace(found, replacement) + + # assemble output + out = f".. _{tag}:\n\n" + out += f"* ``{self.name}``: [ {choices} ]\n" + out += f"{tab}{description}\n\n" + out += f"{tab}- default: ``{default}``\n" + + return out + + def __repr__(self): + if self.func is None: + return f"{self.args}" + return f"{self.func}{self.args}" + + +def parse(input_file, output_file, dev): + """Parse SConstruct and extract configuration""" + + with open(input_file, "r") as fid: + code = fid.read() + + tree = ast.parse(code) + + names = ["windows_compiler_options", "compiler_options", "config_options"] + options = {name: [] for name in names} + + for node in ast.walk(tree): + + if isinstance(node, ast.Assign) and isinstance(node.targets[0], ast.Name) \ + and len(node.targets) == 1: + # option is assigned as list + name = node.targets[0].id + if name not in names: + continue + + if isinstance(node.value, ast.List): + for item in node.value.elts: + options[name].append(Option(item)) + continue + + if isinstance(node.value, ast.Call): + continue + + raise ValueError(f"Unable to process node {node.value} (name='{name}')") + + elif isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) \ + and isinstance(node.func.value, ast.Name): + # option is appended to list via 'append' or 'extend' + if node.func.attr not in ["append", "extend"]: + continue + + name = node.func.value.id + if name not in names: + continue + + for arg in node.args: + if isinstance(arg, ast.List): + for item in arg.elts: + options[name].append(Option(item)) + continue + + if isinstance(arg, ast.Call): + options[name].append(Option(arg)) + continue + + raise ValueError(f"Unable to process node {node}") + + with open(output_file, "w+") as fid: + for config in names: + for option in options[config]: + fid.write(option.to_rest(dev=dev)) + fid.write("\n") + + print(f"Done writing output to '{output_file}'.") + +def main(): + parser = argparse.ArgumentParser( + description="Extract configuration options from SConstruct", + ) + parser.add_argument( + "input", + nargs="?", + help="The input SConstruct file. Optional.", + default="SConstruct") + parser.add_argument( + "output", + nargs="?", + help="The output ReST filename. Optional.", + default="config-options") + parser.add_argument( + "--dev", + action="store_true", + default=False, + help="Append '-dev' to filename and Sphinx references.") + + args = parser.parse_args() + input_file = pathlib.Path(args.input) + output_file = args.output + if args.dev: + output_file += "-dev" + output_file = pathlib.Path(output_file).with_suffix(".rst") + + parse(input_file, output_file, args.dev) + + +if __name__ == "__main__": + main() From e164ec523517f58a091519a1ce563ac28dcb143d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 30 Oct 2021 14:55:57 -0500 Subject: [PATCH 03/11] [SCons] Refactor default option selection in SConstruct --- SConstruct | 176 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 103 insertions(+), 73 deletions(-) diff --git a/SConstruct b/SConstruct index 6aec28b3a0..ddaa4a13ed 100644 --- a/SConstruct +++ b/SConstruct @@ -141,32 +141,61 @@ opts = Variables('cantera.conf') windows_compiler_options = [] extraEnvArgs = {} + +class Defaults: + """Class enabling selection of options based on attribute dictionary entries""" + + def apply(self, key="defaults"): + """Select attribute dictionary entries corresponding to *key*""" + for attr, val in self.__dict__.items(): + + if isinstance(val, dict) and key in val: + self.__dict__[attr] = val[key] + + +def substitute(entries, env=None): + """Function substituting environment variables in configuration""" + out = [] + for item in entries: + if env is not None and isinstance(item, tuple) and \ + not isinstance(item[2], bool) and "$" in item[2]: + subst = list(item[:2]) + [env.subst(item[2])] + list(item[3:]) + out.append(tuple(subst)) + else: + out.append(item) + + return out + + +defaults = Defaults() + +defaults.toolchain = {"Windows": "msvc"} +defaults.target_arch = {"Windows": "amd64"} + if os.name == 'nt': + defaults.apply("Windows") + # 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' + if '64 bit' not in sys.version: + defaults.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' + defaults.toolchain = "mingw" 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.""", + for Visual Studio 2015. Windows MSVC only.""", ""), EnumVariable( - 'target_arch', + "target_arch", """Target architecture. The default is the same architecture as the - installed version of Python.""", - target_arch, ('amd64', 'x86')) + installed version of Python. Windows only.""", + defaults.target_arch, ("amd64", "x86")) ]) opts.AddVariables(*windows_compiler_options) @@ -174,12 +203,14 @@ if os.name == 'nt': opts.Update(pickCompilerEnv) if pickCompilerEnv['msvc_version']: - defaultToolchain = 'msvc' + defaults.toolchain = "msvc" - windows_compiler_options.append(EnumVariable( - 'toolchain', - """The preferred compiler toolchain.""", - defaultToolchain, ('msvc', 'mingw', 'intel'))) + windows_compiler_options.append( + EnumVariable( + "toolchain", + """The preferred compiler toolchain. If MSVC is not on the path but + 'g++' is on he path, 'mingw' is used as a backup. Windows only.""", + defaults.toolchain, ("msvc", "mingw", "intel"))) opts.AddVariables(windows_compiler_options[-1]) opts.Update(pickCompilerEnv) @@ -238,41 +269,61 @@ if 'FRAMEWORKS' not in env: add_RegressionTest(env) -class defaults: pass +defaults.prefix = {"Windows": "$ProgramFiles\Cantera", "default": "/usr/local"} +defaults.boostIncDir = "" 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) +defaults.CXX = "${CXX}" +defaults.CC = "${CC}" + compiler_options = [ ('CXX', """The C++ compiler to use.""", - env['CXX']), + defaults.CXX), ('CC', """The C compiler to use. This is only used to compile CVODE.""", - env['CC'])] + defaults.CC)] + +compiler_options = substitute(compiler_options, env=env) 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' +defaults.cxxFlags = {"cl": "/EHsc", "Cygwin": "-std=gnu++11", "default": "-std=c++11"} +defaults.ccFlags = { + "cl": "/MD /nologo /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS", + "icc": "-vec-report0 -diag-disable 1478", + "clang": "-fcolor-diagnostics", + "default": "", +} +defaults.noOptimizeCcFlags = {"cl": "/Od /Ob0", "default": "-O0"} +defaults.optimizeCcFlags = { + "cl": "/O2", + "gcc": "-O3 -Wno-inline", + "default": "-O3", +} +defaults.debugCcFlags = {"cl": "/Zi /Fd${TARGET}.pdb", "default": "-g"} +defaults.noDebugCcFlags = "" +defaults.debugLinkFlags = {"cl": "/DEBUG", "default": ""} +defaults.noDebugLinkFlags = "" +defaults.warningFlags = {"cl": "/W3", "icc": "-Wcheck", "default": "-Wall"} +defaults.buildPch = {"icc": False, "default": True} + +defaults.threadFlags = {"Windows": "", "macOS": "", "default": "-pthread"} +defaults.fsLayout = {"Windows": "compact", "default": "standard"} +defaults.python_prefix = {"Windows": "", "default": "$prefix"} +defaults.python_cmd = "${PYTHON_CMD}" +defaults.env_vars = "PATH,LD_LIBRARY_PATH,PYTHONPATH" +defaults.versionedSharedLibrary = {"mingw": False, "default": True} +defaults.sphinx_options = "-W --keep-going" + env['pch_flags'] = [] env['openmp_flag'] = ['-fopenmp'] # used to generate sample build scripts @@ -285,65 +336,41 @@ if env['OS'] == 'Darwin': env['openmp_flag'].insert(0, '-Xpreprocessor') if 'gcc' in env.subst('$CC') or 'gnu-cc' in env.subst('$CC'): - defaults.optimizeCcFlags += ' -Wno-inline' + defaults.apply("gcc") if env['OS'] == 'Cygwin': # See http://stackoverflow.com/questions/18784112 - defaults.cxxFlags = '-std=gnu++11' - else: - defaults.cxxFlags = '-std=c++11' - defaults.buildPch = True + defaults.apply("Cygwin") 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 + defaults.apply("cl") env['pch_flags'] = ['/FIpch/system.h'] env['openmp_flag'] = ['/openmp'] elif 'icc' in env.subst('$CC'): - defaults.cxxFlags = '-std=c++11' - defaults.ccFlags = '-vec-report0 -diag-disable 1478' - defaults.warningFlags = '-Wcheck' + defaults.apply("icc") env['openmp_flag'] = ['-openmp'] elif 'clang' in env.subst('$CC'): - defaults.ccFlags = '-fcolor-diagnostics' - defaults.cxxFlags = '-std=c++11' - defaults.buildPch = True + defaults.apply("clang") 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' +if env['OS'] == 'Windows': + defaults.apply("Windows") +elif env["OS"] == "Darwin": + defaults.apply("macOS") # 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.apply("mingw") -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)) +defaults.apply("default") +defaults.python_cmd = sys.executable # ************************************** # *** Read user-configurable options *** @@ -351,8 +378,9 @@ for key,value in defaults.__dict__.items(): config_options = [ PathVariable( - 'prefix', - 'Set this to the directory where Cantera should be installed.', + "prefix", + """Set this to the directory where Cantera should be installed. On Windows + systems, '$ProgramFiles' typically refers to "C:\Program Files".""", defaults.prefix, PathVariable.PathAccept), PathVariable( 'libdirname', @@ -381,7 +409,7 @@ config_options = [ """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), + defaults.python_cmd, PathVariable.PathAccept), PathVariable( 'python_prefix', """Use this option if you want to install the Cantera Python package to @@ -404,7 +432,7 @@ config_options = [ """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 + "/Applications/MATLAB_R2011a.app" on macOS, or "/opt/MATLAB/R2011a" on Linux.""", '', PathVariable.PathAccept), EnumVariable( @@ -693,6 +721,8 @@ config_options = [ True), ] +config_options = substitute(config_options, env=env) + opts.AddVariables(*config_options) opts.Update(env) opts.Save('cantera.conf', env) From 7f1c2a311ded9fb6f83e278079fd0d16f614b851 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 30 Oct 2021 21:39:48 -0500 Subject: [PATCH 04/11] [SCons] Capture default options in scons2rst.py --- site_scons/scons2rst.py | 125 ++++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 19 deletions(-) diff --git a/site_scons/scons2rst.py b/site_scons/scons2rst.py index af72435f98..fed1fee0e5 100644 --- a/site_scons/scons2rst.py +++ b/site_scons/scons2rst.py @@ -21,7 +21,9 @@ def deblank(string): out = [s.strip() for s in string.split("\n")] if not len(out[-1]): out = out[:-1] - return "\n".join(out) + out = "\n".join(out) + # out = out.replace("\", "\").replace("$", "\$") + return out def convert(item): @@ -91,7 +93,7 @@ def args(self): return self.name, self.description, self.default return self.name, self.description, self.default, self.choices - def to_rest(self, dev=False, indent=3): + def to_rest(self, defaults=None, dev=False, indent=3): """ Convert option to restructured text """ @@ -109,28 +111,43 @@ def to_rest(self, dev=False, indent=3): # ensure correct order choices.remove(yes_no) choices = [yes_no] + choices - choices = " | ".join([f"``{c}``" for c in choices]) + choices = " | ".join([f"``'{c}'``" for c in choices]) default = self.default elif self.default in ["True", "False"]: - choices = "``yes`` | ``no``" + choices = "``'yes'`` | ``'no'``" default = "yes" if self.default == "True" else "no" elif self.func == "BoolVariable": - choices = "``yes`` | ``no``" + choices = "``'yes'`` | ``'no'``" default = self.default else: choices = "``string``" default = self.default - if default.startswith("defaults.") or default.startswith("sys."): - default = "''" + if default.startswith("defaults."): + if defaults is None: + default = "" + else: + attr = default.split('.')[1] + if attr in defaults: + default = defaults[attr] + else: + default = "" + elif default.startswith("sys."): + default = "" else: - default = f"'{default}'" + default = f"{default}" - # assemble description + # formatting shortcuts tab = " " * indent + bullet = "*" + bullet = f"{bullet:<{indent}}" + dash = "-" + dash = f"{dash:<{indent}}" + + # assemble description linebreak = "\n" + tab description = linebreak.join(self.description.split("\n")) - pat = r'"([a-zA-Z0-9\-\+_.,: =/\'\\]+)"' + pat = r'"([a-zA-Z0-9\-\+\*$_.,: =/\'\\]+)"' double_quoted = [] for item in re.findall(pat, description): # enclose double-quoted strings in '``' @@ -138,7 +155,7 @@ def to_rest(self, dev=False, indent=3): double_quoted += [found] replacement = f"``{found}``" description = description.replace(found, replacement) - pat = r"\'([a-zA-Z0-9\-\+_.,:=/\\]+)\'" + pat = r"\'([a-zA-Z0-9\-\+\*$_.,:=/\\]+)\'" for item in re.findall(pat, description): # replace "'" for single-quoted words by '``'; do not replace "'" when # whitespace is enclosed or if word is part of double-quoted string @@ -147,7 +164,7 @@ def to_rest(self, dev=False, indent=3): found = f"'{item}'" replacement = found.replace("'", "``") description = description.replace(found, replacement) - pat = r"\*([a-zA-Z0-9\-\+_., :=/\'\\]+)" + pat = r"\*([^\*]+)" asterisks = re.findall(pat, description) if len(asterisks) == 1: # catch unbalanced '*', for example in '*nix' @@ -157,19 +174,48 @@ def to_rest(self, dev=False, indent=3): # assemble output out = f".. _{tag}:\n\n" - out += f"* ``{self.name}``: [ {choices} ]\n" + out += f"{bullet}``{self.name}``: [ {choices} ]\n" out += f"{tab}{description}\n\n" - out += f"{tab}- default: ``{default}``\n" + + # add information on default values + if isinstance(default, dict): + comment, defaults = Option.parse_defaults(default) + out += f"{tab}{dash}default: {comment}\n\n" + for key, value in defaults.items(): + if key == "default": + out += f"{tab}{tab}{dash}Otherwise: ``'{value}'``\n" + else: + out += f"{tab}{tab}{dash}{key}: ``'{value}'``\n" + else: + out += f"{tab}{dash}default: ``'{default}'``\n" return out + @staticmethod + def parse_defaults(defaults): + # check if this is a compiler option + compilers = {"cl": "MSVC", "gcc": "GCC", "icc": "ICC", "clang": "Clang"} + if not any([d in compilers for d in defaults]): + return "platform dependent", defaults + + out = {} + for key, value in defaults.items(): + if key in compilers: + key = f"If using {compilers[key]}" + elif key != "default": + key = f"If using {key}" + if isinstance(value, bool): + value = "yes" if value else "no" + out[key] = value + return "compiler dependent", out + def __repr__(self): if self.func is None: return f"{self.args}" return f"{self.func}{self.args}" -def parse(input_file, output_file, dev): +def parse(input_file, output_file, dev, no_defaults): """Parse SConstruct and extract configuration""" with open(input_file, "r") as fid: @@ -179,12 +225,14 @@ def parse(input_file, output_file, dev): names = ["windows_compiler_options", "compiler_options", "config_options"] options = {name: [] for name in names} + defaults = {} for node in ast.walk(tree): if isinstance(node, ast.Assign) and isinstance(node.targets[0], ast.Name) \ and len(node.targets) == 1: - # option is assigned as list + + # configuration option is assigned as list name = node.targets[0].id if name not in names: continue @@ -199,9 +247,39 @@ def parse(input_file, output_file, dev): raise ValueError(f"Unable to process node {node.value} (name='{name}')") + elif isinstance(node, ast.Assign) \ + and isinstance(node.targets[0], ast.Attribute) \ + and len(node.targets) == 1: + + # retrieve 'defaults' used for platform-dependent configurations + name = node.targets[0].value.id + if name == "defaults": + attr = node.targets[0].attr + + if attr in defaults: + # do not overwrite values + continue + + if isinstance(node.value, ast.Str): + defaults[attr] = node.value.s + continue + + if isinstance(node.value, ast.Dict): + items = {} + for k, v in zip(node.value.keys, node.value.values): + items[k.value] = v.value + defaults[attr] = items + continue + + if isinstance(node.value, ast.Call): + continue + + raise ValueError(f"Unable to process node {node.value} (name='{name}')") + elif isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) \ and isinstance(node.func.value, ast.Name): - # option is appended to list via 'append' or 'extend' + + # configuration option is appended to list via 'append' or 'extend' if node.func.attr not in ["append", "extend"]: continue @@ -221,14 +299,18 @@ def parse(input_file, output_file, dev): raise ValueError(f"Unable to process node {node}") + if no_defaults: + defaults = None + with open(output_file, "w+") as fid: for config in names: for option in options[config]: - fid.write(option.to_rest(dev=dev)) + fid.write(option.to_rest(defaults=defaults, dev=dev)) fid.write("\n") print(f"Done writing output to '{output_file}'.") + def main(): parser = argparse.ArgumentParser( description="Extract configuration options from SConstruct", @@ -248,6 +330,11 @@ def main(): action="store_true", default=False, help="Append '-dev' to filename and Sphinx references.") + parser.add_argument( + "--no-defaults", + action="store_true", + default=False, + help="Do not apply default values.") args = parser.parse_args() input_file = pathlib.Path(args.input) @@ -256,7 +343,7 @@ def main(): output_file += "-dev" output_file = pathlib.Path(output_file).with_suffix(".rst") - parse(input_file, output_file, args.dev) + parse(input_file, output_file, args.dev, args.no_defaults) if __name__ == "__main__": From 7d57d1a8c4c828a245da78c5b07294aad354d612 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 30 Oct 2021 22:30:16 -0500 Subject: [PATCH 05/11] [CI] Parse SConstruct options --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 073515ad72..d79ae9dbe0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -158,6 +158,11 @@ jobs: 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 + - name: Capture configuration options from SConstruct + run: | + python3 site_scons/scons2rst.py --dev SConstruct config-options + mkdir build/docs/scons + mv config-options-dev.rst build/docs/scons/ # The known_hosts key is generated with `ssh-keygen -F cantera.org` from a # machine that has previously logged in to cantera.org and trusts # that it logged in to the right machine From 91e0db88f54f474fae361bbbf87373a18bb05fc7 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 30 Oct 2021 23:55:12 -0500 Subject: [PATCH 06/11] [CI] Store artifact from docs build --- .github/workflows/main.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d79ae9dbe0..a31c3f2380 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -163,6 +163,15 @@ jobs: python3 site_scons/scons2rst.py --dev SConstruct config-options mkdir build/docs/scons mv config-options-dev.rst build/docs/scons/ + - name: Create archive for docs output + run: | + cd build + tar -czf docs.tar.gz docs + - name: Store archive of docs output + uses: actions/upload-artifact@v2 + with: + path: build/docs.tar.gz + retention-days: 2 # The known_hosts key is generated with `ssh-keygen -F cantera.org` from a # machine that has previously logged in to cantera.org and trusts # that it logged in to the right machine From 42a1cbf914103d0fd14c3243a50e7e65299a8d8f Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Nov 2021 13:46:49 -0500 Subject: [PATCH 07/11] [SCons] Switch CamelCase to snake_case for defaults --- SConstruct | 104 +++++++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/SConstruct b/SConstruct index ddaa4a13ed..74fbdff1f2 100644 --- a/SConstruct +++ b/SConstruct @@ -143,9 +143,16 @@ extraEnvArgs = {} class Defaults: - """Class enabling selection of options based on attribute dictionary entries""" + """ + Class enabling selection of options based on attribute dictionary entries that + allof for differentiation between platform/compiler dependent options. - def apply(self, key="defaults"): + Individual member variables are assigned explicitly after object instantiation, and + should contain strings specifying platform/compiler independent default options, or + dictionaries with platform/compiler dependent options. + """ + + def select(self, key="default"): """Select attribute dictionary entries corresponding to *key*""" for attr, val in self.__dict__.items(): @@ -173,11 +180,11 @@ defaults.toolchain = {"Windows": "msvc"} defaults.target_arch = {"Windows": "amd64"} if os.name == 'nt': - defaults.apply("Windows") + defaults.select("Windows") # On Windows, target the same architecture as the current copy of Python, # unless the user specified another option. - if '64 bit' not in sys.version: + if "64 bit" not in sys.version: defaults.target_arch = "x86" # Make an educated guess about the right default compiler @@ -270,7 +277,7 @@ if 'FRAMEWORKS' not in env: add_RegressionTest(env) defaults.prefix = {"Windows": "$ProgramFiles\Cantera", "default": "/usr/local"} -defaults.boostIncDir = "" +defaults.boost_inc_dir = "" if os.name == 'posix': env['INSTALL_MANPAGES'] = True @@ -281,47 +288,51 @@ else: print("Error: Unrecognized operating system '%s'" % os.name) sys.exit(1) -defaults.CXX = "${CXX}" -defaults.CC = "${CC}" +defaults.cxx = "${CXX}" +defaults.cc = "${CC}" compiler_options = [ ('CXX', """The C++ compiler to use.""", - defaults.CXX), + defaults.cxx), ('CC', """The C compiler to use. This is only used to compile CVODE.""", - defaults.CC)] + defaults.cc)] compiler_options = substitute(compiler_options, env=env) opts.AddVariables(*compiler_options) opts.Update(env) -defaults.cxxFlags = {"cl": "/EHsc", "Cygwin": "-std=gnu++11", "default": "-std=c++11"} -defaults.ccFlags = { +defaults.cxx_flags = { + "cl": "/EHsc", + "Cygwin": "-std=gnu++11", # See http://stackoverflow.com/questions/18784112 + "default": "-std=c++11" +} +defaults.cc_flags = { "cl": "/MD /nologo /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS", "icc": "-vec-report0 -diag-disable 1478", "clang": "-fcolor-diagnostics", "default": "", } -defaults.noOptimizeCcFlags = {"cl": "/Od /Ob0", "default": "-O0"} -defaults.optimizeCcFlags = { +defaults.no_optimize_cc_flags = {"cl": "/Od /Ob0", "default": "-O0"} +defaults.optimize_cc_flags = { "cl": "/O2", "gcc": "-O3 -Wno-inline", "default": "-O3", } -defaults.debugCcFlags = {"cl": "/Zi /Fd${TARGET}.pdb", "default": "-g"} -defaults.noDebugCcFlags = "" -defaults.debugLinkFlags = {"cl": "/DEBUG", "default": ""} -defaults.noDebugLinkFlags = "" -defaults.warningFlags = {"cl": "/W3", "icc": "-Wcheck", "default": "-Wall"} -defaults.buildPch = {"icc": False, "default": True} - -defaults.threadFlags = {"Windows": "", "macOS": "", "default": "-pthread"} -defaults.fsLayout = {"Windows": "compact", "default": "standard"} +defaults.debug_cc_flags = {"cl": "/Zi /Fd${TARGET}.pdb", "default": "-g"} +defaults.no_debug_cc_flags = "" +defaults.debug_link_flags = {"cl": "/DEBUG", "default": ""} +defaults.no_debug_link_flags = "" +defaults.warning_flags = {"cl": "/W3", "icc": "-Wcheck", "default": "-Wall"} +defaults.build_pch = {"icc": False, "default": True} + +defaults.thread_flags = {"Windows": "", "macOS": "", "default": "-pthread"} +defaults.fs_layout = {"Windows": "compact", "default": "standard"} defaults.python_prefix = {"Windows": "", "default": "$prefix"} defaults.python_cmd = "${PYTHON_CMD}" defaults.env_vars = "PATH,LD_LIBRARY_PATH,PYTHONPATH" -defaults.versionedSharedLibrary = {"mingw": False, "default": True} +defaults.versioned_shared_library = {"mingw": False, "default": True} defaults.sphinx_options = "-W --keep-going" env['pch_flags'] = [] @@ -336,40 +347,39 @@ if env['OS'] == 'Darwin': env['openmp_flag'].insert(0, '-Xpreprocessor') if 'gcc' in env.subst('$CC') or 'gnu-cc' in env.subst('$CC'): - defaults.apply("gcc") if env['OS'] == 'Cygwin': - # See http://stackoverflow.com/questions/18784112 - defaults.apply("Cygwin") + defaults.select("Cygwin") + defaults.select("gcc") env['pch_flags'] = ['-include', 'src/pch/system.h'] elif env['CC'] == 'cl': # Visual Studio - defaults.apply("cl") + defaults.select("cl") env['pch_flags'] = ['/FIpch/system.h'] env['openmp_flag'] = ['/openmp'] elif 'icc' in env.subst('$CC'): - defaults.apply("icc") + defaults.select("icc") env['openmp_flag'] = ['-openmp'] elif 'clang' in env.subst('$CC'): - defaults.apply("clang") + defaults.select("clang") env['pch_flags'] = ['-include-pch', 'src/pch/system.h.gch'] else: print("WARNING: Unrecognized C compiler '%s'" % env['CC']) if env['OS'] == 'Windows': - defaults.apply("Windows") + defaults.select("Windows") elif env["OS"] == "Darwin": - defaults.apply("macOS") + defaults.select("macOS") # 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.apply("mingw") + defaults.select("mingw") -defaults.apply("default") +defaults.select("default") defaults.python_cmd = sys.executable # ************************************** @@ -572,21 +582,21 @@ config_options = [ BoolVariable( 'use_pch', """Use a precompiled-header to speed up compilation""", - defaults.buildPch), + defaults.build_pch), ( '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), + defaults.cxx_flags), ( 'cc_flags', """Compiler flags passed to both the C and C++ compilers, regardless of optimization level.""", - defaults.ccFlags), + defaults.cc_flags), ( 'thread_flags', """Compiler and linker flags for POSIX multithreading support.""", - defaults.threadFlags), + defaults.thread_flags), BoolVariable( 'optimize', """Enable extra compiler optimizations specified by the @@ -596,11 +606,11 @@ config_options = [ ( 'optimize_flags', """Additional compiler flags passed to the C/C++ compiler when 'optimize=yes'.""", - defaults.optimizeCcFlags), + defaults.optimize_cc_flags), ( 'no_optimize_flags', """Additional compiler flags passed to the C/C++ compiler when 'optimize=no'.""", - defaults.noOptimizeCcFlags), + defaults.no_optimize_cc_flags), BoolVariable( 'debug', """Enable compiler debugging symbols.""", @@ -608,25 +618,25 @@ config_options = [ ( 'debug_flags', """Additional compiler flags passed to the C/C++ compiler when 'debug=yes'.""", - defaults.debugCcFlags), + defaults.debug_cc_flags), ( 'no_debug_flags', """Additional compiler flags passed to the C/C++ compiler when 'debug=no'.""", - defaults.noDebugCcFlags), + defaults.no_debug_cc_flags), ( 'debug_linker_flags', """Additional options passed to the linker when 'debug=yes'.""", - defaults.debugLinkFlags), + defaults.debug_link_flags), ( 'no_debug_linker_flags', """Additional options passed to the linker when 'debug=no'.""", - defaults.noDebugLinkFlags), + defaults.no_debug_link_flags), ( '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), + defaults.warning_flags), ( 'extra_inc_dirs', """Additional directories to search for header files, with multiple @@ -641,7 +651,7 @@ config_options = [ '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), + defaults.boost_inc_dir, PathVariable.PathAccept), PathVariable( 'stage_dir', """Directory relative to the Cantera source directory to be @@ -675,7 +685,7 @@ config_options = [ actual library and 'libcantera_shared.so' and 'libcantera_shared.so.2' as symlinks. """, - defaults.versionedSharedLibrary), + defaults.versioned_shared_library), BoolVariable( 'use_rpath_linkage', """If enabled, link to all shared libraries using 'rpath', i.e., a fixed @@ -690,7 +700,7 @@ config_options = [ 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')), + defaults.fs_layout, ('standard','compact','debian')), BoolVariable( "fast_fail_tests", """If enabled, tests will exit at the first failure.""", From 1d500a0583bca81b3c44078936e0351e3e498efc Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Nov 2021 14:07:29 -0500 Subject: [PATCH 08/11] [SCons] Document pch_flags defaults --- SConstruct | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/SConstruct b/SConstruct index 74fbdff1f2..446f4a0112 100644 --- a/SConstruct +++ b/SConstruct @@ -326,7 +326,12 @@ defaults.debug_link_flags = {"cl": "/DEBUG", "default": ""} defaults.no_debug_link_flags = "" defaults.warning_flags = {"cl": "/W3", "icc": "-Wcheck", "default": "-Wall"} defaults.build_pch = {"icc": False, "default": True} - +defaults.pch_flags = { + "cl": "/FIpch/system.h", + "gcc": "-include src/pch/system.h", + "clang": "-include-pch src/pch/system.h.gch", + "default": "", +} defaults.thread_flags = {"Windows": "", "macOS": "", "default": "-pthread"} defaults.fs_layout = {"Windows": "compact", "default": "standard"} defaults.python_prefix = {"Windows": "", "default": "$prefix"} @@ -335,7 +340,6 @@ defaults.env_vars = "PATH,LD_LIBRARY_PATH,PYTHONPATH" defaults.versioned_shared_library = {"mingw": False, "default": True} defaults.sphinx_options = "-W --keep-going" -env['pch_flags'] = [] env['openmp_flag'] = ['-fopenmp'] # used to generate sample build scripts env['using_apple_clang'] = False @@ -350,11 +354,9 @@ if 'gcc' in env.subst('$CC') or 'gnu-cc' in env.subst('$CC'): if env['OS'] == 'Cygwin': defaults.select("Cygwin") defaults.select("gcc") - env['pch_flags'] = ['-include', 'src/pch/system.h'] elif env['CC'] == 'cl': # Visual Studio defaults.select("cl") - env['pch_flags'] = ['/FIpch/system.h'] env['openmp_flag'] = ['/openmp'] elif 'icc' in env.subst('$CC'): @@ -363,7 +365,6 @@ elif 'icc' in env.subst('$CC'): elif 'clang' in env.subst('$CC'): defaults.select("clang") - env['pch_flags'] = ['-include-pch', 'src/pch/system.h.gch'] else: print("WARNING: Unrecognized C compiler '%s'" % env['CC']) @@ -583,6 +584,10 @@ config_options = [ 'use_pch', """Use a precompiled-header to speed up compilation""", defaults.build_pch), + ( + 'pch_flags', + """Compiler flags when using precompiled-header.""", + defaults.pch_flags), ( 'cxx_flags', """Compiler flags passed to the C++ compiler only. Separate multiple @@ -886,6 +891,7 @@ env['LINKFLAGS'] += listify(env['thread_flags']) env['CPPDEFINES'] = {} env['warning_flags'] = listify(env['warning_flags']) +env["pch_flags"] = listify(env["pch_flags"]) if env['optimize']: env['CCFLAGS'] += listify(env['optimize_flags']) From adf0ba02d63744334e6c2c6361c2cc482480934a Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Nov 2021 14:20:50 -0500 Subject: [PATCH 09/11] [SCons] Document openmp_flag defaults --- SConstruct | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/SConstruct b/SConstruct index 446f4a0112..bffaaa1216 100644 --- a/SConstruct +++ b/SConstruct @@ -333,6 +333,12 @@ defaults.pch_flags = { "default": "", } defaults.thread_flags = {"Windows": "", "macOS": "", "default": "-pthread"} +defaults.openmp_flag = { + "cl": "/openmp", + "icc": "openmp", + "apple-clang": "-Xpreprocessor -fopenmp", + "default": "-fopenmp", +} defaults.fs_layout = {"Windows": "compact", "default": "standard"} defaults.python_prefix = {"Windows": "", "default": "$prefix"} defaults.python_cmd = "${PYTHON_CMD}" @@ -340,15 +346,13 @@ defaults.env_vars = "PATH,LD_LIBRARY_PATH,PYTHONPATH" defaults.versioned_shared_library = {"mingw": False, "default": True} defaults.sphinx_options = "-W --keep-going" -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 +env['using_apple_clang'] = False 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') + defaults.select("apple-clang") if 'gcc' in env.subst('$CC') or 'gnu-cc' in env.subst('$CC'): if env['OS'] == 'Cygwin': @@ -357,11 +361,9 @@ if 'gcc' in env.subst('$CC') or 'gnu-cc' in env.subst('$CC'): elif env['CC'] == 'cl': # Visual Studio defaults.select("cl") - env['openmp_flag'] = ['/openmp'] elif 'icc' in env.subst('$CC'): defaults.select("icc") - env['openmp_flag'] = ['-openmp'] elif 'clang' in env.subst('$CC'): defaults.select("clang") @@ -696,6 +698,11 @@ config_options = [ """If enabled, link to all shared libraries using 'rpath', i.e., a fixed run-time search path for dynamic library loading.""", True), + ( + "openmp_flag", + """Compiler flags used for multiprocessing (only used to generate sample build + scripts).""", + defaults.openmp_flag), EnumVariable( 'layout', """The layout of the directory structure. 'standard' installs files to @@ -892,6 +899,7 @@ env['CPPDEFINES'] = {} env['warning_flags'] = listify(env['warning_flags']) env["pch_flags"] = listify(env["pch_flags"]) +env["openmp_flag"] = listify(env["openmp_flag"]) if env['optimize']: env['CCFLAGS'] += listify(env['optimize_flags']) From 71ebed1d212fdd153e5e6d22148a673d28eef47d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Nov 2021 15:09:15 -0500 Subject: [PATCH 10/11] [SCons] Update comments --- SConstruct | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/SConstruct b/SConstruct index bffaaa1216..19f731d177 100644 --- a/SConstruct +++ b/SConstruct @@ -195,8 +195,10 @@ if os.name == 'nt': ( "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. Windows MSVC only.""", + installed version. Specify '12.0' for Visual Studio 2013, '14.0' for + Visual Studio 2015, '14.1' ('14.1x') Visual Studio 2017, or '14.2' + ('14.2x') for Visual Studio 2019. For version numbers in parentheses, + 'x' is a placeholder for a minor version number. Windows MSVC only.""", ""), EnumVariable( "target_arch", @@ -216,7 +218,7 @@ if os.name == 'nt': EnumVariable( "toolchain", """The preferred compiler toolchain. If MSVC is not on the path but - 'g++' is on he path, 'mingw' is used as a backup. Windows only.""", + 'g++' is on the path, 'mingw' is used as a backup. Windows only.""", defaults.toolchain, ("msvc", "mingw", "intel"))) opts.AddVariables(windows_compiler_options[-1]) opts.Update(pickCompilerEnv) @@ -444,7 +446,7 @@ config_options = [ '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, + are: "C:\Program Files\MATLAB\R2021a" on Windows, "/Applications/MATLAB_R2011a.app" on macOS, or "/opt/MATLAB/R2011a" on Linux.""", '', PathVariable.PathAccept), @@ -501,7 +503,7 @@ config_options = [ '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 + ('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 From 54e260092d9865015df8d56ac114218fe6107c47 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Nov 2021 16:18:10 -0500 Subject: [PATCH 11/11] [SCons] Require SCons >= 3.0.0 --- SConstruct | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/SConstruct b/SConstruct index 19f731d177..44e93ccbf0 100644 --- a/SConstruct +++ b/SConstruct @@ -64,6 +64,11 @@ if not COMMAND_LINE_TARGETS: logger.info(__doc__, print_level=False) sys.exit(0) +if parse_version(SCons.__version__) < parse_version("3.0.0"): + logger.info("Cantera requires SCons with a minimum version of 3.0.0. Exiting.", + print_level=False) + sys.exit(0) + valid_commands = ("build", "clean", "install", "uninstall", "help", "msi", "samples", "sphinx", "doxygen", "dump") @@ -378,10 +383,8 @@ if env['OS'] == 'Windows': elif env["OS"] == "Darwin": defaults.select("macOS") -# 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')): +if (env["toolchain"] == "mingw"): defaults.select("mingw") defaults.select("default")