diff --git a/easybuild/easyblocks/s/suitesparse.py b/easybuild/easyblocks/s/suitesparse.py index f25fc66785..7afd352080 100644 --- a/easybuild/easyblocks/s/suitesparse.py +++ b/easybuild/easyblocks/s/suitesparse.py @@ -35,14 +35,12 @@ import fileinput import re import os -import shutil import sys -import stat from distutils.version import LooseVersion from easybuild.easyblocks.generic.configuremake import ConfigureMake from easybuild.tools.build_log import EasyBuildError -from easybuild.tools.filetools import mkdir, write_file, adjust_permissions, copy_dir +from easybuild.tools.filetools import mkdir, write_file from easybuild.tools.modules import get_software_root from easybuild.tools.modules import get_software_libdir from easybuild.tools.systemtools import get_shared_lib_ext @@ -61,24 +59,22 @@ def configure_step(self): if LooseVersion(self.version) < LooseVersion('4.0'): self.config_name = 'UFconfig' - else: + elif LooseVersion(self.version) < LooseVersion('6.0.0'): self.config_name = 'SuiteSparse_config' + else: + # config file is removed after v6.0.0 + self.config_name = '' cfgvars = { - 'CC': os.getenv('MPICC'), + 'CC': os.getenv('CC'), 'CFLAGS': os.getenv('CFLAGS'), - 'CXX': os.getenv('MPICXX'), - 'F77': os.getenv('MPIF77'), + 'CXX': os.getenv('CXX'), + 'F77': os.getenv('F77'), 'F77FLAGS': os.getenv('F77FLAGS'), + 'BLAS': os.getenv('LIBBLAS_MT'), + 'LAPACK': os.getenv('LIBLAPACK_MT'), } - # avoid that (system) Intel compilers are always considered - self.cfg.update('buildopts', 'AUTOCC=no') - - # Set BLAS and LAPACK libraries as specified in SuiteSparse README.txt - 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: @@ -91,125 +87,102 @@ def configure_step(self): # Get METIS or ParMETIS settings metis = get_software_root('METIS') parmetis = get_software_root('ParMETIS') - if parmetis: - metis_path = parmetis - metis_include = os.path.join(parmetis, 'include') - metis_libs = os.path.join(parmetis, get_software_libdir('ParMETIS'), 'libmetis.a') - - elif metis: - metis_path = metis - metis_include = os.path.join(metis, 'include') - metis_libs = os.path.join(metis, get_software_libdir('METIS'), 'libmetis.a') + if parmetis or metis: + if parmetis: + metis_name = 'ParMETIS' + else: + metis_name = 'METIS' + metis_path = get_software_root(metis_name) + metis_include = os.path.join(metis_path, 'include') + metis_libs = os.path.join(metis_path, get_software_libdir(metis_name), 'libmetis.a') else: - raise EasyBuildError("Neither METIS or ParMETIS module loaded.") + self.log.info("Use METIS built in SuiteSparse") + # raise EasyBuildError("Neither METIS or ParMETIS module loaded.") - if LooseVersion(self.version) >= LooseVersion('4.5.1'): + # config file can catch environment variables after v4.5.0 + if LooseVersion(self.version) < LooseVersion('4.5.0'): cfgvars.update({ - 'MY_METIS_LIB': metis_libs, - 'MY_METIS_INC': metis_include, - }) - else: - cfgvars.update({ - 'METIS_PATH': metis_path, - 'METIS': metis_libs, + 'INSTALL_LIB': os.path.join(self.installdir, 'lib'), + 'INSTALL_INCLUDE': os.path.join(self.installdir, 'include'), }) + if parmetis or metis: + cfgvars.update({ + 'METIS_PATH': metis_path, + 'METIS': metis_libs, + }) - # patch file - fp = os.path.join(self.cfg['start_dir'], self.config_name, '%s.mk' % self.config_name) - - try: - for line in fileinput.input(fp, inplace=1, backup='.orig'): - for (var, val) in list(cfgvars.items()): - # 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) - - # add remaining entries at the end - if cfgvars: - cfgtxt = '# lines below added automatically by EasyBuild\n' - cfgtxt += '\n'.join(["%s = %s" % (var, val) for (var, val) in cfgvars.items()]) - write_file(fp, cfgtxt, append=True) + # patch file + fp = os.path.join(self.cfg['start_dir'], self.config_name, '%s.mk' % self.config_name) - def install_step(self): - """Install by copying the contents of the builddir to the installdir (preserving permissions)""" - for x in os.listdir(self.cfg['start_dir']): - src = os.path.join(self.cfg['start_dir'], x) - dst = os.path.join(self.installdir, x) try: - if os.path.isdir(src): - # symlink points to CUDA folder that is - # not created for non GPU nodes. shutil - # throws an error in this case. - copy_dir(src, dst, symlinks=True) - # symlink - # - dst/Lib to dst/lib - # - dst/Include to dst/include - for c in ['Lib', 'Include']: - nsrc = os.path.join(dst, c) - ndst = os.path.join(dst, c.lower()) - if os.path.exists(nsrc): - os.symlink(nsrc, ndst) - # enable r-x permissions for group/others - perms = stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH - adjust_permissions(dst, perms, add=True, recursive=True, onlydirs=True) + for line in fileinput.input(fp, inplace=1, backup='.orig'): + for (var, val) in list(cfgvars.items()): + # 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 + # path variables are also moved to the end + if var not in ['NVCCFLAGS', 'INSTALL_LIB', 'INSTALL_INCLUDE', 'METIS_PATH']: + 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) + + # add remaining entries at the end + if cfgvars: + cfgtxt = '# lines below added automatically by EasyBuild\n' + cfgtxt += '\n'.join(["%s = %s" % (var, val) for (var, val) in cfgvars.items()]) + write_file(fp, cfgtxt, append=True) + + elif LooseVersion(self.version) < LooseVersion('6.0.0'): + # avoid that (system) Intel compilers are always considered + self.cfg.update('prebuildopts', 'AUTOCC=no') + + # Set BLAS and LAPACK libraries as specified in SuiteSparse README.txt + self.cfg.update('buildopts', 'BLAS="%s"' % cfgvars.get('BLAS')) + self.cfg.update('buildopts', 'LAPACK="%s"' % cfgvars.get('LAPACK')) + + self.cfg.update('installopts', 'INSTALL="%s"' % self.installdir) + self.cfg.update('installopts', 'BLAS="%s"' % cfgvars.get('BLAS')) + self.cfg.update('installopts', 'LAPACK="%s"' % cfgvars.get('LAPACK')) + + if LooseVersion(self.version) >= LooseVersion('5.1.2'): + # graphblas exists, needs cmake + # v5.0.0 until v5.1.2 has no CMAKE_OPTIONS to set + # probably need patch + self.cfg.update('preinstallopts', 'CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=%s"' % self.installdir) + + # set METIS library + if parmetis or metis: + if LooseVersion(self.version) == LooseVersion('4.5.0'): + self.cfg.update('buildopts', 'METIS_PATH="%s"' % metis_path) + self.cfg.update('installopts', 'METIS_PATH="%s"' % metis_path) else: - shutil.copy2(src, dst) - except OSError as err: - raise EasyBuildError("Copying src %s to dst %s failed: %s", src, dst, err) - - # some extra symlinks are necessary for UMFPACK to work. - paths = [ - os.path.join('AMD', 'include', 'amd.h'), - os.path.join('AMD', 'include', 'amd_internal.h'), - os.path.join(self.config_name, '%s.h' % self.config_name), - os.path.join('AMD', 'lib', 'libamd.a') - ] - for path in paths: - src = os.path.join(self.installdir, path) - dn = path.split(os.path.sep)[-2] - fn = path.split(os.path.sep)[-1] - dstdir = os.path.join(self.installdir, 'UMFPACK', dn) - mkdir(dstdir) - if os.path.exists(src): - try: - os.symlink(src, os.path.join(dstdir, fn)) - except OSError as err: - raise EasyBuildError("Failed to make symbolic link from %s to %s: %s", src, dst, err) - - def make_module_req_guess(self): - """ - Extra path to consider for module file: - * add config dir and include to $CPATH so include files are found - * add UMFPACK and AMD library, and lib dirs to $LD_LIBRARY_PATH - """ - - guesses = super(EB_SuiteSparse, self).make_module_req_guess() - - # Previous versions of SuiteSparse used specific directories for includes and libraries - if LooseVersion(self.version) < LooseVersion('4.5'): - include_dirs = [self.config_name] - ld_library_path = ['AMD/lib', 'BTF/lib', 'CAMD/lib', 'CCOLAMD/lib', 'CHOLAMD/lib', 'CHOLMOD/lib', - 'COLAMD/lib/', 'CSparse/lib', 'CXSparse/lib', 'KLU/lib', 'LDL/lib', 'RBio/lib', - 'UMFPACK/lib', self.config_name] + self.cfg.update('buildopts', 'MY_METIS_LIB="%s"' % metis_libs) + self.cfg.update('buildopts', 'MY_METIS_INC="%s"' % metis_include) + self.cfg.update('installopts', 'MY_METIS_LIB="%s"' % metis_libs) + self.cfg.update('installopts', 'MY_METIS_INC="%s"' % metis_include) - guesses['CPATH'].extend(include_dirs) - guesses['LD_LIBRARY_PATH'].extend(ld_library_path) - guesses['LIBRARY_PATH'].extend(ld_library_path) + else: + # after v6.0.0, no option for metis, its own metis is used anyway + # nothing to do here, set the CMAKE_OPTIONS in easyconfigs + pass + + def install_step(self): + """Install by copying the contents of the builddir to the installdir (preserving permissions)""" + + if LooseVersion(self.version) < LooseVersion('4.5.0'): + mkdir(os.path.join(self.installdir, 'lib')) + mkdir(os.path.join(self.installdir, 'include')) - return guesses + super(EB_SuiteSparse, self).install_step() def sanity_check_step(self): """Custom sanity check for SuiteSparse.""" @@ -219,24 +192,17 @@ def sanity_check_step(self): raise EasyBuildError("SuiteSparse has compiled its own Metis. This will conflict with the Metis build." " The SuiteSparse EasyBlock need to be updated!") + shlib_ext = get_shared_lib_ext() libnames = ['AMD', 'BTF', 'CAMD', 'CCOLAMD', 'CHOLMOD', 'COLAMD', 'CXSparse', 'KLU', 'LDL', 'RBio', 'SPQR', 'UMFPACK'] - libs = [os.path.join(x, 'lib', 'lib%s.a' % x.lower()) for x in libnames] - - if LooseVersion(self.version) < LooseVersion('4.0'): - csparse_dir = 'CSparse3' + if LooseVersion(self.version) < LooseVersion('4.5'): + libs = [os.path.join('lib', 'lib%s.a' % x.lower()) for x in libnames] else: - csparse_dir = 'CSparse' - libs.append(os.path.join(csparse_dir, 'lib', 'libcsparse.a')) - - # Latest version of SuiteSparse also compiles shared library and put them in 'lib' - shlib_ext = get_shared_lib_ext() - if LooseVersion(self.version) >= LooseVersion('4.5.1'): - libs += [os.path.join('lib', 'lib%s.%s' % (x.lower(), shlib_ext)) for x in libnames] + libs = [os.path.join('lib', 'lib%s.%s' % (x.lower(), shlib_ext)) for x in libnames] custom_paths = { 'files': libs, - 'dirs': ['MATLAB_Tools'], + 'dirs': [], } super(EB_SuiteSparse, self).sanity_check_step(custom_paths=custom_paths)