diff --git a/var/spack/repos/builtin/packages/cp2k/package.py b/var/spack/repos/builtin/packages/cp2k/package.py
index 0909d8cb81c60fbbd31d318fe527edb914a3343d..9bc1026ba9a412352a2118bd225375b8e51a49cb 100644
--- a/var/spack/repos/builtin/packages/cp2k/package.py
+++ b/var/spack/repos/builtin/packages/cp2k/package.py
@@ -37,6 +37,7 @@ class Cp2k(Package):
     homepage = 'https://www.cp2k.org'
     url = 'https://sourceforge.net/projects/cp2k/files/cp2k-3.0.tar.bz2'
 
+    version('4.1', 'b0534b530592de15ac89828b1541185e')
     version('3.0', 'c05bc47335f68597a310b1ed75601d35')
 
     variant('mpi', default=True, description='Enable MPI support')
@@ -47,14 +48,18 @@ class Cp2k(Package):
     depends_on('lapack')
     depends_on('blas')
     depends_on('fftw')
-    depends_on('libint@:1.2', when='@3.0')
+    depends_on('libint@:1.2', when='@3.0,4.1')
 
     depends_on('mpi', when='+mpi')
     depends_on('scalapack', when='+mpi')
     depends_on('plumed+shared+mpi', when='+plumed+mpi')
     depends_on('plumed+shared~mpi', when='+plumed~mpi')
-    depends_on('pexsi', when='+mpi')
-    depends_on('wannier90', when='+mpi')
+    depends_on('pexsi+fortran', when='+mpi')
+
+    # Apparently cp2k@4.1 needs an "experimental" version of libwannier.a
+    # which is only available contacting the developer directly. See INSTALL
+    # in the stage of cp2k@4.1
+    depends_on('wannier90', when='@3.0+mpi')
     depends_on('elpa', when='+mpi')
 
     # TODO : add dependency on libsmm, libxsmm
@@ -94,6 +99,8 @@ def install(self, spec, prefix):
             fcflags.append(spec['fftw'].cppflags)
             fftw = find_libraries('libfftw3', root=spec['fftw'].prefix.lib)
             ldflags = [fftw.search_flags]
