# Copyright 2013-2020 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) from spack import * import glob import sys import os class Metis(Package): """METIS is a set of serial programs for partitioning graphs, partitioning finite element meshes, and producing fill reducing orderings for sparse matrices. The algorithms implemented in METIS are based on the multilevel recursive-bisection, multilevel k-way, and multi-constraint partitioning schemes.""" # # the previous metis website http://glaros.dtc.umn.edu/gkhome/metis/metis # no longer exists. This is a github mirror that provides metis 5.1.0 # homepage = "https://github.com/scivision/METIS/" url = "https://github.com/scivision/METIS/raw/master/metis-5.1.0.tar.gz" version('5.1.0', sha256='76faebe03f6c963127dbb73c13eab58c9a3faeae48779f049066a21c087c5db2') variant('shared', default=True, description='Enables the build of shared libraries.') variant('gdb', default=False, description='Enables gdb support (version 5+).') variant('int64', default=False, description='Sets the bit width of METIS\'s index type to 64.') variant('real64', default=False, description='Sets the bit width of METIS\'s real type to 64.') # For Metis version 5:, the build system is CMake, provide the # `build_type` variant. variant('build_type', default='Release', description='The build type for the installation (only Debug or' ' Release allowed for version 4).', values=('Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel')) # Prior to version 5, the (non-cmake) build system only knows about # 'build_type=Debug|Release'. conflicts('@:4.999', when='build_type=RelWithDebInfo') conflicts('@:4.999', when='build_type=MinSizeRel') conflicts('@:4.999', when='+gdb') conflicts('@:4.999', when='+int64') conflicts('@:4.999', when='+real64') depends_on('cmake@2.8:', when='@5:', type='build') patch('install_gklib_defs_rename.patch', when='@5:') patch('gklib_nomisleadingindentation_warning.patch', when='@5: %gcc@6:') @when('@5:') def patch(self): source_path = self.stage.source_path metis_header = FileFilter(join_path(source_path, 'include', 'metis.h')) metis_header.filter( r'(\b)(IDXTYPEWIDTH )(\d+)(\b)', r'\1\2{0}\4'.format('64' if '+int64' in self.spec else '32'), ) metis_header.filter( r'(\b)(REALTYPEWIDTH )(\d+)(\b)', r'\1\2{0}\4'.format('64' if '+real64' in self.spec else '32'), ) # Make clang 7.3 happy. # Prevents "ld: section __DATA/__thread_bss extends beyond end of file" # See upstream LLVM issue https://llvm.org/bugs/show_bug.cgi?id=27059 # and https://github.com/Homebrew/homebrew-science/blob/master/metis.rb if self.spec.satisfies('%clang@7.3.0'): filter_file('#define MAX_JBUFS 128', '#define MAX_JBUFS 24', join_path(source_path, 'GKlib', 'error.c')) @when('@:4') def install(self, spec, prefix): # Process library spec and options options = [] if '+shared' in spec: options.append('COPTIONS={0}'.format(self.compiler.cc_pic_flag)) if spec.variants['build_type'].value == 'Debug': options.append('OPTFLAGS=-g -O0') make(*options) # Compile and install library files ccompile = Executable(self.compiler.cc) mkdir(prefix.bin) binfiles = ('pmetis', 'kmetis', 'oemetis', 'onmetis', 'partnmesh', 'partdmesh', 'mesh2nodal', 'mesh2dual', 'graphchk') for binfile in binfiles: install(binfile, prefix.bin) mkdir(prefix.lib) install('libmetis.a', prefix.lib) mkdir(prefix.include) for h in glob.glob(join_path('Lib', '*.h')): install(h, prefix.include) mkdir(prefix.share) sharefiles = (('Graphs', '4elt.graph'), ('Graphs', 'metis.mesh'), ('Graphs', 'test.mgraph')) for sharefile in tuple(join_path(*sf) for sf in sharefiles): install(sharefile, prefix.share) if '+shared' in spec: shared_flags = [self.compiler.cc_pic_flag, '-shared'] if sys.platform == 'darwin': shared_suffix = 'dylib' shared_flags.extend(['-Wl,-all_load', 'libmetis.a']) else: shared_suffix = 'so' shared_flags.extend(['-Wl,-whole-archive', 'libmetis.a', '-Wl,-no-whole-archive']) shared_out = '%s/libmetis.%s' % (prefix.lib, shared_suffix) shared_flags.extend(['-o', shared_out]) ccompile(*shared_flags) # Set up and run tests on installation ccompile('-I%s' % prefix.include, '-L%s' % prefix.lib, (self.compiler.cc_rpath_arg + prefix.lib if '+shared' in spec else ''), join_path('Programs', 'io.o'), join_path('Test', 'mtest.c'), '-o', '%s/mtest' % prefix.bin, '-lmetis', '-lm') if self.run_tests: test_bin = lambda testname: join_path(prefix.bin, testname) test_graph = lambda graphname: join_path(prefix.share, graphname) graph = test_graph('4elt.graph') os.system('%s %s' % (test_bin('mtest'), graph)) os.system('%s %s 40' % (test_bin('kmetis'), graph)) os.system('%s %s' % (test_bin('onmetis'), graph)) graph = test_graph('test.mgraph') os.system('%s %s 2' % (test_bin('pmetis'), graph)) os.system('%s %s 2' % (test_bin('kmetis'), graph)) os.system('%s %s 5' % (test_bin('kmetis'), graph)) graph = test_graph('metis.mesh') os.system('%s %s 10' % (test_bin('partnmesh'), graph)) os.system('%s %s 10' % (test_bin('partdmesh'), graph)) os.system('%s %s' % (test_bin('mesh2dual'), graph)) # FIXME: The following code should replace the testing code in the # block above since it causes installs to fail when one or more of # the Metis tests fail, but it currently doesn't work because the # 'mtest', 'onmetis', and 'partnmesh' tests return error codes that # trigger false positives for failure. """ Executable(test_bin('mtest'))(test_graph('4elt.graph')) Executable(test_bin('kmetis'))(test_graph('4elt.graph'), '40') Executable(test_bin('onmetis'))(test_graph('4elt.graph')) Executable(test_bin('pmetis'))(test_graph('test.mgraph'), '2') Executable(test_bin('kmetis'))(test_graph('test.mgraph'), '2') Executable(test_bin('kmetis'))(test_graph('test.mgraph'), '5') Executable(test_bin('partnmesh'))(test_graph('metis.mesh'), '10') Executable(test_bin('partdmesh'))(test_graph('metis.mesh'), '10') Executable(test_bin('mesh2dual'))(test_graph('metis.mesh')) """ @when('@5:') def install(self, spec, prefix): source_directory = self.stage.source_path build_directory = join_path(source_directory, 'build') options = std_cmake_args[:] options.append('-DGKLIB_PATH:PATH=%s/GKlib' % source_directory) options.append('-DCMAKE_INSTALL_NAME_DIR:PATH=%s/lib' % prefix) # Normally this is available via the 'CMakePackage' object, but metis # IS-A 'Package' (not a 'CMakePackage') to support non-cmake metis@:5. build_type = spec.variants['build_type'].value options.extend(['-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type)]) if '+shared' in spec: options.append('-DSHARED:BOOL=ON') else: # Remove all RPATH options # (RPATHxxx options somehow trigger cmake to link dynamically) rpath_options = [] for o in options: if o.find('RPATH') >= 0: rpath_options.append(o) for o in rpath_options: options.remove(o) if '+gdb' in spec: options.append('-DGDB:BOOL=ON') with working_dir(build_directory, create=True): cmake(source_directory, *options) make() make('install') # install GKlib headers, which will be needed for ParMETIS gklib_dist = join_path(prefix.include, 'GKlib') mkdirp(gklib_dist) hfiles = glob.glob(join_path(source_directory, 'GKlib', '*.h')) for hfile in hfiles: install(hfile, gklib_dist) if self.run_tests: # FIXME: On some systems, the installed binaries for METIS cannot # be executed without first being read. ls = which('ls') ls('-a', '-l', prefix.bin) for f in ['4elt', 'copter2', 'mdual']: graph = join_path(source_directory, 'graphs', '%s.graph' % f) Executable(join_path(prefix.bin, 'graphchk'))(graph) Executable(join_path(prefix.bin, 'gpmetis'))(graph, '2') Executable(join_path(prefix.bin, 'ndmetis'))(graph) graph = join_path(source_directory, 'graphs', 'test.mgraph') Executable(join_path(prefix.bin, 'gpmetis'))(graph, '2') graph = join_path(source_directory, 'graphs', 'metis.mesh') Executable(join_path(prefix.bin, 'mpmetis'))(graph, '2')