diff --git a/RELEASE_NOTES b/RELEASE_NOTES index b6fbb12f27..8b67ed4f8c 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -3,7 +3,35 @@ For more detailed information, please see the git log. These release notes can also be consulted at http://easybuild.readthedocs.org/en/latest/Release_notes.html. -The latest version of easybuild-easyblocks provides 242 software-specific easyblocks and 37 generic easyblocks. +The latest version of easybuild-easyblocks provides 243 software-specific easyblocks and 37 generic easyblocks. + + +v4.5.2 (January 24th 2022) +-------------------------- + +update/bugfix release + +- minor enhancements and updates, including: + - remove necessity for license for oneAPI versions (>= 2021.x) in itac easyblock (#2492) + - add CUDA support in SuiteSparse easyblock (#2627) + - use all available cores for running Perl test suite (#2637) + - add option to not copy the license file for Gurobi (#2639, #2641) + - update AOCC easyblock to support version 3.2.0 (#2643) + - don't grep for specific version in --version output of Intel compiler commands for versions 2022.x (#2644) + - add support for enabling sanity checks for specific components in the Bundle easyblock (#2649) + - update Libint easyblock for LibInt v2.7.0 which requires configuring via CMake (#2650) + - update Mathematica easyblock for version 13 (#2652) + - add sanity check command for Bazel (#2653) + - update NAMD easyblock to allow non-system csh (#2654) + - enhance CUDA easyblock to create version independent pkgconfig files (#2656) + - add util subdirectory to $PATH for WPS (#2658) +- various bug fixes, including: + - convert version numbers to stricly numerical in Siesta easyblock (#2553) + - fix shebang of scripts in Perl installation if installation prefix is too long (#2640) + - enhance IntelBase easyblock to avoid crash when $USER is not set (#2642) + - fix quotes in definition of default platform macro and enhance sanity check in GATE easyblock (#2645) + - avoid excessively long shebang line in compile script for WRF (#2648) + - fix checking of Intel Fortran compiler version in CP2K easyblock for recent toolchains (#2651) v4.5.1 (December 13th 2021) diff --git a/easybuild/easyblocks/__init__.py b/easybuild/easyblocks/__init__.py index 399474bc59..d7dc7a6b26 100644 --- a/easybuild/easyblocks/__init__.py +++ b/easybuild/easyblocks/__init__.py @@ -43,7 +43,7 @@ # recent setuptools versions will *TRANSFORM* something like 'X.Y.Zdev' into 'X.Y.Z.dev0', with a warning like # UserWarning: Normalizing '2.4.0dev' to '2.4.0.dev0' # This causes problems further up the dependency chain... -VERSION = LooseVersion('4.5.1') +VERSION = LooseVersion('4.5.2') UNKNOWN = 'UNKNOWN' diff --git a/easybuild/easyblocks/a/aocc.py b/easybuild/easyblocks/a/aocc.py index 97d30d17dc..e5d5e7b367 100644 --- a/easybuild/easyblocks/a/aocc.py +++ b/easybuild/easyblocks/a/aocc.py @@ -72,6 +72,7 @@ def _aocc_guess_clang_version(self): '2.3.0': '11.0.0', '3.0.0': '12.0.0', '3.1.0': '12.0.0', + '3.2.0': '13.0.0', } if self.version in map_aocc_to_clang_ver: @@ -143,8 +144,8 @@ def sanity_check_step(self): 'bin/clang', 'bin/clang++', 'bin/flang', 'bin/lld', 'bin/llvm-ar', 'bin/llvm-as', 'bin/llvm-config', 'bin/llvm-link', 'bin/llvm-nm', 'bin/llvm-symbolizer', 'bin/opt', 'bin/scan-build', 'bin/scan-view', 'include/clang-c/Index.h', 'include/llvm-c/Core.h', 'lib/clang/%s/include/omp.h' % self.clangversion, - 'lib/clang/%s/include/stddef.h' % self.clangversion, 'lib/libc++.%s' % shlib_ext, - 'lib/libc++abi.%s' % shlib_ext, 'lib/libclang.%s' % shlib_ext, 'lib/libomp.%s' % shlib_ext, + 'lib/clang/%s/include/stddef.h' % self.clangversion, 'lib/libclang.%s' % shlib_ext, + 'lib/libomp.%s' % shlib_ext, ], 'dirs': ['include/llvm', 'lib/clang/%s/lib' % self.clangversion, 'lib32'], } diff --git a/easybuild/easyblocks/b/bazel.py b/easybuild/easyblocks/b/bazel.py index d3c5a87d79..fe34417666 100644 --- a/easybuild/easyblocks/b/bazel.py +++ b/easybuild/easyblocks/b/bazel.py @@ -197,4 +197,8 @@ def sanity_check_step(self): 'files': ['bin/bazel'], 'dirs': [], } - super(EB_Bazel, self).sanity_check_step(custom_paths=custom_paths) + custom_commands = [] + if LooseVersion(self.version) >= LooseVersion('1.0'): + custom_commands.append("bazel --help") + + super(EB_Bazel, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) diff --git a/easybuild/easyblocks/c/cp2k.py b/easybuild/easyblocks/c/cp2k.py index cb13c6a7c2..26d7493b08 100644 --- a/easybuild/easyblocks/c/cp2k.py +++ b/easybuild/easyblocks/c/cp2k.py @@ -474,7 +474,16 @@ def configure_intel_based(self): options['FCFLAGSOPT'] += ' $(INCFLAGS) -heap-arrays 64' options['FCFLAGSOPT2'] += ' $(INCFLAGS) -heap-arrays 64' - ifortver = LooseVersion(get_software_version('ifort')) + # for recent intel toolchains (>= intel/2021a), intel-compilers is the toolchain component + ifortver = get_software_version('intel-compilers') + if ifortver is None: + # fall back to trying to determining Intel Fortran compiler version using 'ifort' as software name + ifortver = get_software_version('ifort') + + if ifortver: + ifortver = LooseVersion(ifortver) + else: + raise EasyBuildError("Failed to determine Intel Fortran compiler version!") # Required due to memory leak that occurs if high optimizations are used (from CP2K 7.1 intel-popt-makefile) if ifortver >= LooseVersion("2018.5"): @@ -511,7 +520,7 @@ def configure_intel_based(self): raise EasyBuildError(failmsg, "v12", "v2011.8") elif ifortver >= LooseVersion("11"): - if LooseVersion(get_software_version('ifort')) >= LooseVersion("11.1.072"): + if ifortver >= LooseVersion("11.1.072"): self.make_instructions += "qs_vxc_atom.o: qs_vxc_atom.F\n\t$(FC) -c $(FCFLAGS2) $<\n" else: diff --git a/easybuild/easyblocks/c/cuda.py b/easybuild/easyblocks/c/cuda.py index cf8e95923e..9a6c52c891 100644 --- a/easybuild/easyblocks/c/cuda.py +++ b/easybuild/easyblocks/c/cuda.py @@ -35,6 +35,7 @@ @author: Robert Mijakovic (LuxProvide S.A.) """ import os +import re import stat from distutils.version import LooseVersion @@ -43,8 +44,8 @@ from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError from easybuild.tools.config import IGNORE -from easybuild.tools.filetools import adjust_permissions, copy_dir, patch_perl_script_autoflush -from easybuild.tools.filetools import remove_file, symlink, which, write_file +from easybuild.tools.filetools import adjust_permissions, change_dir, copy_dir, expand_glob_paths +from easybuild.tools.filetools import patch_perl_script_autoflush, remove_file, symlink, which, write_file from easybuild.tools.run import run_cmd, run_cmd_qa from easybuild.tools.systemtools import AARCH64, POWER, X86_64, get_cpu_architecture, get_shared_lib_ext import easybuild.tools.environment as env @@ -201,7 +202,10 @@ def install_step(self): run_cmd("/bin/sh " + patch['path'] + " --accept-eula --silent --installdir=" + self.installdir) def post_install_step(self): - """Create wrappers for the specified host compilers and generate the appropriate stub symlinks""" + """ + Create wrappers for the specified host compilers, generate the appropriate stub symlinks, + and create version independent pkgconfig files + """ def create_wrapper(wrapper_name, wrapper_comp): """Create for a particular compiler, with a particular name""" wrapper_f = os.path.join(self.installdir, 'bin', wrapper_name) @@ -243,6 +247,19 @@ def create_wrapper(wrapper_name, wrapper_comp): # Also create the lib dir as a symlink symlink('lib64', os.path.join(new_stubs_dir, 'lib'), use_abspath_source=False) + # Packages like xpra look for version independent pc files. + # See e.g. https://github.com/Xpra-org/xpra/blob/master/setup.py#L206 + # Distros provide these files, so let's do it here too + pkgconfig_dir = os.path.join(self.installdir, 'pkgconfig') + if os.path.exists(pkgconfig_dir): + pc_files = expand_glob_paths([os.path.join(pkgconfig_dir, '*.pc')]) + cwd = change_dir(pkgconfig_dir) + for pc_file in pc_files: + pc_file = os.path.basename(pc_file) + link = re.sub('-[0-9]*.?[0-9]*(.[0-9]*)?.pc', '.pc', pc_file) + symlink(pc_file, link, use_abspath_source=False) + change_dir(cwd) + super(EB_CUDA, self).post_install_step() def sanity_check_step(self): @@ -266,6 +283,12 @@ def sanity_check_step(self): custom_paths['files'].append(os.path.join("extras", "CUPTI", "lib64", "libcupti.%s") % shlib_ext) custom_paths['dirs'].append(os.path.join("extras", "CUPTI", "include")) + # Just a subset of files are checked, since the whole list is likely to change, + # and irrelevant in most cases anyway + if os.path.exists(os.path.join(self.installdir, 'pkgconfig')): + pc_files = ['cublas.pc', 'cudart.pc', 'cuda.pc'] + custom_paths['files'].extend(os.path.join('pkgconfig', x) for x in pc_files) + super(EB_CUDA, self).sanity_check_step(custom_paths=custom_paths) def make_module_extra(self): @@ -304,10 +327,11 @@ def make_module_req_guess(self): inc_path.append(os.path.join('nvvm', 'include')) guesses.update({ - 'PATH': bin_path, + 'CPATH': inc_path, 'LD_LIBRARY_PATH': lib_path, 'LIBRARY_PATH': ['lib64', os.path.join('stubs', 'lib64')], - 'CPATH': inc_path, + 'PATH': bin_path, + 'PKG_CONFIG_PATH': ['pkgconfig'], }) return guesses diff --git a/easybuild/easyblocks/g/gate.py b/easybuild/easyblocks/g/gate.py index bc5f71021c..e8311b7c6d 100644 --- a/easybuild/easyblocks/g/gate.py +++ b/easybuild/easyblocks/g/gate.py @@ -88,7 +88,7 @@ def build_step(self): # redefine $CFLAGS/$CXXFLAGS via options to build command ('make') cflags = os.getenv('CFLAGS') - cxxflags = "%s -DGC_DEFAULT_PLATFORM=\\'%s\\'" % (os.getenv('CXXFLAGS'), self.cfg['default_platform']) + cxxflags = "%s -DGC_DEFAULT_PLATFORM='\\\"%s\\\"'" % (os.getenv('CXXFLAGS'), self.cfg['default_platform']) if self.toolchain.comp_family() in [toolchain.INTELCOMP]: # make sure GNU macros are defined by Intel compiler cflags += " -gcc" @@ -214,4 +214,6 @@ def sanity_check_step(self): 'files': [os.path.join('bin', subdir, 'Gate')] + extra_files, 'dirs': dirs, } - super(EB_GATE, self).sanity_check_step(custom_paths=custom_paths) + custom_commands = ["gjs -h | grep 'This executable is compiled with %s as default'" + % self.cfg['default_platform']] + super(EB_GATE, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) diff --git a/easybuild/easyblocks/g/gurobi.py b/easybuild/easyblocks/g/gurobi.py index 3d066b96d5..5a0d7d18f3 100644 --- a/easybuild/easyblocks/g/gurobi.py +++ b/easybuild/easyblocks/g/gurobi.py @@ -27,11 +27,13 @@ EasyBuild support for installing Gurobi, implemented as an easyblock @author: Bob Dröge (University of Groningen) +@author: Samuel Moors (Vrije Universiteit Brussel) """ import os from easybuild.easyblocks.generic.pythonpackage import det_pylibdir from easybuild.easyblocks.generic.tarball import Tarball +from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError from easybuild.tools.filetools import copy_file from easybuild.tools.modules import get_software_root @@ -41,15 +43,34 @@ class EB_Gurobi(Tarball): """Support for installing linux64 version of Gurobi.""" + @staticmethod + def extra_options(extra_vars=None): + """Define extra options for Gurobi""" + extra = { + 'copy_license_file': [True, "Copy license_file to installdir", CUSTOM], + } + return Tarball.extra_options(extra_vars=extra) + + def __init__(self, *args, **kwargs): + """Easyblock constructor, define custom class variables specific to Gurobi.""" + super(EB_Gurobi, self).__init__(*args, **kwargs) + + self.license_file = self.cfg['license_file'] + + if self.cfg['copy_license_file']: + self.license_file = os.path.join(self.installdir, 'gurobi.lic') + def install_step(self): """Install Gurobi and license file.""" + # make sure license file is available if self.cfg['license_file'] is None or not os.path.exists(self.cfg['license_file']): raise EasyBuildError("No existing license file specified: %s", self.cfg['license_file']) super(EB_Gurobi, self).install_step() - copy_file(self.cfg['license_file'], os.path.join(self.installdir, 'gurobi.lic')) + if self.cfg['copy_license_file']: + copy_file(self.cfg['license_file'], self.license_file) if get_software_root('Python'): run_cmd("python setup.py install --prefix=%s" % self.installdir) @@ -61,7 +82,10 @@ def sanity_check_step(self): 'dirs': [], } - custom_commands = ["gurobi_cl --help"] + custom_commands = [ + "gurobi_cl --help", + 'test -f $GRB_LICENSE_FILE', + ] if get_software_root('Python'): custom_commands.append("python -c 'import gurobipy'") @@ -72,7 +96,7 @@ def make_module_extra(self): """Custom extra module file entries for Gurobi.""" txt = super(EB_Gurobi, self).make_module_extra() txt += self.module_generator.set_environment('GUROBI_HOME', self.installdir) - txt += self.module_generator.set_environment('GRB_LICENSE_FILE', os.path.join(self.installdir, 'gurobi.lic')) + txt += self.module_generator.set_environment('GRB_LICENSE_FILE', self.license_file) if get_software_root('Python'): txt += self.module_generator.prepend_paths('PYTHONPATH', det_pylibdir()) diff --git a/easybuild/easyblocks/generic/bundle.py b/easybuild/easyblocks/generic/bundle.py index 6f530310b7..bf4c3a684f 100644 --- a/easybuild/easyblocks/generic/bundle.py +++ b/easybuild/easyblocks/generic/bundle.py @@ -30,6 +30,7 @@ @author: Kenneth Hoste (Ghent University) @author: Pieter De Baets (Ghent University) @author: Jens Timmerman (Ghent University) +@author: Jasper Grimm (University of York) """ import copy import os @@ -58,6 +59,8 @@ def extra_options(extra_vars=None): 'altversion': [None, "Software name of dependency to use to define $EBVERSION for this bundle", CUSTOM], 'default_component_specs': [{}, "Default specs to use for every component", CUSTOM], 'components': [(), "List of components to install: tuples w/ name, version and easyblock to use", CUSTOM], + 'sanity_check_components': [[], "List of components for which to run sanity checks", CUSTOM], + 'sanity_check_all_components': [False, "Enable sanity checks for all components", CUSTOM], 'default_easyblock': [None, "Default easyblock to use for components", CUSTOM], }) return EasyBlock.extra_options(extra_vars) @@ -71,6 +74,9 @@ def __init__(self, *args, **kwargs): # list of EasyConfig instances for components self.comp_cfgs = [] + # list of EasyConfig instances of components for which to run sanity checks + self.comp_cfgs_sanity_check = [] + # list of sources for bundle itself *must* be empty if self.cfg['sources']: raise EasyBuildError("List of sources for bundle itself must be empty, found %s", self.cfg['sources']) @@ -83,6 +89,20 @@ def __init__(self, *args, **kwargs): # list of checksums for patches (must be included after checksums for sources) checksums_patches = [] + if self.cfg['sanity_check_components'] and self.cfg['sanity_check_all_components']: + raise EasyBuildError("sanity_check_components and sanity_check_all_components cannot be enabled together") + + # backup and reset general sanity checks from main body of ec, if component-specific sanity checks are enabled + # necessary to avoid: + # - duplicating the general sanity check across all components running sanity checks + # - general sanity checks taking precedence over those defined in a component's easyblock + self.backup_sanity_paths = self.cfg['sanity_check_paths'] + self.backup_sanity_cmds = self.cfg['sanity_check_commands'] + if self.cfg['sanity_check_components'] or self.cfg['sanity_check_all_components']: + # reset general sanity checks, to be restored later + self.cfg['sanity_check_paths'] = {} + self.cfg['sanity_check_commands'] = {} + for comp in self.cfg['components']: comp_name, comp_version, comp_specs = comp[0], comp[1], {} if len(comp) == 3: @@ -178,6 +198,11 @@ def __init__(self, *args, **kwargs): self.cfg.enable_templating = True + # restore general sanity checks if using component-specific sanity checks + if self.cfg['sanity_check_components'] or self.cfg['sanity_check_all_components']: + self.cfg['sanity_check_paths'] = self.backup_sanity_paths + self.cfg['sanity_check_commands'] = self.backup_sanity_cmds + def check_checksums(self): """ Check whether a SHA256 checksum is available for all sources & patches (incl. extensions). @@ -264,6 +289,10 @@ def install_step(self): # location of first unpacked source is used to determine where to apply patch(es) comp.src[-1]['finalpath'] = comp.cfg['start_dir'] + # check if sanity checks are enabled for the component + if self.cfg['sanity_check_all_components'] or comp.cfg['name'] in self.cfg['sanity_check_components']: + self.comp_cfgs_sanity_check.append(comp) + # run relevant steps for step_name in ['patch', 'configure', 'build', 'install']: if step_name in cfg['skipsteps']: @@ -299,7 +328,8 @@ def make_module_extra(self, *args, **kwargs): def sanity_check_step(self, *args, **kwargs): """ - Nothing is being installed, so just being able to load the (fake) module is sufficient + If component sanity checks are enabled, run sanity checks for the desired components listed. + If nothing is being installed, just being able to load the (fake) module is sufficient """ if self.cfg['exts_list'] or self.cfg['sanity_check_paths'] or self.cfg['sanity_check_commands']: super(Bundle, self).sanity_check_step(*args, **kwargs) @@ -308,3 +338,12 @@ def sanity_check_step(self, *args, **kwargs): fake_mod_data = self.load_fake_module(purge=True) self.log.debug("Cleaning up after testing loading of module") self.clean_up_fake_module(fake_mod_data) + + # run sanity checks for specific components + cnt = len(self.comp_cfgs_sanity_check) + for idx, comp in enumerate(self.comp_cfgs_sanity_check): + comp_name, comp_ver = comp.cfg['name'], comp.cfg['version'] + print_msg("sanity checking bundle component %s v%s (%i/%i)...", comp_name, comp_ver, idx + 1, cnt) + self.log.info("Starting sanity check step for component %s v%s", comp_name, comp_ver) + + comp.run_step('sanity_check', [lambda x: x.sanity_check_step]) diff --git a/easybuild/easyblocks/generic/intelbase.py b/easybuild/easyblocks/generic/intelbase.py index 73360a0c53..b19ff93cc8 100644 --- a/easybuild/easyblocks/generic/intelbase.py +++ b/easybuild/easyblocks/generic/intelbase.py @@ -107,7 +107,7 @@ def __init__(self, *args, **kwargs): self.home_subdir = os.path.join(os.getenv('HOME'), 'intel') common_tmp_dir = os.path.dirname(tempfile.gettempdir()) # common tmp directory, same across nodes - self.home_subdir_local = os.path.join(common_tmp_dir, os.getenv('USER'), 'easybuild_intel') + self.home_subdir_local = os.path.join(common_tmp_dir, os.environ.get('USER', 'nouser'), 'easybuild_intel') self.install_components = None diff --git a/easybuild/easyblocks/i/intel_compilers.py b/easybuild/easyblocks/i/intel_compilers.py index 9b40365ded..716eb8bf6f 100644 --- a/easybuild/easyblocks/i/intel_compilers.py +++ b/easybuild/easyblocks/i/intel_compilers.py @@ -110,7 +110,13 @@ def sanity_check_step(self): all_compiler_cmds = classic_compiler_cmds + oneapi_compiler_cmds custom_commands = ["which %s" % c for c in all_compiler_cmds] - custom_commands.extend("%s --version | grep %s" % (c, self.version) for c in all_compiler_cmds) + + # only for 2021.x versions do all compiler commands have the expected version; + # for example: for 2022.0.1, icc has version 2021.5.0, icpx has 2022.0.0 + if LooseVersion(self.version) >= LooseVersion('2022.0'): + custom_commands.extend("%s --version" % c for c in all_compiler_cmds) + else: + custom_commands.extend("%s --version | grep %s" % (c, self.version) for c in all_compiler_cmds) super(EB_intel_minus_compilers, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) diff --git a/easybuild/easyblocks/i/itac.py b/easybuild/easyblocks/i/itac.py index 3db04ed5ae..c1bbaee33d 100644 --- a/easybuild/easyblocks/i/itac.py +++ b/easybuild/easyblocks/i/itac.py @@ -55,15 +55,25 @@ def extra_options(): } return IntelBase.extra_options(extra_vars) - def install_step(self): + def prepare_step(self, *args, **kwargs): """ - Actual installation + Custom prepare step for itac: don't require runtime license for oneAPI versions (>= 2021) + """ + if LooseVersion(self.version) >= LooseVersion('2021'): + kwargs['requires_runtime_license'] = False + + super(EB_itac, self).prepare_step(*args, **kwargs) + + def install_step_classic(self): + """ + Actual installation for versions prior to 2021.x + - create silent cfg file - execute command """ if LooseVersion(self.version) >= LooseVersion('8.1'): - super(EB_itac, self).install_step(silent_cfg_names_map=None) + super(EB_itac, self).install_step_classic(silent_cfg_names_map=None) # itac v9.0.1 installer create itac/ subdir, so stuff needs to be moved afterwards if LooseVersion(self.version) >= LooseVersion('9.0'): @@ -98,6 +108,34 @@ def install_step(self): run_cmd(cmd, log_all=True, simple=True) + def install_step_oneapi(self, *args, **kwargs): + """ + Actual installation for versions 2021.x onwards. + """ + # require that EULA is accepted + intel_eula_url = 'https://software.intel.com/content/www/us/en/develop/articles/end-user-license-agreement.html' + self.check_accepted_eula(name='Intel-oneAPI', more_info=intel_eula_url) + + # exactly one "source" file is expected: the (offline) installation script + if len(self.src) == 1: + install_script = self.src[0]['name'] + else: + src_fns = ', '.join([x['name'] for x in self.src]) + raise EasyBuildError("Expected to find exactly one 'source' file (installation script): %s", src_fns) + + cmd = ' '.join([ + "sh %s" % install_script, + '-a', + '-s', + "--eula accept", + "--install-dir=%s" % self.installdir, + ]) + + run_cmd(cmd, log_all=True, simple=True) + + # itac installer create itac/ subdir, so stuff needs to be moved afterwards + super(EB_itac, self).move_after_install() + def sanity_check_step(self): """Custom sanity check paths for ITAC.""" diff --git a/easybuild/easyblocks/l/libint.py b/easybuild/easyblocks/l/libint.py index 945d97d1b3..21ca74c18b 100644 --- a/easybuild/easyblocks/l/libint.py +++ b/easybuild/easyblocks/l/libint.py @@ -32,7 +32,8 @@ import os.path from distutils.version import LooseVersion -from easybuild.easyblocks.generic.configuremake import ConfigureMake +from easybuild.easyblocks.generic.configuremake import DEFAULT_CONFIGURE_CMD, ConfigureMake +from easybuild.easyblocks.generic.cmakemake import CMakeMake from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError, print_msg from easybuild.tools.filetools import change_dir, extract_file @@ -40,7 +41,7 @@ from easybuild.tools.systemtools import get_shared_lib_ext -class EB_Libint(ConfigureMake): +class EB_Libint(CMakeMake): @staticmethod def extra_options(): @@ -49,28 +50,11 @@ def extra_options(): 'libint_compiler_configopts': [True, "Configure options for Libint compiler", CUSTOM], 'with_fortran': [False, "Enable Fortran support", CUSTOM], } - return ConfigureMake.extra_options(extra_vars) + return CMakeMake.extra_options(extra_vars) def configure_step(self): """Add some extra configure options.""" - # also build shared libraries (not enabled by default) - self.cfg.update('configopts', "--enable-shared") - - if self.toolchain.options['pic']: - # Enforce consistency. - self.cfg.update('configopts', "--with-pic") - - if LooseVersion(self.version) >= LooseVersion('2.0') and LooseVersion(self.version) < LooseVersion('2.1'): - # the code in libint is automatically generated and hence it is in some - # parts so complex that -O2 or -O3 compiler optimization takes forever - self.cfg.update('configopts', "--with-cxx-optflags='-O1'") - - elif LooseVersion(self.version) >= LooseVersion('2.1'): - # pass down $CXXFLAGS to --with-cxxgen-optflags configure option; - # mainly to avoid warning about it not being set (but $CXXFLAGS is picked up anyway in practice) - self.cfg.update('configopts', "--with-cxxgen-optflags='%s'" % os.getenv('CXXFLAGS')) - if LooseVersion(self.version) >= LooseVersion('2.6.0'): # Libint 2.6.0 requires first compiling the Libint compiler, # by running configure with appropriate options, followed by 'make export' @@ -102,12 +86,42 @@ def configure_step(self): else: raise EasyBuildError("Could not find generated source tarball after 'make export'!") - # --enable-fortran is only a known configure option for Libint library, not for Libint compiler, - # so only add --enable-fortran *after* configuring & generating Libint compiler - if self.cfg['with_fortran']: - self.cfg.update('configopts', '--enable-fortran') + # Libint < 2.7.0 can be configured using configure script, + # Libint >= 2.7.0 should be configured via cmake + if LooseVersion(self.version) < LooseVersion('2.7.0'): + + # also build shared libraries (not enabled by default) + self.cfg.update('configopts', "--enable-shared") + + if self.toolchain.options['pic']: + # Enforce consistency. + self.cfg.update('configopts', "--with-pic") + + if LooseVersion(self.version) >= LooseVersion('2.0') and LooseVersion(self.version) < LooseVersion('2.1'): + # the code in libint is automatically generated and hence it is in some + # parts so complex that -O2 or -O3 compiler optimization takes forever + self.cfg.update('configopts', "--with-cxx-optflags='-O1'") + + elif LooseVersion(self.version) >= LooseVersion('2.1'): + # pass down $CXXFLAGS to --with-cxxgen-optflags configure option; + # mainly to avoid warning about it not being set (but $CXXFLAGS is picked up anyway in practice) + self.cfg.update('configopts', "--with-cxxgen-optflags='%s'" % os.getenv('CXXFLAGS')) + + # --enable-fortran is only a known configure option for Libint library, not for Libint compiler, + # so only add --enable-fortran *after* configuring & generating Libint compiler + if self.cfg['with_fortran']: + self.cfg.update('configopts', '--enable-fortran') + + self.cfg['configure_cmd'] = DEFAULT_CONFIGURE_CMD + ConfigureMake.configure_step(self) + + else: + if self.cfg['with_fortran']: + self.cfg.update('configopts', '-DENABLE_FORTRAN=ON') - super(EB_Libint, self).configure_step() + # specify current directory as source directory (that contains CMakeLists.txt), + # since that's the path to the unpacked source tarball for Libint library (created by 'make export') + super(EB_Libint, self).configure_step(srcdir=os.getcwd()) def test_step(self): """Run Libint test suite for recent versions""" diff --git a/easybuild/easyblocks/m/mathematica.py b/easybuild/easyblocks/m/mathematica.py index a6cd3e70e6..40dd1808cf 100644 --- a/easybuild/easyblocks/m/mathematica.py +++ b/easybuild/easyblocks/m/mathematica.py @@ -66,6 +66,10 @@ def install_step(self): orig_display = os.environ.pop('DISPLAY', None) install_script_glob = '%s_%s_LINUX*.sh' % (self.name, self.version) + # Starting at V13, Mathematica have renamed their install file... + if LooseVersion(self.version) >= LooseVersion("13"): + install_script_glob = '%s_%s_BNDL_LINUX*.sh' % (self.name, self.version) + matches = glob.glob(install_script_glob) if len(matches) == 1: install_script = matches[0] diff --git a/easybuild/easyblocks/n/namd.py b/easybuild/easyblocks/n/namd.py index a7a7cdaa1f..533048b5cd 100644 --- a/easybuild/easyblocks/n/namd.py +++ b/easybuild/easyblocks/n/namd.py @@ -86,6 +86,20 @@ def extract_step(self): srcdir = extract_file(self.charm_tarballs[0], os.getcwd(), change_into_dir=False) change_dir(srcdir) + def patch_step(self, *args, **kwargs): + """Patch scripts to avoid using hardcoded /bin/csh.""" + super(EB_NAMD, self).patch_step(*args, **kwargs) + + self.charm_dir = self.charm_tarballs[0][:-4] + + charm_config = os.path.join(self.charm_dir, 'src', 'scripts', 'configure') + apply_regex_substitutions(charm_config, [(r'SHELL=/bin/csh', 'SHELL=$(which csh)')]) + + for csh_script in [os.path.join('plugins', 'import_tree'), os.path.join('psfgen', 'import_tree'), + os.path.join(self.charm_dir, 'src', 'QuickThreads', 'time', 'raw')]: + if os.path.exists(csh_script): + apply_regex_substitutions(csh_script, [(r'^#!\s*/bin/csh\s*$', '#!/usr/bin/env csh')]) + def configure_step(self): """Custom configure step for NAMD, we build charm++ first (if required).""" diff --git a/easybuild/easyblocks/p/perl.py b/easybuild/easyblocks/p/perl.py index 1ee8adf843..414dc525d9 100644 --- a/easybuild/easyblocks/p/perl.py +++ b/easybuild/easyblocks/p/perl.py @@ -28,11 +28,15 @@ @author: Jens Timmerman (Ghent University) @author: Kenneth Hoste (Ghent University) """ +from distutils.version import LooseVersion +import glob import os +import stat from easybuild.easyblocks.generic.configuremake import ConfigureMake from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.config import build_option +from easybuild.tools.filetools import adjust_permissions from easybuild.tools.environment import setvar, unset_env_vars from easybuild.tools.modules import get_software_root from easybuild.tools.py2vs3 import string_type @@ -119,8 +123,17 @@ def test_step(self): """Test Perl build via 'make test'.""" # allow escaping with runtest = False if self.cfg['runtest'] is None or self.cfg['runtest']: + parallel = self.cfg['parallel'] if isinstance(self.cfg['runtest'], string_type): cmd = "make %s" % self.cfg['runtest'] + elif parallel and LooseVersion(self.version) >= LooseVersion('5.30.0'): + # run tests in parallel, see https://perldoc.perl.org/perlhack#Parallel-tests; + # only do this for Perl 5.30 and newer (conservative choice, actually supported in Perl >= 5.10.1) + cmd = ' '.join([ + 'TEST_JOBS=%s' % parallel, + 'PERL_TEST_HARNESS_ASAP=1', + "make -j %s test_harness" % parallel, + ]) else: cmd = "make test" @@ -143,6 +156,30 @@ def prepare_for_extensions(self): # from specified sysroot rather than from host OS setvar('OPENSSL_PREFIX', sysroot) + def post_install_step(self, *args, **kwargs): + """ + Custom post-installation step for Perl: avoid excessive long shebang lines in Perl scripts. + """ + + # if path to install directory is too long, we need to patch the shebang line in all Perl scripts; + # there is a strict limit on the allowed shebang length (~128 characters) + bin_path = os.path.join(self.installdir, 'bin') + bin_perl = os.path.join(bin_path, 'perl') + bin_perl_len = len(bin_perl) + if bin_perl_len > 110: + self.log.info("Path to 'perl' (%s) is too long (%d), we need to patch the shebang line in bin/*...", + bin_perl, bin_perl_len) + + # first make sure that files in bin/ are writable for current user + bin_paths = glob.glob(os.path.join(bin_path, '*')) + for bin_path in bin_paths: + adjust_permissions(bin_path, stat.S_IWUSR, add=True, relative=True) + + # specify pattern for paths (relative to install dir) of files for which shebang should be patched + self.cfg['fix_perl_shebang_for'] = 'bin/*' + + super(EB_Perl, self).post_install_step(*args, **kwargs) + def sanity_check_step(self): """Custom sanity check for Perl.""" majver = self.version.split('.')[0] diff --git a/easybuild/easyblocks/s/siesta.py b/easybuild/easyblocks/s/siesta.py index 6bfaf99ac2..923c10aa36 100644 --- a/easybuild/easyblocks/s/siesta.py +++ b/easybuild/easyblocks/s/siesta.py @@ -71,7 +71,8 @@ def configure_step(self): arch_make = os.path.join(obj_dir, 'arch.make') bindir = os.path.join(start_dir, 'bin') - loose_ver = LooseVersion(self.version) + version = self.version.replace('-b', '.0.2.').replace('-MaX-', '.') + loose_ver = LooseVersion(version) par = '' if loose_ver >= LooseVersion('4.1'): @@ -125,7 +126,7 @@ def configure_step(self): # Populate start_dir with makefiles run_cmd(os.path.join(start_dir, 'Src', 'obj_setup.sh'), log_all=True, simple=True, log_output=True) - if loose_ver < LooseVersion('4.1-b2'): + if loose_ver < LooseVersion('4.1.0.2.2'): # MPI? if self.toolchain.options.get('usempi', None): self.cfg.update('configopts', '--enable-mpi') @@ -270,6 +271,13 @@ def configure_step(self): makefile = os.path.join(start_dir, 'Util', 'TS', 'tshs2tshs', 'Makefile') apply_regex_substitutions(makefile, regex_subs_TS) + if self.version != '4.1-MaX-1.0': + regex_subs_Gen_basis = [ + (r"^(INCFLAGS.*)$", r"\1 -I%s" % obj_dir), + ] + makefile = os.path.join(start_dir, 'Util', 'Gen-basis', 'Makefile') + apply_regex_substitutions(makefile, regex_subs_Gen_basis) + if loose_ver >= LooseVersion('4'): # SUFFIX rules in wrong place regex_subs_suffix = [ @@ -293,7 +301,7 @@ def configure_step(self): # remove clean at the end of default target # And yes, they are re-introducing this bug. is_ver40_to_401 = loose_ver >= LooseVersion('4.0') and loose_ver < LooseVersion('4.0.2') - if (is_ver40_to_401 or loose_ver == LooseVersion('4.1-b3')): + if (is_ver40_to_401 or loose_ver == LooseVersion('4.1.0.2.3')): makefile = os.path.join(start_dir, 'Util', 'SiestaSubroutine', 'SimpleTest', 'Src', 'Makefile') apply_regex_substitutions(makefile, [(r"simple_mpi_parallel clean", r"simple_mpi_parallel")]) makefile = os.path.join(start_dir, 'Util', 'SiestaSubroutine', 'ProtoNEB', 'Src', 'Makefile') @@ -416,7 +424,7 @@ def configure_step(self): change_dir(obj_dir) ts_clean_target = 'clean' - if loose_ver >= LooseVersion('4.1-b4'): + if loose_ver >= LooseVersion('4.1.0.2.4'): ts_clean_target += '-transiesta' run_cmd('make %s' % ts_clean_target, log_all=True, simple=True, log_output=True) diff --git a/easybuild/easyblocks/s/suitesparse.py b/easybuild/easyblocks/s/suitesparse.py index a8e8f803f1..97dd0b7be7 100644 --- a/easybuild/easyblocks/s/suitesparse.py +++ b/easybuild/easyblocks/s/suitesparse.py @@ -30,6 +30,7 @@ @author: Kenneth Hoste (Ghent University) @author: Pieter De Baets (Ghent University) @author: Jens Timmerman (Ghent University) +@author: Damian Alvarez (Forschungszentrum Juelich GmbH) """ import fileinput import re @@ -78,6 +79,15 @@ def configure_step(self): self.cfg.update('buildopts', 'BLAS="%s"' % os.getenv('LIBBLAS_MT')) self.cfg.update('buildopts', 'LAPACK="%s"' % os.getenv('LIBLAPACK_MT')) + # Get CUDA and set it up appropriately + cuda = get_software_root('CUDA') + if cuda: + cuda_cc_space_sep = self.cfg.get_cuda_cc_template_value('cuda_cc_space_sep').replace('.', '').split() + nvcc_gencode = ' '.join(['-gencode=arch=compute_' + x + ',code=sm_' + x for x in cuda_cc_space_sep]) + cfgvars.update({ + 'NVCCFLAGS': ' '.join(['-Xcompiler', '-fPIC', '-O3', nvcc_gencode]), + }) + # Get METIS or ParMETIS settings metis = get_software_root('METIS') parmetis = get_software_root('ParMETIS') @@ -111,15 +121,18 @@ def configure_step(self): try: for line in fileinput.input(fp, inplace=1, backup='.orig'): for (var, val) in list(cfgvars.items()): - orig_line = line - # for variables in cfgvars, substiture lines assignment - # in the file, whatever they are, by assignments to the - # values in cfgvars - line = re.sub(r"^\s*(%s\s*=\s*).*\n$" % var, - r"\1 %s # patched by EasyBuild\n" % val, - line) - if line != orig_line: - cfgvars.pop(var) + # Let's overwrite NVCCFLAGS at the end, since the line breaks and the fact that it appears multiple + # times makes it tricky to handle it properly + if var != 'NVCCFLAGS': + orig_line = line + # for variables in cfgvars, substiture lines assignment + # in the file, whatever they are, by assignments to the + # values in cfgvars + line = re.sub(r"^\s*(%s\s*=\s*).*\n$" % var, + r"\1 %s # patched by EasyBuild\n" % val, + line) + if line != orig_line: + cfgvars.pop(var) sys.stdout.write(line) except IOError as err: raise EasyBuildError("Failed to patch %s in: %s", fp, err) diff --git a/easybuild/easyblocks/w/wps.py b/easybuild/easyblocks/w/wps.py index 1149ba4de2..ec50fa8ef4 100644 --- a/easybuild/easyblocks/w/wps.py +++ b/easybuild/easyblocks/w/wps.py @@ -381,7 +381,7 @@ def sanity_check_step(self): def make_module_req_guess(self): """Make sure PATH and LD_LIBRARY_PATH are set correctly.""" return { - 'PATH': [self.wps_subdir], + 'PATH': [self.wps_subdir, os.path.join(self.wps_subdir, 'util')], 'LD_LIBRARY_PATH': [self.wps_subdir], 'MANPATH': [], } diff --git a/easybuild/easyblocks/w/wrf.py b/easybuild/easyblocks/w/wrf.py index 3751a66c04..7f62d68b18 100644 --- a/easybuild/easyblocks/w/wrf.py +++ b/easybuild/easyblocks/w/wrf.py @@ -44,7 +44,8 @@ from easybuild.framework.easyconfig import CUSTOM, MANDATORY from easybuild.tools.build_log import EasyBuildError from easybuild.tools.config import build_option -from easybuild.tools.filetools import apply_regex_substitutions, change_dir, patch_perl_script_autoflush, read_file +from easybuild.tools.filetools import apply_regex_substitutions, change_dir +from easybuild.tools.filetools import patch_perl_script_autoflush, read_file, which from easybuild.tools.filetools import remove_file, symlink from easybuild.tools.modules import get_software_root from easybuild.tools.run import run_cmd, run_cmd_qa @@ -252,11 +253,26 @@ def build_step(self): if par: self.par = "-j %s" % par - # fix compile script shebang + # fix compile script shebang to use provided tcsh cmpscript = os.path.join(self.start_dir, 'compile') - cmpsh_root = get_software_root('tcsh') - if cmpsh_root: - regex_subs = [('/bin/csh', os.path.join(cmpsh_root, 'bin/tcsh'))] + tcsh_root = get_software_root('tcsh') + if tcsh_root: + tcsh_path = os.path.join(tcsh_root, 'bin', 'tcsh') + # avoid using full path to tcsh if possible, since it may be too long to be used as shebang line + which_tcsh = which('tcsh') + if which_tcsh and os.path.samefile(which_tcsh, tcsh_path): + env_path = os.path.join('/usr', 'bin', 'env') + # use env command from alternate sysroot, if available + sysroot = build_option('sysroot') + if sysroot: + sysroot_env_path = os.path.join(sysroot, 'usr', 'bin', 'env') + if os.path.exists(sysroot_env_path): + env_path = sysroot_env_path + new_shebang = env_path + ' tcsh' + else: + new_shebang = tcsh_path + + regex_subs = [('^#!/bin/csh.*', '#!' + new_shebang)] apply_regex_substitutions(cmpscript, regex_subs) # build wrf