+            if 'superlu-dist@4.3' in spec:
+                ldflags = ['-Wl,--allow-multiple-definition'] + ldflags
             libs = [
                 join_path(spec['libint'].prefix.lib, 'libint.so'),
                 join_path(spec['libint'].prefix.lib, 'libderiv.so'),
@@ -147,10 +154,12 @@ def install(self, spec, prefix):
                 cppflags.extend([
                     '-D__parallel',
                     '-D__LIBPEXSI',
-                    '-D__WANNIER90',
                     '-D__ELPA3',
                     '-D__SCALAPACK'
                 ])
+                if 'wannier90' in spec:
+                    cppflags.append('-D__WANNIER90')
+
                 fcflags.extend([
                     # spec['elpa:fortran'].cppflags
                     '-I' + join_path(
@@ -167,7 +176,6 @@ def install(self, spec, prefix):
                 libs.extend([
                     join_path(spec['elpa'].prefix.lib,
                               'libelpa.{0}'.format(dso_suffix)),
-                    join_path(spec['wannier90'].prefix.lib, 'libwannier.a'),
                     join_path(spec['pexsi'].prefix.lib, 'libpexsi.a'),
                     join_path(spec['superlu-dist'].prefix.lib,
                               'libsuperlu_dist.a'),
@@ -180,6 +188,13 @@ def install(self, spec, prefix):
                         'libmetis.{0}'.format(dso_suffix)
                     ),
                 ])
+
+                if 'wannier90' in spec:
+                    wannier = join_path(
+                        spec['wannier90'].prefix.lib, 'libwannier.a'
+                    )
+                    libs.append(wannier)
+
                 libs.extend(scalapack)
                 libs.extend(self.spec['mpi'].mpicxx_shared_libs)
                 libs.extend(self.compiler.stdcxx_libs)
diff --git a/var/spack/repos/builtin/packages/elpa/package.py b/var/spack/repos/builtin/packages/elpa/package.py
index fe249269c7d21bb341853fd462590f22b3e75ffc..033cb1e36c2bc7456c637a82888f2cb582483216 100644
--- a/var/spack/repos/builtin/packages/elpa/package.py
+++ b/var/spack/repos/builtin/packages/elpa/package.py
@@ -26,24 +26,15 @@
 from spack import *
 
 
-class Elpa(Package):
-    """
-    Eigenvalue solvers for Petaflop-Applications (ELPA)
-    """
+class Elpa(AutotoolsPackage):
+    """Eigenvalue solvers for Petaflop-Applications (ELPA)"""
 
     homepage = 'http://elpa.mpcdf.mpg.de/'
     url = 'http://elpa.mpcdf.mpg.de/elpa-2015.11.001.tar.gz'
 
-    version(
-        '2016.05.003',
-        '88a9f3f3bfb63e16509dd1be089dcf2c',
-        url='http://elpa.mpcdf.mpg.de/html/Releases/2016.05.003/elpa-2016.05.003.tar.gz'
-    )
-    version(
-        '2015.11.001',
-        'de0f35b7ee7c971fd0dca35c900b87e6',
-        url='http://elpa.mpcdf.mpg.de/elpa-2015.11.001.tar.gz'
-    )
+    version('2016.05.004', 'c0dd3a53055536fc3a2a221e78d8b376')
+    version('2016.05.003', '88a9f3f3bfb63e16509dd1be089dcf2c')
+    version('2015.11.001', 'de0f35b7ee7c971fd0dca35c900b87e6')
 
     variant('openmp', default=False, description='Activates OpenMP support')
 
@@ -52,30 +43,26 @@ class Elpa(Package):
     depends_on('lapack')
     depends_on('scalapack')
 
-    def install(self, spec, prefix):
+    def url_for_version(self, version):
+        t = 'http://elpa.mpcdf.mpg.de/html/Releases/{0}/elpa-{0}.tar.gz'
+        if version < Version('2016.05.003'):
+            t = 'http://elpa.mpcdf.mpg.de/elpa-{0}.tar.gz'
+        return t.format(str(version))
 
-        options = [
-            'CC={0}'.format(self.spec['mpi'].mpicc),
-            'FC={0}'.format(self.spec['mpi'].mpifc),
-            'CXX={0}'.format(self.spec['mpi'].mpicxx),
-            'FCFLAGS={0}'.format(
-                spec['lapack'].libs.joined()
-            ),
-            'LDFLAGS={0}'.format(
-                spec['lapack'].libs.joined()
-            ),
-            'SCALAPACK_FCFLAGS={0}'.format(
-                spec['scalapack'].libs.joined()
-            ),
-            'SCALAPACK_LDFLAGS={0}'.format(
-                spec['scalapack'].libs.joined()
-            ),
-            '--prefix={0}'.format(self.prefix)
-        ]
+    def setup_environment(self, spack_env, run_env):
 
-        if '+openmp' in spec:
-            options.append("--enable-openmp")
+        spec = self.spec
+
+        spack_env.set('CC', spec['mpi'].mpicc)
+        spack_env.set('FC', spec['mpi'].mpifc)
+        spack_env.set('CXX', spec['mpi'].mpicxx)
 
-        configure(*options)
-        make()
-        make("install")
+        spack_env.set('LDFLAGS', spec['lapack'].libs.search_flags)
+        spack_env.set('LIBS', spec['lapack'].libs.link_flags)
+        spack_env.set('SCALAPACK_LDFLAGS', spec['scalapack'].libs.joined())
+
+    def configure_args(self):
+        options = []
+        if '+openmp' in self.spec:
+            options.append("--enable-openmp")
+        return options
diff --git a/var/spack/repos/builtin/packages/libint/package.py b/var/spack/repos/builtin/packages/libint/package.py
index 2ad5e93191939855916a4d2c6ea375e1445fea06..569aa68b68a833e368367b353077e22ddb5642c7 100644
--- a/var/spack/repos/builtin/packages/libint/package.py
+++ b/var/spack/repos/builtin/packages/libint/package.py
@@ -25,25 +25,27 @@
 from spack import *
 
 
-class Libint(Package):
+class Libint(AutotoolsPackage):
     """Libint is a high-performance library for computing
-    Gaussian integrals in quantum mechanics."""
+    Gaussian integrals in quantum mechanics.
+    """
 
     homepage = "https://github.com/evaleev/libint"
-    url      = "https://github.com/evaleev/libint/archive/v2.1.0.tar.gz"
+    url = "https://github.com/evaleev/libint/archive/v2.1.0.tar.gz"
 
+    version('2.2.0', 'da37dab862fb0b97a7ed7d007695ef47')
     version('2.1.0', 'd0dcb985fe32ddebc78fe571ce37e2d6')
     version('1.1.6', '990f67b55f49ecc18f32c58da9240684')
     version('1.1.5', '379b7d0718ff398715d6898807adf628')
 
     # Build dependencies
     depends_on('autoconf@2.52:', type='build')
-    depends_on('automake',       type='build')
-    depends_on('libtool',        type='build')
+    depends_on('automake', type='build')
+    depends_on('libtool', type='build')
 
     # Libint 2 dependencies
     depends_on('boost', when='@2:')
-    depends_on('gmp',   when='@2:')
+    depends_on('gmp', when='@2:')
 
     def url_for_version(self, version):
         base_url = "https://github.com/evaleev/libint/archive"
@@ -54,16 +56,14 @@ def url_for_version(self, version):
         else:
             return "{0}/v{1}.tar.gz".format(base_url, version)
 
-    def install(self, spec, prefix):
-        # Generate configure
+    def autoreconf(self, spec, prefix):
         libtoolize()
         aclocal('-I', 'lib/autoconf')
         autoconf()
 
-        config_args = [
-            '--prefix={0}'.format(prefix),
-            '--enable-shared'
-        ]
+    def configure_args(self):
+
+        config_args = ['--enable-shared']
 
         # Optimizations for the Intel compiler, suggested by CP2K
         optflags = '-O2'
@@ -93,12 +93,4 @@ def install(self, spec, prefix):
                 '--with-libint-max-am=5',
                 '--with-libderiv-max-am1=4'
             ])
-
-        configure(*config_args)
-        make()
-
-        # Testing suite was added in libint 2
-        if self.version >= Version('2.0.0'):
-            make('check')
-
-        make('install')
+        return config_args
diff --git a/var/spack/repos/builtin/packages/netlib-scalapack/package.py b/var/spack/repos/builtin/packages/netlib-scalapack/package.py
index 6a97180328e6a4ecd65289805ced276fc7f08927..e860926f967a9d187ad9a72459f3162a6b1cfb64 100644
--- a/var/spack/repos/builtin/packages/netlib-scalapack/package.py
+++ b/var/spack/repos/builtin/packages/netlib-scalapack/package.py
@@ -26,7 +26,7 @@
 import sys
 
 
-class NetlibScalapack(Package):
+class NetlibScalapack(CMakePackage):
     """ScaLAPACK is a library of high-performance linear algebra routines for
     parallel distributed memory machines
     """
@@ -60,12 +60,16 @@ class NetlibScalapack(Package):
 
     @property
     def scalapack_libs(self):
+        # Note that the default will be to search
+        # for 'libnetlib-scalapack.<suffix>'
         shared = True if '+shared' in self.spec else False
         return find_libraries(
             'libscalapack', root=self.prefix, shared=shared, recurse=True
         )
 
-    def install(self, spec, prefix):
+    def cmake_args(self):
+        spec = self.spec
+
         options = [
             "-DBUILD_SHARED_LIBS:BOOL=%s" % ('ON' if '+shared' in spec else
                                              'OFF'),
@@ -89,13 +93,10 @@ def install(self, spec, prefix):
                 "-DCMAKE_Fortran_FLAGS=-fPIC"
             ])
 
-        options.extend(std_cmake_args)
-
-        with working_dir('spack-build', create=True):
-            cmake('..', *options)
-            make()
-            make("install")
+        return options
 
+    @run_after('install')
+    def fix_darwin_install(self):
         # The shared libraries are not installed correctly on Darwin:
-        if (sys.platform == 'darwin') and ('+shared' in spec):
-            fix_darwin_install_name(prefix.lib)
+        if (sys.platform == 'darwin') and ('+shared' in self.spec):
+            fix_darwin_install_name(self.spec.prefix.lib)
diff --git a/var/spack/repos/builtin/packages/pexsi/make.inc b/var/spack/repos/builtin/packages/pexsi/make.inc
index c97b09b42432f7871061c937a23c177e72b562cb..ec619a7dbab3e01cc73f131d0148e4cbe66835bf 100644
--- a/var/spack/repos/builtin/packages/pexsi/make.inc
+++ b/var/spack/repos/builtin/packages/pexsi/make.inc
@@ -63,7 +63,7 @@ CXXFLAGS     = ${COMPILE_FLAG} ${CPPFLAG} ${PROFILE_FLAG} ${INCLUDES}
 CCDEFS       = ${COMPILE_DEF}
 CPPDEFS      = ${COMPILE_DEF}
 LOADOPTS     = ${PROFILE_FLAG} ${LIBS}
-FLOADOPTS    = ${PROFILE_FLAG} ${LIBS} ${CPP_LIB}
+FLOADOPTS    = @FLDFLAGS ${PROFILE_FLAG} ${LIBS} ${CPP_LIB}
 
 # Generate auto-dependencies
 %.d: %.c
diff --git a/var/spack/repos/builtin/packages/pexsi/package.py b/var/spack/repos/builtin/packages/pexsi/package.py
index 989e2ebf6ed4bd06c9ccee0b9b43f7b425964ca7..04d22c4da8fe34b3502e892f41079e78c5f9f704 100644
--- a/var/spack/repos/builtin/packages/pexsi/package.py
+++ b/var/spack/repos/builtin/packages/pexsi/package.py
@@ -30,7 +30,7 @@
 from spack import *
 
 
-class Pexsi(Package):
+class Pexsi(MakefilePackage):
     """The PEXSI library is written in C++, and uses message passing interface
     (MPI) to parallelize the computation on distributed memory computing
     systems and achieve scalability on more than 10,000 processors.
@@ -46,14 +46,20 @@ class Pexsi(Package):
     homepage = 'https://math.berkeley.edu/~linlin/pexsi/index.html'
     url = 'https://math.berkeley.edu/~linlin/pexsi/download/pexsi_v0.9.0.tar.gz'
 
+    version('0.9.2', '0ce491a3a922d271c4edf9b20aa93076')
     version('0.9.0', '0c1a2de891ba1445dfc184b2fa270ed8')
 
     depends_on('parmetis')
     depends_on('superlu-dist@3.3', when='@0.9.0')
+    depends_on('superlu-dist@4.3', when='@0.9.2')
+
+    variant(
+        'fortran', default=False, description='Builds the Fortran interface'
+    )
 
     parallel = False
 
-    def install(self, spec, prefix):
+    def edit(self, spec, prefix):
 
         substitutions = {
             '@MPICC': self.spec['mpi'].mpicc,
@@ -70,9 +76,13 @@ def install(self, spec, prefix):
             '@LAPACK_LIBS': self.spec['lapack'].libs.joined(),
             '@BLAS_LIBS': self.spec['blas'].libs.joined(),
             # FIXME : what to do with compiler provided libraries ?
-            '@STDCXX_LIB': ' '.join(self.compiler.stdcxx_libs)
+            '@STDCXX_LIB': ' '.join(self.compiler.stdcxx_libs),
+            '@FLDFLAGS': ''
         }
 
+        if '@0.9.2' in self.spec:
+            substitutions['@FLDFLAGS'] = '-Wl,--allow-multiple-definition'
+
         template = join_path(
             os.path.dirname(inspect.getmodule(self).__file__),
             'make.inc'
@@ -85,20 +95,29 @@ def install(self, spec, prefix):
         for key, value in substitutions.items():
             filter_file(key, value, makefile)
 
-        make()
+    def build(self, spec, prefix):
+        super(Pexsi, self).build(spec, prefix)
+        if '+fortran' in self.spec:
+            make('-C', 'fortran')
+
+    def install(self, spec, prefix):
+
         # 'make install' does not exist, despite what documentation says
         mkdirp(self.prefix.lib)
+
         install(
             join_path(self.stage.source_path, 'src', 'libpexsi_linux.a'),
             join_path(self.prefix.lib, 'libpexsi.a')
         )
+
         install_tree(
             join_path(self.stage.source_path, 'include'),
             self.prefix.include
         )
+
         # fortran "interface"
-        make('-C', 'fortran')
-        install_tree(
-            join_path(self.stage.source_path, 'fortran'),
-            join_path(self.prefix, 'fortran')
-        )
+        if '+fortran' in self.spec:
+            install_tree(
+                join_path(self.stage.source_path, 'fortran'),
+                join_path(self.prefix, 'fortran')
+            )
diff --git a/var/spack/repos/builtin/packages/plumed/package.py b/var/spack/repos/builtin/packages/plumed/package.py
index 80cc1aa66a4a457b6284dbfb3989dfc03e5f5ba4..60443cbcc654863005da881b7d52fc4a06e324a8 100644
--- a/var/spack/repos/builtin/packages/plumed/package.py
+++ b/var/spack/repos/builtin/packages/plumed/package.py
@@ -43,17 +43,16 @@ class Plumed(AutotoolsPackage):
     homepage = 'http://www.plumed.org/'
     url = 'https://github.com/plumed/plumed2/archive/v2.2.3.tar.gz'
 
+    version('2.3.0', 'a9b5728f115dca8f0519111f1f5a6fa5')
+    version('2.2.4', 'afb00da25a3fbd47acf377e53342059d')
     version('2.2.3', 'a6e3863e40aac07eb8cf739cbd14ecf8')
 
     # Variants. PLUMED by default builds a number of optional modules.
     # The ones listed here are not built by default for various reasons,
     # such as stability, lack of testing, or lack of demand.
-    variant('crystallization', default=False,
-            description='Build support for optional crystallization module.')
-    variant('imd', default=False,
-            description='Build support for optional imd module.')
-    variant('manyrestraints', default=False,
-            description='Build support for optional manyrestraints module.')
+    # FIXME: This needs to be an optional
+    variant('optional_modules', default='all',
+            description='String that is used to build optional modules')
     variant('shared', default=True, description='Builds shared libraries')
     variant('mpi', default=True, description='Activates MPI support')
     variant('gsl', default=True, description='Activates GSL support')
@@ -73,6 +72,28 @@ class Plumed(AutotoolsPackage):
     # Dictionary mapping PLUMED versions to the patches it provides
     # interactively
     plumed_patches = {
+        '2.3.0': {
+            'amber-14': '1',
+            'gromacs-2016.1': '2',
+            'gromacs-4.5.7': '3',
+            'gromacs-5.0.7': '4',
+            'gromacs-5.1.4': '5',
+            'lammps-6Apr13': '6',
+            'namd-2.8': '7',
+            'namd-2.9': '8',
+            'espresso-5.0.2': '9'
+        },
+        '2.2.4': {
+            'amber-14': '1',
+            'gromacs-4.5.7': '2',
+            'gromacs-4.6.7': '3',
+            'gromacs-5.0.7': '4',
+            'gromacs-5.1.2': '5',
+            'lammps-6Apr13': '6',
+            'namd-2.8': '7',
+            'namd-2.9': '8',
+            'espresso-5.0.2': '9'
+        },
         '2.2.3': {
             'amber-14': '1',
             'gromacs-4.5.7': '2',
@@ -132,7 +153,7 @@ def configure_args(self):
             # If the MPI dependency is provided by the intel-mpi package then
             # the following additional argument is required to allow it to
             # build.
-            if spec.satisfies('^intel-mpi'):
+            if 'intel-mpi' in spec:
                 configure_opts.extend([
                     'STATIC_LIBS=-mt_mpi'
                 ])
@@ -144,19 +165,16 @@ def configure_args(self):
         ])
 
         # Construct list of optional modules
-        module_opts = []
-        module_opts.extend([
-            '+crystallization' if (
-                '+crystallization' in spec) else '-crystallization',
-            '+imd' if '+imd' in spec else '-imd',
-            '+manyrestraints' if (
-                '+manyrestraints' in spec) else '-manyrestraints'
-        ])
 
         # If we have specified any optional modules then add the argument to
         # enable or disable them.
-        if module_opts:
-            configure_opts.extend([
-                '--enable-modules={0}'.format("".join(module_opts))])
+        optional_modules = self.spec.variants['optional_modules'].value
+        if optional_modules:
+            # From 'configure --help' @2.3:
+            # all/none/reset or : separated list such as
+            # +crystallization:-bias default: reset
+            configure_opts.append(
+                '--enable-modules={0}'.format(optional_modules)
+            )
 
         return configure_opts
diff --git a/var/spack/repos/builtin/packages/wannier90/package.py b/var/spack/repos/builtin/packages/wannier90/package.py
index ad61860d73721b9d1e0a44540c2903541683d333..25d238dd642edf5ccc68fb8754934598f64d942f 100644
--- a/var/spack/repos/builtin/packages/wannier90/package.py
+++ b/var/spack/repos/builtin/packages/wannier90/package.py
@@ -29,7 +29,7 @@
 from spack import *
 
 
-class Wannier90(Package):
+class Wannier90(MakefilePackage):
     """Wannier90 calculates maximally-localised Wannier functions (MLWFs).
 
     Wannier90 is released under the GNU General Public License.
@@ -37,6 +37,7 @@ class Wannier90(Package):
     homepage = 'http://wannier.org'
     url = 'http://wannier.org/code/wannier90-2.0.1.tar.gz'
 
+    version('2.1.0', '07a81c002b41d6d0f97857e55c57d769')
     version('2.0.1', '4edd742506eaba93317249d33261fb22')
 
     depends_on('mpi')
@@ -45,7 +46,23 @@ class Wannier90(Package):
 
     parallel = False
 
-    def install(self, spec, prefix):
+    build_targets = [
+        'wannier', 'post', 'lib', 'w90chk2chk', 'w90vdw', 'w90pov'
+    ]
+
+    @property
+    def makefile_name(self):
+        # Older versions use 'make.sys'
+        filename = 'make.sys'
+
+        # While newer search for 'make.inc'
+        if self.spec.satisfies('@2.1.0:'):
+            filename = 'make.inc'
+
+        abspath = join_path(self.stage.source_path, filename)
+        return abspath
+
+    def edit(self, spec, prefix):
 
         lapack = self.spec['lapack'].libs
         blas = self.spec['blas'].libs
@@ -54,57 +71,46 @@ def install(self, spec, prefix):
             '@MPIF90': self.spec['mpi'].mpifc,
             '@LIBS': (lapack + blas).joined()
         }
-        #######
-        # TODO : this part is replicated in PEXSI
-        # TODO : and may be a common pattern for Editable Makefiles
-        # TODO : see #1186
+
         template = join_path(
             os.path.dirname(inspect.getmodule(self).__file__),
             'make.sys'
         )
-        makefile = join_path(
-            self.stage.source_path,
-            'make.sys'
-        )
 
-        shutil.copy(template, makefile)
+        shutil.copy(template, self.makefile_name)
         for key, value in substitutions.items():
-            filter_file(key, value, makefile)
-        ######
+            filter_file(key, value, self.makefile_name)
+
+    def install(self, spec, prefix):
 
-        make('wannier')
         mkdirp(self.prefix.bin)
+        mkdirp(self.prefix.lib)
+
         install(
             join_path(self.stage.source_path, 'wannier90.x'),
             join_path(self.prefix.bin, 'wannier90.x')
         )
 
-        make('post')
         install(
             join_path(self.stage.source_path, 'postw90.x'),
             join_path(self.prefix.bin, 'postw90.x')
         )
 
-        make('lib')
-        mkdirp(self.prefix.lib)
         install(
             join_path(self.stage.source_path, 'libwannier.a'),
             join_path(self.prefix.lib, 'libwannier.a')
         )
 
-        make('w90chk2chk')
         install(
             join_path(self.stage.source_path, 'w90chk2chk.x'),
             join_path(self.prefix.bin, 'w90chk2chk.x')
         )
 
-        make('w90vdw')
         install(
             join_path(self.stage.source_path, 'utility', 'w90vdw', 'w90vdw.x'),
             join_path(self.prefix.bin, 'w90vdw.x')
         )
 
-        make('w90pov')
         install(
             join_path(self.stage.source_path, 'utility', 'w90pov', 'w90pov'),
             join_path(self.prefix.bin, 'w90pov')