Repository: easybuilders/easybuild-easyblocks

Issue:
cp2k easyblock ignores --optarch=GENERIC
In cp2k.py:438:
optarch = ''
if self.toolchain.options['optarch']:
   optarch = '-xHOST'

and cp2k.py:484:
optarch = ''
if self.toolchain.options['optarch']:
   optarch = '-march=native'

Is this behaviour intended?



Discussion:

Resolving patch:
diff --git a/easybuild/easyblocks/c/cp2k.py b/easybuild/easyblocks/c/cp2k.py
--- a/easybuild/easyblocks/c/cp2k.py
+++ b/easybuild/easyblocks/c/cp2k.py
@@ -32,6 +32,9 @@
 @author: Jens Timmerman (Ghent University)
 @author: Ward Poelmans (Ghent University)
 @author: Luca Marsella (CSCS)
+@author: Damian Alvarez (Forschungszentrum Juelich GmbH)
+@author: Alan O'Cais (Forschungszentrum Juelich GmbH)
+@author: Balazs Hajgato (Free University Brussels (VUB))
 """
 
 import fileinput
@@ -46,10 +49,12 @@
 from easybuild.framework.easyblock import EasyBlock
 from easybuild.framework.easyconfig import CUSTOM
 from easybuild.tools.build_log import EasyBuildError
+from easybuild.tools.filetools import write_file
 from easybuild.tools.config import build_option
 from easybuild.tools.modules import get_software_root, get_software_version
 from easybuild.tools.run import run_cmd
 from easybuild.tools.systemtools import get_avail_core_count
+from easybuild.tools.toolchain.compiler import OPTARCH_GENERIC
 
 # CP2K needs this version of libxc
 LIBXC_MIN_VERSION = '2.0.1'
@@ -99,15 +104,15 @@ def extra_options():
             'maxtasks': [4, ("Maximum number of CP2K instances run at "
                              "the same time during testing"), CUSTOM],
             'runtest': [True, "Build and run CP2K tests", CUSTOM],
-            'plumed': [False, "Enable PLUMED support", CUSTOM],
+            'plumed': [None, "Enable PLUMED support", CUSTOM],
         }
         return EasyBlock.extra_options(extra_vars)
 
     def _generate_makefile(self, options):
         """Generate Makefile based on options dictionary and optional make instructions"""
 
-        text = "# Makefile generated by CP2K._generateMakefile, items might appear in random order\n"
-        for key, value in options.iteritems():
+        text = "# Makefile generated by CP2K easyblock in EasyBuild\n"
+        for key, value in sorted(options.iteritems()):
             text += "%s = %s\n" % (key, value)
         return text + self.make_instructions
 
@@ -150,7 +155,7 @@ def configure_step(self):
         libxsmm = get_software_root('libxsmm')
         if libxsmm:
             self.cfg.update('extradflags', '-D__LIBXSMM')
-            self.libsmm = '-lxsmm'
+            self.libsmm = '-lxsmm -lxsmmf'
             self.log.debug('Using libxsmm %s' % libxsmm)
         elif libsmm:
             libsmms = glob.glob(os.path.join(libsmm, 'lib', 'libsmm_*nn.a'))
@@ -201,12 +206,24 @@ def configure_step(self):
             options = self.configure_ScaLAPACK(options)
 
         # PLUMED
-        if self.cfg["plumed"]:
-            if not get_software_root('PLUMED'):
-                raise EasyBuildError("The PLUMED module needs to be loaded to build CP2K with PLUMED support")
+        plumed = get_software_root('PLUMED')
+        if self.cfg['plumed'] and not plumed:
+            raise EasyBuildError("The PLUMED module needs to be loaded to build CP2K with PLUMED support")
+
+        # enable PLUMED support if PLUMED is listed as a dependency
+        # and PLUMED support is either explicitly enabled (plumed = True) or unspecified ('plumed' not defined)
+        if plumed and (self.cfg['plumed'] or self.cfg['plumed'] is None):
             options['LIBS'] += ' -lplumed'
             options['DFLAGS'] += ' -D__PLUMED2'
 
+        # ELPA
+        elpa = get_software_root('ELPA')
+        if elpa:
+            options['LIBS'] += ' -lelpa'
+            options['DFLAGS'] += ' -D__ELPA3'
+            elpa_inc_dir = os.path.join(elpa, 'include', 'elpa-%s' % get_software_version('ELPA'), 'modules')
+            options['FCFLAGSOPT'] += ' -I%s ' % elpa_inc_dir
+
         # CUDA
         cuda = get_software_root('CUDA')
         if cuda:
@@ -220,16 +237,10 @@ def configure_step(self):
         options['LIBS'] = "-Wl,--start-group %s -Wl,--end-group" % options['LIBS']
 
         # create arch file using options set
-        archfile = os.path.join(self.cfg['start_dir'], 'arch',
-                                '%s.%s' % (self.typearch, self.cfg['type']))
-        try:
-            txt = self._generate_makefile(options)
-            f = open(archfile, 'w')
-            f.write(txt)
-            f.close()
-            self.log.info("Content of makefile (%s):\n%s" % (archfile, txt))
-        except IOError, err:
-            raise EasyBuildError("Writing makefile %s failed: %s", archfile, err)
+        archfile = os.path.join(self.cfg['start_dir'], 'arch', '%s.%s' % (self.typearch, self.cfg['type']))
+        txt = self._generate_makefile(options)
+        write_file(archfile, txt)
+        self.log.info("Content of makefile (%s):\n%s" % (archfile, txt))
 
     def prepmodinc(self):
         """Prepare list of module files"""
@@ -322,6 +333,13 @@ def configure_common(self):
         if not mpi2:
             raise EasyBuildError("CP2K needs MPI-2, no known MPI-2 supporting library loaded?")
 
+        # pick up optarch value from toolchain, when optarch toolchain option is enabled or --optarch=GENERIC is used
+        optarch = ''
+        if self.toolchain.options.get('optarch', False) or build_option('optarch') == OPTARCH_GENERIC:
+            # take into account that a '-' is missing for the first compiler flag, but also that optarch may be empty
+            if self.toolchain.options.option('optarch'):
+                optarch = '-%s' % self.toolchain.options.option('optarch')
+
         options = {
             'CC': os.getenv('MPICC'),
             'CPP': '',
@@ -343,8 +361,8 @@ def configure_common(self):
             'LIBS': os.getenv('LIBS', ''),
 
             'FCFLAGSNOOPT': '$(DFLAGS) $(CFLAGS) -O0  $(FREE) $(FPIC) $(DEBUG)',
-            'FCFLAGSOPT': '-O2 $(FREE) $(SAFE) $(FPIC) $(DEBUG)',
-            'FCFLAGSOPT2': '-O1 $(FREE) $(SAFE) $(FPIC) $(DEBUG)'
+            'FCFLAGSOPT': '-O2 $(FREE) $(SAFE) $(FPIC) $(DEBUG) %s' % optarch,
+            'FCFLAGSOPT2': '-O1 $(FREE) $(SAFE) $(FPIC) $(DEBUG) %s' % optarch,
         }
 
         libint = get_software_root('LibInt')
@@ -435,20 +453,27 @@ def configure_intel_based(self):
 
             'INCFLAGS': '$(DFLAGS) -I$(INTEL_INC) -I$(INTEL_INCF) %s' % extrainc,
 
-            'LDFLAGS': '$(INCFLAGS) -i-static',
+            'LDFLAGS': '$(INCFLAGS) ',
             'OBJECTS_ARCHITECTURE': 'machine_intel.o',
         })
 
         options['DFLAGS'] += ' -D__INTEL'
 
-        optarch = ''
-        if self.toolchain.options['optarch']:
-            optarch = '-xHOST'
-
-        options['FCFLAGSOPT'] += ' $(INCFLAGS) %s -heap-arrays 64' % optarch
-        options['FCFLAGSOPT2'] += ' $(INCFLAGS) %s -heap-arrays 64' % optarch
+        options['FCFLAGSOPT'] += ' $(INCFLAGS) -heap-arrays 64'
+        options['FCFLAGSOPT2'] += ' $(INCFLAGS) -heap-arrays 64'
 
         ifortver = LooseVersion(get_software_version('ifort'))
+
+        # -i-static has been deprecated prior to 2013, but was still usable. From 2015 it is not.
+        if ifortver < LooseVersion("2013"):
+            options['LDFLAGS'] += ' -i-static '
+        else:
+            options['LDFLAGS'] += ' -static-intel '
+
+        # Otherwise it fails on linking, since there are 2 definitions of main
+        if LooseVersion(self.version) >= LooseVersion('4.1'):
+            options['LDFLAGS'] += ' -nofor-main '
+
         failmsg = "CP2K won't build correctly with the Intel %%s compilers prior to %%s, see %s" % intelurl
 
         if ifortver >= LooseVersion("2011") and ifortver < LooseVersion("2012"):
@@ -488,12 +513,8 @@ def configure_GCC_based(self):
 
         options['DFLAGS'] += ' -D__GFORTRAN'
 
-        optarch = ''
-        if self.toolchain.options['optarch']:
-            optarch = '-march=native'
-
-        options['FCFLAGSOPT'] += ' $(DFLAGS) $(CFLAGS) %s -fmax-stack-var-size=32768' % optarch
-        options['FCFLAGSOPT2'] += ' $(DFLAGS) $(CFLAGS) %s' % optarch
+        options['FCFLAGSOPT'] += ' $(DFLAGS) $(CFLAGS) -fmax-stack-var-size=32768'
+        options['FCFLAGSOPT2'] += ' $(DFLAGS) $(CFLAGS)'
 
         return options
 
@@ -560,8 +581,11 @@ def configure_FFTW3(self, options):
         })
 
         options['DFLAGS'] += ' -D__FFTW3'
-
-        options['LIBS'] += ' -L%s %s' % (os.getenv('FFT_LIB_DIR', '.'), os.getenv('LIBFFT', ''))
+        if self.cfg['type'] == 'psmp':
+            libfft = os.getenv('LIBFFT_MT', '')
+        else:
+            libfft = os.getenv('LIBFFT', '')
+        options['LIBS'] += ' -L%s %s' % (os.getenv('FFT_LIB_DIR', '.'), libfft)
 
         return options
 
@@ -687,13 +711,7 @@ def test_step(self):
                 'mpicmd_prefix': self.toolchain.mpi_cmd_for('', test_core_cnt),
             }
 
-            try:
-                f = open(cfg_fn, "w")
-                f.write(cfg_txt)
-                f.close()
-            except IOError, err:
-                raise EasyBuildError("Failed to create config file %s: %s", cfg_fn, err)
-
+            write_file(cfg_fn, cfg_txt)
             self.log.debug("Contents of %s: %s" % (cfg_fn, cfg_txt))
 
             # run regression test
@@ -765,6 +783,7 @@ def test_report(test_result):
     def install_step(self):
         """Install built CP2K
         - copy from exe to bin
