refactor openfoam packages (#3669)
* Several improvements for the openfoam packages -- Refactor openfoam packages by adding an OpenfoamArch class Use separate configure, build, install phases. Provide FOAM_PROJECT_DIR dependent env for openfoam packages - easier way to locate Eliminate intermediate installation directories - unneeded clutter. - makes it less than easy to find the etc/bashrc file Add versioning for all openfoam patches - no certainty which parts (if any) will be needed in future versions, especially if we strive to ensure that the upstream version builds well with spack to begin with. Support build of develop branches - helps track build regressions for future openfoam releases STYLE: use common/ and assets/ to provide additional (build) resources ... * - adjust OpenFOAM provider Move openfoam-com up front since this is the one being used as a base for the others
This commit is contained in:

committed by
Adam J. Stewart

parent
b1fceb75d0
commit
1f2e56e1f3
@@ -48,16 +48,17 @@
|
||||
# - reworked to mirror the openfoam-com package.
|
||||
# If changes are needed here, consider if they need applying there too.
|
||||
#
|
||||
# Known issues
|
||||
# - Combining +parmgridgen with +float32 probably won't work.
|
||||
#
|
||||
##############################################################################
|
||||
from spack import *
|
||||
from spack.environment import *
|
||||
|
||||
import multiprocessing
|
||||
import glob
|
||||
import re
|
||||
import shutil
|
||||
import os
|
||||
from os.path import isdir, isfile
|
||||
from spack.pkg.builtin.openfoam_com import *
|
||||
|
||||
|
||||
@@ -77,10 +78,9 @@ class FoamExtend(Package):
|
||||
version('3.0', git='http://git.code.sf.net/p/foam-extend/foam-extend-3.0')
|
||||
|
||||
# variant('int64', default=False,
|
||||
# description='Compile with 64-bit labels')
|
||||
# description='Compile with 64-bit label')
|
||||
variant('float32', default=False,
|
||||
description='Compile with 32-bit scalar (single-precision)')
|
||||
|
||||
variant('paraview', default=False,
|
||||
description='Build paraview plugins (eg, paraFoam)')
|
||||
variant('scotch', default=True,
|
||||
@@ -96,10 +96,6 @@ class FoamExtend(Package):
|
||||
variant('source', default=True,
|
||||
description='Install library/application sources and tutorials')
|
||||
|
||||
#: Map spack compiler names to OpenFOAM compiler names
|
||||
# By default, simply capitalize the first letter
|
||||
compiler_mapping = {'intel': 'icc'}
|
||||
|
||||
provides('openfoam')
|
||||
depends_on('mpi')
|
||||
depends_on('python')
|
||||
@@ -111,25 +107,22 @@ class FoamExtend(Package):
|
||||
depends_on('scotch~metis+mpi', when='+ptscotch')
|
||||
depends_on('metis@5:', when='+metis')
|
||||
depends_on('parmetis', when='+parmetis')
|
||||
depends_on('parmgridgen', when='+parmgridgen')
|
||||
# mgridgen is statically linked
|
||||
depends_on('parmgridgen', when='+parmgridgen', type='build')
|
||||
depends_on('paraview@:5.0.1', when='+paraview')
|
||||
|
||||
# Some user settings, to be adjusted manually or via variants
|
||||
foam_cfg = {
|
||||
'WM_COMPILER': 'Gcc', # <- %compiler
|
||||
'WM_ARCH_OPTION': '64', # (32/64-bit on x86_64)
|
||||
# FUTURE? 'WM_LABEL_SIZE': '32', # <- +int64
|
||||
'WM_PRECISION_OPTION': 'DP', # <- +float32
|
||||
'WM_COMPILE_OPTION': 'SPACKOpt', # Do not change
|
||||
'WM_MPLIB': 'USER', # USER | USERMPI
|
||||
# General patches
|
||||
common = ['spack-Allwmake', 'README-spack']
|
||||
assets = []
|
||||
|
||||
# Some user config settings
|
||||
config = {
|
||||
'label-size': False, # <- No int32/int64 support
|
||||
'mplib': 'USERMPI', # USER | USERMPI
|
||||
}
|
||||
|
||||
# The system description is frequently needed
|
||||
foam_sys = {
|
||||
'WM_ARCH': None,
|
||||
'WM_COMPILER': None,
|
||||
'WM_OPTIONS': None,
|
||||
}
|
||||
# The openfoam architecture, compiler information etc
|
||||
_foam_arch = None
|
||||
|
||||
# Content for etc/prefs.{csh,sh}
|
||||
etc_prefs = {}
|
||||
@@ -137,163 +130,54 @@ class FoamExtend(Package):
|
||||
# Content for etc/config.{csh,sh}/ files
|
||||
etc_config = {}
|
||||
|
||||
build_script = './spack-Allwmake' # <- Generated by patch() method.
|
||||
# phases = ['configure', 'build', 'install']
|
||||
# build_system_class = 'OpenfoamCom'
|
||||
phases = ['configure', 'build', 'install']
|
||||
build_script = './spack-Allwmake' # <- Added by patch() method.
|
||||
|
||||
#
|
||||
# - End of definitions / setup -
|
||||
#
|
||||
|
||||
def setup_environment(self, spack_env, run_env):
|
||||
run_env.set('FOAM_INST_DIR', self.prefix)
|
||||
run_env.set('FOAM_INST_DIR', os.path.dirname(self.projectdir)),
|
||||
run_env.set('FOAM_PROJECT_DIR', self.projectdir)
|
||||
run_env.set('WM_PROJECT_DIR', self.projectdir)
|
||||
for d in ['wmake', self.archbin]: # bin already added automatically
|
||||
run_env.prepend_path('PATH', join_path(self.projectdir, d))
|
||||
run_env.set('MPI_BUFFER_SIZE', "20000000")
|
||||
|
||||
@property
|
||||
def _canonical(self):
|
||||
"""Canonical name for this package and version"""
|
||||
return 'foam-extend-{0}'.format(self.version.up_to(2))
|
||||
def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
|
||||
"""Provide location of the OpenFOAM project.
|
||||
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.
|
||||
"""
|
||||
spack_env.set('FOAM_PROJECT_DIR', self.projectdir)
|
||||
|
||||
@property
|
||||
def projectdir(self):
|
||||
"""Absolute location of project directory: WM_PROJECT_DIR/"""
|
||||
return join_path(self.prefix, self._canonical) # <- prefix/canonical
|
||||
return self.prefix # <- install directly under prefix
|
||||
|
||||
@property
|
||||
def etc(self):
|
||||
"""Absolute location of the OpenFOAM etc/ directory"""
|
||||
return join_path(self.projectdir, 'etc')
|
||||
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"""
|
||||
wm_options = self.set_openfoam()
|
||||
return join_path('applications', 'bin', wm_options)
|
||||
return join_path('applications', 'bin', self.foam_arch)
|
||||
|
||||
@property
|
||||
def archlib(self):
|
||||
"""Relative location of architecture-specific libraries"""
|
||||
wm_options = self.set_openfoam()
|
||||
return join_path('lib', wm_options)
|
||||
|
||||
@property
|
||||
def wm_options(self):
|
||||
"""The architecture+compiler+options for OpenFOAM"""
|
||||
opts = self.set_openfoam()
|
||||
return opts
|
||||
|
||||
@property
|
||||
def rpath_info(self):
|
||||
"""Define 'SPACKOpt' compiler optimization file to have wmake
|
||||
use spack information with minimum modifications to OpenFOAM
|
||||
"""
|
||||
build_libpath = join_path(self.stage.source_path, self.archlib)
|
||||
install_libpath = join_path(self.projectdir, self.archlib)
|
||||
|
||||
# 'DBUG': rpaths
|
||||
return '{0}{1} {2}{3}'.format(
|
||||
self.compiler.cxx_rpath_arg, install_libpath,
|
||||
self.compiler.cxx_rpath_arg, build_libpath)
|
||||
|
||||
def openfoam_arch(self):
|
||||
"""Return an architecture value similar to what OpenFOAM does in
|
||||
etc/config.sh/settings, but slightly more generous.
|
||||
Uses and may adjust foam_cfg[WM_ARCH_OPTION] as a side-effect
|
||||
"""
|
||||
# spec.architecture.platform is like `uname -s`, but lower-case
|
||||
platform = self.spec.architecture.platform
|
||||
|
||||
# spec.architecture.target is like `uname -m`
|
||||
target = self.spec.architecture.target
|
||||
|
||||
if platform == 'linux':
|
||||
if target == 'i686':
|
||||
self.foam_cfg['WM_ARCH_OPTION'] = '32' # Force consistency
|
||||
elif target == 'x86_64':
|
||||
if self.foam_cfg['WM_ARCH_OPTION'] == '64':
|
||||
platform += '64'
|
||||
elif target == 'ia64':
|
||||
platform += 'ia64'
|
||||
elif target == 'armv7l':
|
||||
platform += 'ARM7'
|
||||
elif target == ppc64:
|
||||
platform += 'PPC64'
|
||||
elif target == ppc64le:
|
||||
platform += 'PPC64le'
|
||||
elif platform == 'darwin':
|
||||
if target == 'x86_64':
|
||||
platform += 'Intel'
|
||||
if self.foam_cfg['WM_ARCH_OPTION'] == '64':
|
||||
platform += '64'
|
||||
# ... and others?
|
||||
return platform
|
||||
|
||||
def openfoam_compiler(self):
|
||||
"""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 = self.compiler.name
|
||||
if comp in self.compiler_mapping:
|
||||
comp = self.compiler_mapping[comp]
|
||||
comp = comp.capitalize()
|
||||
|
||||
if '+knl' in self.spec:
|
||||
comp += 'KNL'
|
||||
return comp
|
||||
|
||||
# For foam-extend: does not yet support +int64
|
||||
def set_openfoam(self):
|
||||
"""Populate foam_cfg, foam_sys according to
|
||||
variants, architecture, compiler.
|
||||
Returns WM_OPTIONS.
|
||||
"""
|
||||
# Run once
|
||||
opts = self.foam_sys['WM_OPTIONS']
|
||||
if opts:
|
||||
return opts
|
||||
|
||||
wm_arch = self.openfoam_arch()
|
||||
wm_compiler = self.openfoam_compiler()
|
||||
compileOpt = self.foam_cfg['WM_COMPILE_OPTION']
|
||||
|
||||
# Insist on a wmake rule for this architecture/compiler combination
|
||||
archCompiler = wm_arch + wm_compiler
|
||||
compiler_rule = join_path(
|
||||
self.stage.source_path, 'wmake', 'rules', archCompiler)
|
||||
|
||||
if not isdir(compiler_rule):
|
||||
raise RuntimeError(
|
||||
'No wmake rule for {0}'.format(archCompiler))
|
||||
if not re.match(r'.+Opt$', compileOpt):
|
||||
raise RuntimeError(
|
||||
"WM_COMPILE_OPTION={0} is not type '*Opt'".format(compileOpt))
|
||||
|
||||
# Adjust for variants
|
||||
# FUTURE? self.foam_cfg['WM_LABEL_SIZE'] = (
|
||||
# FUTURE? '64' if '+int64' in self.spec else '32'
|
||||
# FUTURE? )
|
||||
self.foam_cfg['WM_PRECISION_OPTION'] = (
|
||||
'SP' if '+float32' in self.spec else 'DP'
|
||||
)
|
||||
|
||||
# ----
|
||||
# WM_OPTIONS=$WM_ARCH$WM_COMPILER$WM_PRECISION_OPTION$WM_COMPILE_OPTION
|
||||
# ----
|
||||
self.foam_sys['WM_ARCH'] = wm_arch
|
||||
self.foam_sys['WM_COMPILER'] = wm_compiler
|
||||
self.foam_cfg['WM_COMPILER'] = wm_compiler # For bashrc,cshrc too
|
||||
self.foam_sys['WM_OPTIONS'] = ''.join([
|
||||
wm_arch,
|
||||
wm_compiler,
|
||||
self.foam_cfg['WM_PRECISION_OPTION'],
|
||||
# FUTURE? 'Int', self.foam_cfg['WM_LABEL_SIZE'], # Int32/Int64
|
||||
compileOpt
|
||||
])
|
||||
return self.foam_sys['WM_OPTIONS']
|
||||
return join_path('lib', self.foam_arch)
|
||||
|
||||
def patch(self):
|
||||
"""Adjust OpenFOAM build for spack. Where needed, apply filter as an
|
||||
alternative to normal patching.
|
||||
"""
|
||||
self.set_openfoam() # May need foam_cfg/foam_sys information
|
||||
"""Adjust OpenFOAM build for spack.
|
||||
Where needed, apply filter as an alternative to normal patching."""
|
||||
add_extra_files(self, self.common, self.assets)
|
||||
|
||||
# Adjust ParMGridGen - this is still a mess
|
||||
files = [
|
||||
@@ -319,23 +203,7 @@ def patch(self):
|
||||
filter_file(
|
||||
r'#if YY_FLEX_SUBMINOR_VERSION < 34',
|
||||
r'#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34', # noqa: E501
|
||||
f, backup=False
|
||||
)
|
||||
|
||||
# Build wrapper script
|
||||
with open(self.build_script, 'w') as out:
|
||||
out.write(
|
||||
"""#!/bin/bash
|
||||
export FOAM_INST_DIR=$(cd .. && pwd -L)
|
||||
. $PWD/etc/bashrc '' # No arguments
|
||||
mkdir -p $FOAM_APPBIN $FOAM_LIBBIN 2>/dev/null # Allow interrupt
|
||||
echo Build openfoam with SPACK
|
||||
echo WM_PROJECT_DIR = $WM_PROJECT_DIR
|
||||
./Allwmake # No arguments
|
||||
#
|
||||
""")
|
||||
set_executable(self.build_script)
|
||||
self.configure(self.spec, self.prefix) # Should be a separate phase
|
||||
f, backup=False)
|
||||
|
||||
def configure(self, spec, prefix):
|
||||
"""Make adjustments to the OpenFOAM configuration files in their various
|
||||
@@ -343,8 +211,6 @@ def configure(self, spec, prefix):
|
||||
don't properly fit get placed in the etc/prefs.sh file (similiarly for
|
||||
csh).
|
||||
"""
|
||||
self.set_openfoam() # Need foam_cfg/foam_sys information
|
||||
|
||||
# Content for etc/prefs.{csh,sh}
|
||||
self.etc_prefs = {
|
||||
'000': { # Sort first
|
||||
@@ -373,7 +239,7 @@ def configure(self, spec, prefix):
|
||||
},
|
||||
}
|
||||
# Adjust configuration via prefs - sort second
|
||||
self.etc_prefs['001'].update(self.foam_cfg)
|
||||
self.etc_prefs['001'].update(self.foam_arch.foam_dict())
|
||||
|
||||
if '+scotch' in spec or '+ptscotch' in spec:
|
||||
pkg = spec['scotch'].prefix
|
||||
@@ -434,41 +300,33 @@ def configure(self, spec, prefix):
|
||||
posix=join_path('etc', 'prefs.sh'),
|
||||
cshell=join_path('etc', 'prefs.csh'))
|
||||
|
||||
archCompiler = self.foam_sys['WM_ARCH'] + self.foam_sys['WM_COMPILER']
|
||||
compileOpt = self.foam_cfg['WM_COMPILE_OPTION']
|
||||
# general_rule = join_path('wmake', 'rules', 'General')
|
||||
compiler_rule = join_path('wmake', 'rules', archCompiler)
|
||||
generate_mplib_rules(compiler_rule, self.spec)
|
||||
generate_compiler_rules(compiler_rule, compileOpt, self.rpath_info)
|
||||
# Record the spack spec information
|
||||
with open("log.spack-spec", 'w') as outfile:
|
||||
outfile.write(spec.tree())
|
||||
|
||||
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.set_openfoam() # Force proper population of foam_cfg/foam_sys
|
||||
self.foam_arch.has_rule(self.stage.source_path)
|
||||
self.foam_arch.create_rules(self.stage.source_path, self)
|
||||
|
||||
args = []
|
||||
if self.parallel: # Build in parallel? - pass via the environment
|
||||
os.environ['WM_NCOMPPROCS'] = str(self.make_jobs) \
|
||||
if self.make_jobs else str(multiprocessing.cpu_count())
|
||||
os.environ['WM_NCOMPPROCS'] = str(make_jobs)
|
||||
builder = Executable(self.build_script)
|
||||
builder(*args)
|
||||
|
||||
def install(self, spec, prefix):
|
||||
"""Install under the projectdir (== prefix/name-version)"""
|
||||
self.build(spec, prefix) # Should be a separate phase
|
||||
opts = self.wm_options
|
||||
"""Install under the projectdir"""
|
||||
opts = str(self.foam_arch)
|
||||
|
||||
# Fairly ugly since intermediate targets are scattered inside sources
|
||||
appdir = 'applications'
|
||||
projdir = os.path.basename(self.projectdir)
|
||||
mkdirp(self.projectdir, join_path(self.projectdir, appdir))
|
||||
|
||||
# Retain build log file
|
||||
out = "spack-build.out"
|
||||
if isfile(out):
|
||||
install(out, join_path(self.projectdir, "log." + opts))
|
||||
# Filtering: bashrc, cshrc
|
||||
edits = {
|
||||
'WM_PROJECT_INST_DIR': os.path.dirname(self.projectdir),
|
||||
'WM_PROJECT_DIR': join_path('$WM_PROJECT_INST_DIR', projdir),
|
||||
}
|
||||
|
||||
# All top-level files, except spack build info and possibly Allwmake
|
||||
if '+source' in spec:
|
||||
@@ -477,36 +335,52 @@ def install(self, spec, prefix):
|
||||
ignored = re.compile(r'^(Allclean|Allwmake|spack-).*')
|
||||
|
||||
files = [
|
||||
f for f in glob.glob("*") if isfile(f) and not ignored.search(f)
|
||||
f for f in glob.glob("*")
|
||||
if os.path.isfile(f) and not ignored.search(f)
|
||||
]
|
||||
for f in files:
|
||||
install(f, self.projectdir)
|
||||
|
||||
# Install directories. install applications/bin directly
|
||||
for d in ['bin', 'etc', 'wmake', 'lib', join_path(appdir, 'bin')]:
|
||||
# Install 'etc' before 'bin' (for symlinks)
|
||||
for d in ['etc', 'bin', 'wmake', 'lib', join_path(appdir, 'bin')]:
|
||||
install_tree(
|
||||
d,
|
||||
join_path(self.projectdir, d))
|
||||
join_path(self.projectdir, d),
|
||||
symlinks=True)
|
||||
|
||||
if '+source' in spec:
|
||||
subitem = join_path(appdir, 'Allwmake')
|
||||
install(subitem, join_path(self.projectdir, subitem))
|
||||
|
||||
ignored = [opts] # Intermediate targets
|
||||
ignored = [opts] # Ignore intermediate targets
|
||||
for d in ['src', 'tutorials']:
|
||||
install_tree(
|
||||
d,
|
||||
join_path(self.projectdir, d),
|
||||
ignore=shutil.ignore_patterns(*ignored))
|
||||
ignore=shutil.ignore_patterns(*ignored),
|
||||
symlinks=True)
|
||||
|
||||
for d in ['solvers', 'utilities']:
|
||||
install_tree(
|
||||
join_path(appdir, d),
|
||||
join_path(self.projectdir, appdir, d),
|
||||
ignore=shutil.ignore_patterns(*ignored))
|
||||
ignore=shutil.ignore_patterns(*ignored),
|
||||
symlinks=True)
|
||||
|
||||
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'))
|
||||
self.install_links()
|
||||
|
||||
def install_links(self):
|
||||
"""Add symlinks into bin/, lib/ (eg, for other applications)"""
|
||||
return
|
||||
# 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)))
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user