summaryrefslogtreecommitdiff
path: root/var/spack/repos/builtin/packages/openfoam/package.py
diff options
context:
space:
mode:
Diffstat (limited to 'var/spack/repos/builtin/packages/openfoam/package.py')
-rw-r--r--var/spack/repos/builtin/packages/openfoam/package.py933
1 files changed, 933 insertions, 0 deletions
diff --git a/var/spack/repos/builtin/packages/openfoam/package.py b/var/spack/repos/builtin/packages/openfoam/package.py
new file mode 100644
index 0000000000..4cf12a19b1
--- /dev/null
+++ b/var/spack/repos/builtin/packages/openfoam/package.py
@@ -0,0 +1,933 @@
+# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+#
+# Author: Mark Olesen <mark.olesen@esi-group.com>
+#
+# Legal Notice
+# ------------
+# OPENFOAM is a trademark owned by OpenCFD Ltd
+# (producer and distributor of the OpenFOAM software via www.openfoam.com).
+# The trademark information must remain visible and unadulterated in this
+# file and via the "spack info" and comply with the term set by
+# http://openfoam.com/legal/trademark-policy.php
+#
+# This file is not part of OpenFOAM, nor does it constitute a component of an
+# OpenFOAM distribution.
+#
+##############################################################################
+#
+# Notes
+# - mpi handling: WM_MPLIB=USERMPI and use spack to populate an appropriate
+# configuration and generate wmake rules for 'USER' and 'USERMPI'
+# mpi implementations.
+#
+# - Resolution of flex, zlib needs more attention (within OpenFOAM)
+# - +paraview:
+# depends_on should just be 'paraview+plugins' but that resolves poorly.
+# Workaround: use preferred variants "+plugins +qt"
+# packages:
+# paraview:
+# variants: +plugins +qt
+# in ~/.spack/packages.yaml
+#
+# Known issues
+# - Combining +zoltan with +int64 has not been tested, but probably won't work.
+# - Combining +mgridgen with +int64 or +float32 probably won't work.
+#
+# The spack 'develop' version of openfoam retains the upstream
+# WM_PROJECT_VERSION=plus naming internally.
+#
+##############################################################################
+import glob
+import re
+import os
+
+from spack import *
+from spack.util.environment import EnvironmentModifications
+import llnl.util.tty as tty
+
+
+# Not the nice way of doing things, but is a start for refactoring
+__all__ = [
+ 'add_extra_files',
+ 'write_environ',
+ 'rewrite_environ_files',
+ 'mplib_content',
+ 'foam_add_path',
+ 'foam_add_lib',
+ 'OpenfoamArch',
+]
+
+
+def add_extra_files(foam_pkg, common, local, **kwargs):
+ """Copy additional common and local files into the stage.source_path
+ from the openfoam/common and the package/assets directories,
+ respectively
+ """
+ outdir = foam_pkg.stage.source_path
+
+ indir = join_path(os.path.dirname(__file__), 'common')
+ for f in common:
+ tty.info('Added file {0}'.format(f))
+ install(join_path(indir, f), join_path(outdir, f))
+
+ indir = join_path(foam_pkg.package_dir, 'assets')
+ for f in local:
+ tty.info('Added file {0}'.format(f))
+ install(join_path(indir, f), join_path(outdir, f))
+
+
+def format_export(key, value):
+ """Format key,value pair as 'export' with newline for POSIX shell.
+ A leading '#' for key adds a comment character to the entire line.
+ A value of 'None' corresponds to 'unset'.
+ """
+ if key.startswith('#'):
+ return '## export {0}={1}\n'.format(re.sub(r'^#+\s*', '', key), value)
+ elif value is None:
+ return 'unset {0}\n'.format(key)
+ else:
+ return 'export {0}={1}\n'.format(key, value)
+
+
+def format_setenv(key, value):
+ """Format key,value pair as 'setenv' with newline for C-shell.
+ A leading '#' for key adds a comment character to the entire line.
+ A value of 'None' corresponds to 'unsetenv'.
+ """
+ if key.startswith('#'):
+ return '## setenv {0} {1}\n'.format(re.sub(r'^#+\s*', '', key), value)
+ elif value is None:
+ return 'unsetenv {0}\n'.format(key)
+ else:
+ return 'setenv {0} {1}\n'.format(key, value)
+
+
+def _write_environ_entries(outfile, environ, formatter):
+ """Write environment settings as 'export' or 'setenv'.
+ If environ is a dict, write in sorted order.
+ If environ is a list, write pair-wise.
+ Also descends into sub-dict and sub-list, but drops the key.
+ """
+ if isinstance(environ, dict):
+ for key in sorted(environ):
+ entry = environ[key]
+ if isinstance(entry, dict):
+ _write_environ_entries(outfile, entry, formatter)
+ elif isinstance(entry, list):
+ _write_environ_entries(outfile, entry, formatter)
+ else:
+ outfile.write(formatter(key, entry))
+ elif isinstance(environ, list):
+ for item in environ:
+ outfile.write(formatter(item[0], item[1]))
+
+
+def _write_environ_file(output, environ, formatter):
+ """Write environment settings as 'export' or 'setenv'.
+ If environ is a dict, write in sorted order.
+ If environ is a list, write pair-wise.
+ Also descends into sub-dict and sub-list, but drops the key.
+ """
+ with open(output, 'w') as outfile:
+ outfile.write('# spack generated\n')
+ _write_environ_entries(outfile, environ, formatter)
+ outfile.write('# spack\n')
+
+
+def write_environ(environ, **kwargs):
+ """Write environment settings as 'export' or 'setenv'.
+ If environ is a dict, write in sorted order.
+ If environ is a list, write pair-wise.
+
+ Keyword Options:
+ posix[=None] If set, the name of the POSIX file to rewrite.
+ cshell[=None] If set, the name of the C-shell file to rewrite.
+ """
+ rcfile = kwargs.get('posix', None)
+ if rcfile:
+ _write_environ_file(rcfile, environ, format_export)
+ rcfile = kwargs.get('cshell', None)
+ if rcfile:
+ _write_environ_file(rcfile, environ, format_setenv)
+
+
+def rewrite_environ_files(environ, **kwargs):
+ """Use filter_file to rewrite (existing) POSIX shell or C-shell files.
+ Keyword Options:
+ posix[=None] If set, the name of the POSIX file to rewrite.
+ cshell[=None] If set, the name of the C-shell file to rewrite.
+ """
+ rcfile = kwargs.get('posix', None)
+ if rcfile and os.path.isfile(rcfile):
+ for k, v in environ.items():
+ regex = r'^(\s*export\s+{0})=.*$'.format(k)
+ if not v:
+ replace = r'unset {0} #SPACK: unset'.format(k)
+ elif v.startswith('#'):
+ replace = r'unset {0} {1}'.format(k, v)
+ else:
+ replace = r'\1={0}'.format(v)
+ filter_file(regex, replace, rcfile, backup=False)
+
+ rcfile = kwargs.get('cshell', None)
+ if rcfile and os.path.isfile(rcfile):
+ for k, v in environ.items():
+ regex = r'^(\s*setenv\s+{0})\s+.*$'.format(k)
+ if not v:
+ replace = r'unsetenv {0} #SPACK: unset'.format(k)
+ elif v.startswith('#'):
+ replace = r'unsetenv {0} {1}'.format(k, v)
+ else:
+ replace = r'\1 {0}'.format(v)
+ filter_file(regex, replace, rcfile, backup=False)
+
+
+def foam_add_path(*args):
+ """A string with args prepended to 'PATH'"""
+ return '"' + ':'.join(args) + ':${PATH}"'
+
+
+def foam_add_lib(*args):
+ """A string with args prepended to 'LD_LIBRARY_PATH'"""
+ return '"' + ':'.join(args) + ':${LD_LIBRARY_PATH}"'
+
+
+def pkglib(package, pre=None):
+ """Get lib64 or lib from package prefix.
+
+ Optional parameter 'pre' to provide alternative prefix
+ """
+ libdir = package.prefix.lib64
+ if not os.path.isdir(libdir):
+ libdir = package.prefix.lib
+ if pre:
+ return join_path(pre, os.path.basename(libdir))
+ else:
+ return libdir
+
+
+def mplib_content(spec, pre=None):
+ """The mpi settings (from spack) for the OpenFOAM wmake includes, which
+ allows later reuse within OpenFOAM.
+
+ Optional parameter 'pre' to provide alternative prefix
+ """
+ mpi_spec = spec['mpi']
+ bin = mpi_spec.prefix.bin
+ inc = mpi_spec.prefix.include
+ lib = pkglib(mpi_spec)
+
+ libname = 'mpi'
+ if 'mpich' in mpi_spec.name:
+ libname = 'mpich'
+
+ if pre:
+ bin = join_path(pre, os.path.basename(bin))
+ inc = join_path(pre, os.path.basename(inc))
+ lib = join_path(pre, os.path.basename(lib))
+ else:
+ pre = mpi_spec.prefix
+
+ info = {
+ 'name': '{0}-{1}'.format(mpi_spec.name, mpi_spec.version),
+ 'prefix': pre,
+ 'include': inc,
+ 'bindir': bin,
+ 'libdir': lib,
+ 'FLAGS': '-DOMPI_SKIP_MPICXX -DMPICH_SKIP_MPICXX',
+ 'PINC': '-I{0}'.format(inc),
+ 'PLIBS': '-L{0} -l{1}'.format(lib, libname),
+ }
+ return info
+
+
+# -----------------------------------------------------------------------------
+
+class Openfoam(Package):
+ """OpenFOAM is a GPL-opensource C++ CFD-toolbox.
+ This offering is supported by OpenCFD Ltd,
+ producer and distributor of the OpenFOAM software via www.openfoam.com,
+ and owner of the OPENFOAM trademark.
+ OpenCFD Ltd has been developing and releasing OpenFOAM since its debut
+ in 2004.
+ """
+
+ maintainers = ['olesenm']
+ homepage = "http://www.openfoam.com/"
+ url = "https://sourceforge.net/projects/openfoamplus/files/v1706/OpenFOAM-v1706.tgz"
+ git = "https://develop.openfoam.com/Development/OpenFOAM-plus.git"
+ list_url = "https://sourceforge.net/projects/openfoamplus/files/"
+ list_depth = 2
+
+ version('develop', branch='develop', submodules='True')
+ version('1906', 'ab7017e262c0c0fceec55c31e2153180')
+ version('1812_190531', 'a4b416838a8a76fdec22706a33c96de3')
+ version('1812', '6a315687b3601eeece7ff7c7aed3d9a5')
+ version('1806', 'bb244a3bde7048a03edfccffc46c763f')
+ version('1712', '6ad92df051f4d52c7d0ec34f4b8eb3bc')
+ version('1706', '630d30770f7b54d6809efbf94b7d7c8f')
+ version('1612', 'ca02c491369150ab127cbb88ec60fbdf')
+
+ variant('float32', default=False,
+ description='Use single-precision')
+ variant('int64', default=False,
+ description='With 64-bit labels')
+ variant('knl', default=False,
+ description='Use KNL compiler settings')
+ variant('kahip', default=False,
+ description='With kahip decomposition')
+ variant('metis', default=False,
+ description='With metis decomposition')
+ variant('scotch', default=True,
+ description='With scotch/ptscotch decomposition')
+ variant('zoltan', default=False,
+ description='With zoltan renumbering')
+ # TODO?# variant('scalasca', default=False,
+ # TODO?# description='With scalasca profiling')
+ variant('mgridgen', default=False, description='With mgridgen support')
+ variant('paraview', default=False,
+ description='Build paraview plugins and runtime post-processing')
+ variant('vtk', default=False,
+ description='With VTK runTimePostProcessing')
+ variant('source', default=True,
+ description='Install library/application sources and tutorials')
+
+ depends_on('mpi')
+
+ # After 1712, could suggest openmpi+thread_multiple for collated output
+ # but particular mixes of mpi versions and InfiniBand may not work so well
+ # conflicts('^openmpi~thread_multiple', when='@1712:')
+
+ depends_on('zlib')
+ depends_on('fftw')
+ depends_on('boost')
+ depends_on('cgal')
+ # The flex restriction is ONLY to deal with a spec resolution clash
+ # introduced by the restriction within scotch!
+ depends_on('flex@:2.6.1,2.6.4:')
+ depends_on('cmake', type='build')
+
+ # Require scotch with ptscotch - corresponds to standard OpenFOAM setup
+ depends_on('scotch~metis+mpi~int64', when='+scotch~int64')
+ depends_on('scotch~metis+mpi+int64', when='+scotch+int64')
+ depends_on('kahip', when='+kahip')
+ depends_on('metis@5:', when='+metis')
+ depends_on('metis+int64', when='+metis+int64')
+ # mgridgen is statically linked
+ depends_on('parmgridgen', when='+mgridgen', type='build')
+ depends_on('zoltan', when='+zoltan')
+ depends_on('vtk', when='+vtk')
+
+ # TODO?# depends_on('scalasca', when='+scalasca')
+
+ # For OpenFOAM plugins and run-time post-processing this should just be
+ # 'paraview+plugins' but that resolves poorly.
+ # Workaround: use preferred variants "+plugins +qt" in
+ # ~/.spack/packages.yaml
+
+ # 1706 ok with newer paraview but avoid pv-5.2, pv-5.3 readers
+ depends_on('paraview@5.4:', when='@1706:+paraview')
+ # 1612 plugins need older paraview
+ depends_on('paraview@:5.0.1', when='@1612+paraview')
+
+ # General patches
+ common = ['spack-Allwmake', 'README-spack']
+ assets = []
+
+ # Version-specific patches
+ patch('1612-spack-patches.patch', when='@1612')
+ patch('1806-have-kahip.patch', when='@1806')
+
+ # Some user config settings
+ # default: 'compile-option': 'RpathOpt',
+ # default: 'mplib': 'USERMPI', # Use user mpi for spack
+ config = {
+ # Add links into bin/, lib/ (eg, for other applications)
+ 'link': False
+ }
+
+ # The openfoam architecture, compiler information etc
+ _foam_arch = None
+
+ # Content for etc/prefs.{csh,sh}
+ etc_prefs = {}
+
+ # Content for etc/config.{csh,sh}/ files
+ etc_config = {}
+
+ phases = ['configure', 'build', 'install']
+ build_script = './spack-Allwmake' # From patch() method.
+
+ #
+ # - End of definitions / setup -
+ #
+
+ def url_for_version(self, version):
+ # Prior to 'v1706' and additional '+' in the naming
+ fmt = self.list_url
+ if version <= Version('1612'):
+ fmt += 'v{0}+/OpenFOAM-v{0}+.tgz'
+ else:
+ fmt += 'v{0}/OpenFOAM-v{0}.tgz'
+ return fmt.format(version, version)
+
+ def setup_environment(self, spack_env, run_env):
+ """Add environment variables to the generated module file.
+ These environment variables come from running:
+
+ .. code-block:: console
+
+ $ . $WM_PROJECT_DIR/etc/bashrc
+ """
+
+ # NOTE: Spack runs setup_environment twice.
+ # 1) pre-build to set up the build environment
+ # 2) post-install to determine runtime environment variables
+ # The etc/bashrc is only available (with corrrect content)
+ # post-installation.
+
+ bashrc = join_path(self.projectdir, 'etc', 'bashrc')
+ minimal = True
+ if os.path.isfile(bashrc):
+ # post-install: source the installed bashrc
+ try:
+ mods = EnvironmentModifications.from_sourcing_file(
+ bashrc,
+ clean=True, # Remove duplicate entries
+ blacklist=[ # Blacklist these
+ # Inadvertent changes
+ # -------------------
+ 'PS1', # Leave unaffected
+ 'MANPATH', # Leave unaffected
+
+ # Unneeded bits
+ # -------------
+ # 'FOAM_SETTINGS', # Do not use with modules
+ # 'FOAM_INST_DIR', # Old
+ # 'FOAM_(APP|ETC|SRC|SOLVERS|UTILITIES)',
+ # 'FOAM_TUTORIALS', # can be useful
+ # 'WM_OSTYPE', # Purely optional value
+
+ # Third-party cruft - only used for orig compilation
+ # -----------------
+ '[A-Z].*_ARCH_PATH',
+ # '(KAHIP|METIS|SCOTCH)_VERSION',
+
+ # User-specific
+ # -------------
+ 'FOAM_RUN',
+ '(FOAM|WM)_.*USER_.*',
+ ],
+ whitelist=[ # Whitelist these
+ 'MPI_ARCH_PATH', # Can be needed for compilation
+ ])
+
+ run_env.extend(mods)
+ spack_env.extend(mods)
+ minimal = False
+ tty.info('OpenFOAM bashrc env: {0}'.format(bashrc))
+ except Exception:
+ minimal = True
+
+ if minimal:
+ # pre-build or minimal environment
+ tty.info('OpenFOAM minimal env {0}'.format(self.prefix))
+ run_env.set('FOAM_PROJECT_DIR', self.projectdir)
+ run_env.set('WM_PROJECT_DIR', self.projectdir)
+ spack_env.set('FOAM_PROJECT_DIR', self.projectdir)
+ spack_env.set('WM_PROJECT_DIR', self.projectdir)
+ for d in ['wmake', self.archbin]: # bin added automatically
+ run_env.prepend_path('PATH', join_path(self.projectdir, d))
+ spack_env.prepend_path('PATH', join_path(self.projectdir, d))
+
+ def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
+ """Location of the OpenFOAM project directory.
+ This is identical to the WM_PROJECT_DIR value, but we avoid that
+ variable since it would mask the normal OpenFOAM cleanup of
+ previous versions.
+ """
+ self.setup_environment(spack_env, run_env)
+
+ @property
+ def projectdir(self):
+ """Absolute location of project directory: WM_PROJECT_DIR/"""
+ return self.prefix # <- install directly under prefix
+
+ @property
+ def foam_arch(self):
+ if not self._foam_arch:
+ self._foam_arch = OpenfoamArch(self.spec, **self.config)
+ return self._foam_arch
+
+ @property
+ def archbin(self):
+ """Relative location of architecture-specific executables"""
+ return join_path('platforms', self.foam_arch, 'bin')
+
+ @property
+ def archlib(self):
+ """Relative location of architecture-specific libraries"""
+ return join_path('platforms', self.foam_arch, 'lib')
+
+ def patch(self):
+ """Adjust OpenFOAM build for spack.
+ Where needed, apply filter as an alternative to normal patching."""
+ add_extra_files(self, self.common, self.assets)
+
+ @when('@:1806')
+ def patch(self):
+ """Adjust OpenFOAM build for spack.
+ Where needed, apply filter as an alternative to normal patching."""
+ add_extra_files(self, self.common, self.assets)
+
+ # Avoid WM_PROJECT_INST_DIR for ThirdParty
+ # This modification is non-critical
+ edits = {
+ 'WM_THIRD_PARTY_DIR':
+ r'$WM_PROJECT_DIR/ThirdParty #SPACK: No separate third-party',
+ }
+ rewrite_environ_files( # etc/{bashrc,cshrc}
+ edits,
+ posix=join_path('etc', 'bashrc'),
+ cshell=join_path('etc', 'cshrc'))
+
+ # The following filtering is non-critical.
+ # It simply prevents 'site' dirs at the wrong level
+ # (likely non-existent anyhow) from being added to
+ # PATH, LD_LIBRARY_PATH.
+ for rcdir in ['config.sh', 'config.csh']:
+ rcfile = join_path('etc', rcdir, 'settings')
+ if os.path.isfile(rcfile):
+ filter_file(
+ 'WM_PROJECT_INST_DIR/',
+ 'WM_PROJECT_DIR/',
+ rcfile,
+ backup=False)
+
+ def configure(self, spec, prefix):
+ """Make adjustments to the OpenFOAM configuration files in their various
+ locations: etc/bashrc, etc/config.sh/FEATURE and customizations that
+ don't properly fit get placed in the etc/prefs.sh file (similiarly for
+ csh).
+ """
+ # Filtering bashrc, cshrc
+ edits = {}
+ edits.update(self.foam_arch.foam_dict())
+ rewrite_environ_files( # etc/{bashrc,cshrc}
+ edits,
+ posix=join_path('etc', 'bashrc'),
+ cshell=join_path('etc', 'cshrc'))
+
+ # Content for etc/prefs.{csh,sh}
+ self.etc_prefs = {
+ # TODO
+ # 'CMAKE_ARCH_PATH': spec['cmake'].prefix,
+ # 'FLEX_ARCH_PATH': spec['flex'].prefix,
+ # 'ZLIB_ARCH_PATH': spec['zlib'].prefix,
+ }
+
+ # MPI content, using MPI_ARCH_PATH
+ user_mpi = mplib_content(spec, '${MPI_ARCH_PATH}')
+
+ # Content for etc/config.{csh,sh}/ files
+ self.etc_config = {
+ 'CGAL': [
+ ('BOOST_ARCH_PATH', spec['boost'].prefix),
+ ('CGAL_ARCH_PATH', spec['cgal'].prefix),
+ ('LD_LIBRARY_PATH',
+ foam_add_lib(
+ pkglib(spec['boost'], '${BOOST_ARCH_PATH}'),
+ pkglib(spec['cgal'], '${CGAL_ARCH_PATH}'))),
+ ],
+ 'FFTW': [
+ ('FFTW_ARCH_PATH', spec['fftw'].prefix), # Absolute
+ ('LD_LIBRARY_PATH',
+ foam_add_lib(
+ pkglib(spec['fftw'], '${BOOST_ARCH_PATH}'))),
+ ],
+ # User-defined MPI
+ 'mpi-user': [
+ ('MPI_ARCH_PATH', spec['mpi'].prefix), # Absolute
+ ('LD_LIBRARY_PATH', foam_add_lib(user_mpi['libdir'])),
+ ('PATH', foam_add_path(user_mpi['bindir'])),
+ ],
+ 'scotch': {},
+ 'kahip': {},
+ 'metis': {},
+ 'ensight': {}, # Disable settings
+ 'paraview': [],
+ 'gperftools': [], # Currently unused
+ 'vtk': [],
+ }
+
+ if '+scotch' in spec:
+ self.etc_config['scotch'] = {
+ 'SCOTCH_ARCH_PATH': spec['scotch'].prefix,
+ # For src/parallel/decompose/Allwmake
+ 'SCOTCH_VERSION': 'scotch-{0}'.format(spec['scotch'].version),
+ }
+
+ if '+kahip' in spec:
+ self.etc_config['kahip'] = {
+ 'KAHIP_ARCH_PATH': spec['kahip'].prefix,
+ }
+
+ if '+metis' in spec:
+ self.etc_config['metis'] = {
+ 'METIS_ARCH_PATH': spec['metis'].prefix,
+ }
+
+ # ParaView_INCLUDE_DIR is not used in 1812, but has no ill-effect
+ if '+paraview' in spec:
+ pvmajor = 'paraview-{0}'.format(spec['paraview'].version.up_to(2))
+ self.etc_config['paraview'] = [
+ ('ParaView_DIR', spec['paraview'].prefix),
+ ('ParaView_INCLUDE_DIR', '${ParaView_DIR}/include/' + pvmajor),
+ ('PV_PLUGIN_PATH', '$FOAM_LIBBIN/' + pvmajor),
+ ('PATH', foam_add_path('${ParaView_DIR}/bin')),
+ ]
+
+ if '+vtk' in spec:
+ self.etc_config['vtk'] = [
+ ('VTK_DIR', spec['vtk'].prefix),
+ ('LD_LIBRARY_PATH',
+ foam_add_lib(pkglib(spec['vtk'], '${VTK_DIR}'))),
+ ]
+
+ # Optional
+ if '+mgridgen' in spec:
+ self.etc_config['mgridgen'] = {
+ 'MGRIDGEN_ARCH_PATH': spec['parmgridgen'].prefix
+ }
+
+ # Optional
+ if '+zoltan' in spec:
+ self.etc_config['zoltan'] = {
+ 'ZOLTAN_ARCH_PATH': spec['zoltan'].prefix
+ }
+
+ # Write prefs files according to the configuration.
+ # Only need prefs.sh for building, but install both for end-users
+ if self.etc_prefs:
+ write_environ(
+ self.etc_prefs,
+ posix=join_path('etc', 'prefs.sh'),
+ cshell=join_path('etc', 'prefs.csh'))
+
+ # Adjust components to use SPACK variants
+ for component, subdict in self.etc_config.items():
+ write_environ(
+ subdict,
+ posix=join_path('etc', 'config.sh', component),
+ cshell=join_path('etc', 'config.csh', component))
+
+ def build(self, spec, prefix):
+ """Build using the OpenFOAM Allwmake script, with a wrapper to source
+ its environment first.
+ Only build if the compiler is known to be supported.
+ """
+ self.foam_arch.has_rule(self.stage.source_path)
+ self.foam_arch.create_rules(self.stage.source_path, self)
+
+ args = ['-silent']
+ if self.parallel: # Build in parallel? - pass as an argument
+ args.append('-j{0}'.format(make_jobs))
+ builder = Executable(self.build_script)
+ builder(*args)
+
+ def install_write_location(self):
+ """Set the installation location (projectdir) in bashrc,cshrc."""
+ mkdirp(self.projectdir)
+
+ # Filtering: bashrc, cshrc
+ edits = {
+ 'WM_PROJECT_DIR': self.projectdir,
+ }
+ etc_dir = join_path(self.projectdir, 'etc')
+ rewrite_environ_files( # Adjust etc/bashrc and etc/cshrc
+ edits,
+ posix=join_path(etc_dir, 'bashrc'),
+ cshell=join_path(etc_dir, 'cshrc'))
+
+ @when('@:1806')
+ def install_write_location(self):
+ """Set the installation location (projectdir) in bashrc,cshrc.
+ In 1806 and earlier, had WM_PROJECT_INST_DIR as the prefix
+ directory where WM_PROJECT_DIR was installed.
+ """
+ mkdirp(self.projectdir)
+ projdir = os.path.basename(self.projectdir)
+
+ # Filtering: bashrc, cshrc
+ edits = {
+ 'WM_PROJECT_INST_DIR': os.path.dirname(self.projectdir),
+ 'WM_PROJECT_DIR': join_path('$WM_PROJECT_INST_DIR', projdir),
+ }
+ etc_dir = join_path(self.projectdir, 'etc')
+ rewrite_environ_files( # Adjust etc/bashrc and etc/cshrc
+ edits,
+ posix=join_path(etc_dir, 'bashrc'),
+ cshell=join_path(etc_dir, 'cshrc'))
+
+ def install(self, spec, prefix):
+ """Install under the projectdir"""
+ mkdirp(self.projectdir)
+
+ # All top-level files, except spack build info and possibly Allwmake
+ if '+source' in spec:
+ ignored = re.compile(r'^spack-.*')
+ else:
+ ignored = re.compile(r'^(Allwmake|spack-).*')
+
+ files = [
+ f for f in glob.glob("*")
+ if os.path.isfile(f) and not ignored.search(f)
+ ]
+ for f in files:
+ install(f, self.projectdir)
+
+ # Having wmake and ~source is actually somewhat pointless...
+ # Install 'etc' before 'bin' (for symlinks)
+ dirs = ['etc', 'bin', 'wmake']
+ if '+source' in spec:
+ dirs.extend(['applications', 'src', 'tutorials'])
+
+ for d in dirs:
+ install_tree(
+ d,
+ join_path(self.projectdir, d),
+ symlinks=True)
+
+ dirs = ['platforms']
+ if '+source' in spec:
+ dirs.extend(['doc'])
+
+ # Install platforms (and doc) skipping intermediate targets
+ relative_ignore_paths = ['src', 'applications', 'html', 'Guides']
+ ignore = lambda p: p in relative_ignore_paths
+ for d in dirs:
+ install_tree(
+ d,
+ join_path(self.projectdir, d),
+ ignore=ignore,
+ symlinks=True)
+
+ self.install_write_location()
+ self.install_links()
+
+ def install_links(self):
+ """Add symlinks into bin/, lib/ (eg, for other applications)"""
+ # Make build log visible - it contains OpenFOAM-specific information
+ with working_dir(self.projectdir):
+ os.symlink(
+ join_path('.spack', 'build.out'),
+ join_path('log.' + str(self.foam_arch)))
+
+ if not self.config['link']:
+ return
+
+ # ln -s platforms/linux64GccXXX/lib lib
+ with working_dir(self.projectdir):
+ if os.path.isdir(self.archlib):
+ os.symlink(self.archlib, 'lib')
+
+ # (cd bin && ln -s ../platforms/linux64GccXXX/bin/* .)
+ with working_dir(join_path(self.projectdir, 'bin')):
+ for f in [
+ f for f in glob.glob(join_path('..', self.archbin, "*"))
+ if os.path.isfile(f)
+ ]:
+ os.symlink(f, os.path.basename(f))
+
+
+# -----------------------------------------------------------------------------
+
+class OpenfoamArch(object):
+ """OpenfoamArch represents architecture/compiler settings for OpenFOAM.
+ The string representation is WM_OPTIONS.
+
+ Keywords
+ label-size=[True] supports int32/int64
+ compile-option[=RpathOpt]
+ mplib[=USERMPI]
+ """
+
+ #: Map spack compiler names to OpenFOAM compiler names
+ # By default, simply capitalize the first letter
+ compiler_mapping = {'intel': 'icc'}
+
+ def __init__(self, spec, **kwargs):
+ # Some user settings, to be adjusted manually or via variants
+ self.compiler = None # <- %compiler
+ self.arch_option = '' # Eg, -march=knl
+ self.label_size = None # <- +int64
+ self.precision_option = 'DP' # <- +float32
+ self.compile_option = kwargs.get('compile-option', 'RpathOpt')
+ self.arch = None
+ self.options = None
+ self.rule = None
+ self.mplib = kwargs.get('mplib', 'USERMPI')
+
+ # Normally support WM_LABEL_OPTION, but not yet for foam-extend
+ if '+int64' in spec:
+ self.label_size = '64'
+ elif kwargs.get('label-size', True):
+ self.label_size = '32'
+
+ if '+float32' in spec:
+ self.precision_option = 'SP'
+
+ # Processor/architecture-specific optimizations
+ if '+knl' in spec:
+ self.arch_option = '-march=knl'
+
+ # spec.architecture.platform is like `uname -s`, but lower-case
+ platform = spec.architecture.platform
+
+ # spec.architecture.target is like `uname -m`
+ target = spec.architecture.target
+
+ if platform == 'linux':
+ if target == 'x86_64':
+ platform += '64'
+ elif target == 'ia64':
+ platform += 'IA64'
+ elif target == 'armv7l':
+ platform += 'ARM7'
+ elif target == 'aarch64':
+ platform += 'ARM64'
+ elif target == 'ppc64':
+ platform += 'PPC64'
+ elif target == 'ppc64le':
+ platform += 'PPC64le'
+ elif platform == 'darwin':
+ if target == 'x86_64':
+ platform += '64'
+ # ... and others?
+
+ self.arch = platform
+
+ # Capitalized version of the compiler name, which usually corresponds
+ # to how OpenFOAM will camel-case things.
+ # Use compiler_mapping to handing special cases.
+ # Also handle special compiler options (eg, KNL)
+ comp = spec.compiler.name
+
+ if comp in self.compiler_mapping:
+ comp = self.compiler_mapping[comp]
+ comp = comp.capitalize()
+
+ self.compiler = comp
+ self.rule = self.arch + self.compiler
+
+ # Build WM_OPTIONS
+ # ----
+ # WM_LABEL_OPTION=Int$WM_LABEL_SIZE
+ # WM_OPTIONS=$WM_ARCH$WM_COMPILER$WM_PRECISION_OPTION$WM_LABEL_OPTION$WM_COMPILE_OPTION
+ # or
+ # WM_OPTIONS=$WM_ARCH$WM_COMPILER$WM_PRECISION_OPTION$WM_COMPILE_OPTION
+ # ----
+ self.options = ''.join([
+ self.rule,
+ self.precision_option,
+ ('Int' + self.label_size if self.label_size else ''),
+ self.compile_option])
+
+ def __str__(self):
+ return self.options
+
+ def __repr__(self):
+ return str(self)
+
+ def foam_dict(self):
+ """Returns a dictionary for OpenFOAM prefs, bashrc, cshrc."""
+ return dict([
+ ('WM_COMPILER', self.compiler),
+ ('WM_LABEL_SIZE', self.label_size),
+ ('WM_PRECISION_OPTION', self.precision_option),
+ ('WM_COMPILE_OPTION', self.compile_option),
+ ('WM_MPLIB', self.mplib),
+ ])
+
+ def _rule_directory(self, projdir=None, general=False):
+ """The wmake/rules/ compiler directory"""
+ if general:
+ relative = os.path.join('wmake', 'rules', 'General')
+ else:
+ relative = os.path.join('wmake', 'rules', self.rule)
+ if projdir:
+ return os.path.join(projdir, relative)
+ else:
+ return relative
+
+ def has_rule(self, projdir):
+ """Verify that a wmake/rules/ compiler rule exists in the project
+ directory.
+ """
+ # Insist on a wmake rule for this architecture/compiler combination
+ rule_dir = self._rule_directory(projdir)
+
+ if not os.path.isdir(rule_dir):
+ raise InstallError(
+ 'No wmake rule for {0}'.format(self.rule))
+ if not re.match(r'.+Opt$', self.compile_option):
+ raise InstallError(
+ "WM_COMPILE_OPTION={0} is not type '*Opt'"
+ .format(self.compile_option))
+ return True
+
+ def create_rules(self, projdir, foam_pkg):
+ """ Create cRpathOpt,c++RpathOpt and mplibUSER,mplibUSERMPI
+ rules in the specified project directory.
+ The compiler rules are based on the respective cOpt,c++Opt rules
+ but with additional rpath information for the OpenFOAM libraries.
+
+ The rpath rules allow wmake to use spack information with minimal
+ modification to OpenFOAM.
+ The rpath is used for the installed libpath (continue to use
+ LD_LIBRARY_PATH for values during the build).
+ """
+ # Note: the 'c' rules normally don't need rpath, since they are just
+ # used for statically linked wmake utilities, but left in anyhow.
+
+ # rpath for installed OpenFOAM libraries
+ rpath = '{0}{1}'.format(
+ foam_pkg.compiler.cxx_rpath_arg,
+ join_path(foam_pkg.projectdir, foam_pkg.archlib))
+
+ user_mpi = mplib_content(foam_pkg.spec)
+ rule_dir = self._rule_directory(projdir)
+
+ with working_dir(rule_dir):
+ # Compiler: copy existing cOpt,c++Opt and modify '*DBUG' value
+ for lang in ['c', 'c++']:
+ src = '{0}Opt'.format(lang)
+ dst = '{0}{1}'.format(lang, self.compile_option)
+ with open(src, 'r') as infile:
+ with open(dst, 'w') as outfile:
+ for line in infile:
+ line = line.rstrip()
+ outfile.write(line)
+ if re.match(r'^\S+DBUG\s*=', line):
+ outfile.write(' ')
+ outfile.write(rpath)
+ elif re.match(r'^\S+OPT\s*=', line):
+ if self.arch_option:
+ outfile.write(' ')
+ outfile.write(self.arch_option)
+ outfile.write('\n')
+
+ # MPI rules
+ for mplib in ['mplibUSER', 'mplibUSERMPI']:
+ with open(mplib, 'w') as out:
+ out.write("""# Use mpi from spack ({name})\n
+PFLAGS = {FLAGS}
+PINC = {PINC}
+PLIBS = {PLIBS}
+""".format(**user_mpi))
+
+# -----------------------------------------------------------------------------