+        - copy data dir (if exists)
         - copy tests
         """
 
@@ -781,6 +800,19 @@ def install_step(self):
         except OSError, err:
             raise EasyBuildError("Copying executables from %s to bin dir %s failed: %s", exedir, targetdir, err)
 
+        # copy data dir
+        datadir = os.path.join(self.cfg['start_dir'], 'data')
+        targetdir = os.path.join(self.installdir, 'data')
+        if os.path.exists(targetdir):
+            self.log.info("Won't copy data dir. Destination directory %s already exists" % targetdir)
+        elif os.path.exists(datadir):
+            try:
+                shutil.copytree(datadir, targetdir)
+            except:
+                raise EasyBuildError("Copying data dir from %s to %s failed", datadir, targetdir)
+        else:
+            self.log.info("Won't copy data dir. Source directory %s does not exist" % datadir)
+
         # copy tests
         srctests = os.path.join(self.cfg['start_dir'], 'tests')
         targetdir = os.path.join(self.installdir, 'tests')
@@ -816,3 +848,12 @@ def sanity_check_step(self):
         }
 
         super(EB_CP2K, self).sanity_check_step(custom_paths=custom_paths)
+
+    def make_module_extra(self):
+        """Set up a CP2K_DATA_DIR environment variable to find CP2K provided basis sets"""
+
+        txt = super(EB_CP2K, self).make_module_extra()
+        datadir = os.path.join(self.installdir, 'data')
+        if os.path.exists(datadir):
+            txt += self.module_generator.set_environment('CP2K_DATA_DIR', datadir)
+        return txt


Test patch: