diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst
index a8971f64a90613347c9aab38a41bd2ff630e9e48..09bd2632b3418f0d7dbb036bffe3f284f32ca833 100644
--- a/lib/spack/docs/packaging_guide.rst
+++ b/lib/spack/docs/packaging_guide.rst
@@ -2536,98 +2536,104 @@ build system.
 Compiler flags
 ^^^^^^^^^^^^^^
 
-Compiler flags set by the user through the Spec object can be passed to
-the build in one of two ways. For packages inheriting from the
-``CmakePackage`` or ``AutotoolsPackage`` classes, the build environment
-passes those flags to the relevant environment variables (``CFLAGS``,
-``CXXFLAGS``, etc) that are respected by the build system. For all other
-packages, the default behavior is to inject the flags directly into the
-compiler commands using Spack's compiler wrappers.
+Compiler flags set by the user through the Spec object can be passed
+to the build in one of three ways. By default, the build environment
+injects these flags directly into the compiler commands using Spack's
+compiler wrappers. In cases where the build system requires knowledge
+of the compiler flags, they can be registered with the build system by
+alternatively passing them through environment variables or as build
+system arguments. The flag_handler method can be used to change this
+behavior.
+
+Packages can override the flag_handler method with one of three
+built-in flag_handlers. The built-in flag_handlers are named
+``inject_flags``, ``env_flags``, and ``build_system_flags``. The
+``inject_flags`` method is the default. The ``env_flags`` method puts
+all of the flags into the environment variables that ``make`` uses as
+implicit variables ('CFLAGS', 'CXXFLAGS', etc.). The
+``build_system_flags`` method adds the flags as
+arguments to the invocation of ``configure`` or ``cmake``,
+respectively.
 
 .. warning::
 
-   The flag handling methods described in this section are in beta.
-   The exact semantics are liable to change to improve usability.
+   Passing compiler flags using build system arguments is only
+   supported for CMake and Autotools packages. Individual packages may
+   also differ in whether they properly respect these arguments.
 
-Individual packages can override the default behavior for the flag
-handling.  Packages can define a ``default_flag_handler`` method that
-applies to all sets of flags handled by Spack, or may define
-individual methods ``cflags_handler``, ``cxxflags_handler``,
-etc. Spack will apply the individual method for a flag set if it
-exists, otherwise the ``default_flag_handler`` method if it exists,
-and fall back on the default for that package class if neither exists.
+Individual packages may also define their own ``flag_handler``
+methods. The ``flag_handler`` method takes the package instance
+(``self``), the name of the flag, and a list of the values of the
+flag. It will be called on each of the six compiler flags supported in
+Spack. It should return a triple of ``(injf, envf, bsf)`` where
+``injf`` is a list of flags to inject via the Spack compiler wrappers,
+``envf`` is a list of flags to set in the appropriate environment
+variables, and ``bsf`` is a list of flags to pass to the build system
+as arguments.
 
-These methods are defined on the package class, and take two
-parameters in addition to the packages itself. The ``env`` parameter
-is an ``EnvironmentModifications`` object that can be used to change
-the build environment. The ``flag_val`` parameter is a tuple. Its
-first entry is the name of the flag (``cflags``, ``cxxflags``, etc.)
-and its second entry is a list of the values for that flag.
+.. warning::
 
-There are three primary idioms that can be combined to create whatever
-behavior the package requires.
+   Passing a non-empty list of flags to ``bsf`` for a build system
+   that does not support build system arguments will result in an
+   error.
 
-1. The default behavior for packages inheriting from
-``AutotoolsPackage`` or ``CmakePackage``.
+Here are the definitions of the three built-in flag handlers:
 
 .. code-block:: python
 
-    def default_flag_handler(self, env, flag_val):
-        env.append_flags(flag_val[0].upper(), ' '.join(flag_val[1]))
-        return []
+   def inject_flags(self, name, flags):
+       return (flags, None, None)
 
-2. The default behavior for other packages
+   def env_flags(self, name, flags):
+       return (None, flags, None)
 
-.. code-block:: python
+   def build_system_flags(self, name, flags):
+       return (None, None, flags)
 
-    def default_flag_handler(self, env, flag_val):
-        return flag_val[1]
+.. note::
 
+   Returning ``[]`` and ``None`` are equivalent in a ``flag_handler``
+   method.
 
-3. Packages may have additional flags to add to the build. These flags
-can be added to either idiom above. For example:
+Packages can override the default behavior either by specifying one of
+the built-in flag handlers,
 
 .. code-block:: python
 
-    def default_flag_handler(self, env, flag_val):
-        flags = flag_val[1]
-        flags.append('-flag')
-        return flags
-
-or
-
-.. code-block:: python
+   flag_handler = <PackageClass>.env_flags
 
-    def default_flag_handler(self, env, flag_val):
-        env.append_flags(flag_val[0].upper(), ' '.join(flag_val[1]))
-        env.append_flags(flag_val[0].upper(), '-flag')
-        return []
+where ``<PackageClass>`` can be any of the subclasses of PackageBase
+discussed in :ref:`installation_procedure`,
 
-Packages may also opt for methods that include aspects of any of the
-idioms above. E.g.
+or by implementing the flag_handler method. Suppose for a package
+``Foo`` we need to pass ``cflags``, ``cxxflags``, and ``cppflags``
+through the environment, the rest of the flags through compiler
+wrapper injection, and we need to add ``-lbar`` to ``ldlibs``. The
+following flag handler method accomplishes that.
 
 .. code-block:: python
 
-    def default_flag_handler(self, env, flag_val):
-        flags = []
-        if len(flag_val[1]) > 3:
-            env.append_flags(flag_val[0].upper(), ' '.join(flag_val[1][3:]))
-            flags = flag_val[1][:3]
-        else:
-            flags = flag_val[1]
-        flags.append('-flag')
-        return flags
+   def flag_handler(self, name, flags):
+       if name in ['cflags', 'cxxflags', 'cppflags']:
+           return (None, flags, None)
+       elif name == 'ldlibs':
+           flags.append('-lbar')
+       return (flags, None, None)
 
 Because these methods can pass values through environment variables,
-it is important not to override these variables unnecessarily in other
-package methods. In the ``setup_environment`` and
+it is important not to override these variables unnecessarily
+(E.g. setting ``env['CFLAGS']``) in other package methods when using
+non-default flag handlers. In the ``setup_environment`` and
 ``setup_dependent_environment`` methods, use the ``append_flags``
 method of the ``EnvironmentModifications`` class to append values to a
-list of flags whenever there is no good reason to override the
-existing value. In the ``install`` method and other methods that can
-operate on the build environment directly through the ``env``
-variable, test for environment variable existance before overriding
-values to add compiler flags.
+list of flags whenever the flag handler is ``env_flags``. If the
+package passes flags through the environment or the build system
+manually (in the install method, for example), we recommend using the
+default flag handler, or removind manual references and implementing a
+custom flag handler method that adds the desired flags to export as
+environment variables or pass to the build system. Manual flag passing
+is likely to interfere with the ``env_flags`` and
+``build_system_flags`` methods.
 
 In rare circumstances such as compiling and running small unit tests, a
 package developer may need to know what are the appropriate compiler
diff --git a/lib/spack/docs/tutorial/examples/Autotools/autotools_class.py b/lib/spack/docs/tutorial/examples/Autotools/autotools_class.py
deleted file mode 100644
index 8f2af3e21e3a931786efbd5ac0d4d1b9b1934832..0000000000000000000000000000000000000000
--- a/lib/spack/docs/tutorial/examples/Autotools/autotools_class.py
+++ /dev/null
@@ -1,460 +0,0 @@
-##############################################################################
-# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
-# Produced at the Lawrence Livermore National Laboratory.
-#
-# This file is part of Spack.
-# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
-# LLNL-CODE-647188
-#
-# For details, see https://github.com/spack/spack
-# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License (as
-# published by the Free Software Foundation) version 2.1, February 1999.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
-# conditions of the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-##############################################################################
-
-import inspect
-import os
-import os.path
-import shutil
-import stat
-from subprocess import PIPE
-from subprocess import check_call
-
-import llnl.util.tty as tty
-from llnl.util.filesystem import working_dir, join_path, force_remove
-from spack.package import PackageBase, run_after, run_before
-from spack.util.executable import Executable
-
-
-class AutotoolsPackage(PackageBase):
-    """Specialized class for packages built using GNU Autotools.
-
-    This class provides four phases that can be overridden:
-
-        1. :py:meth:`~.AutotoolsPackage.autoreconf`
-        2. :py:meth:`~.AutotoolsPackage.configure`
-        3. :py:meth:`~.AutotoolsPackage.build`
-        4. :py:meth:`~.AutotoolsPackage.install`
-
-    They all have sensible defaults and for many packages the only thing
-    necessary will be to override the helper method
-    :py:meth:`~.AutotoolsPackage.configure_args`.
-    For a finer tuning you may also override:
-
-        +-----------------------------------------------+--------------------+
-        | **Method**                                    | **Purpose**        |
-        +===============================================+====================+
-        | :py:attr:`~.AutotoolsPackage.build_targets`   | Specify ``make``   |
-        |                                               | targets for the    |
-        |                                               | build phase        |
-        +-----------------------------------------------+--------------------+
-        | :py:attr:`~.AutotoolsPackage.install_targets` | Specify ``make``   |
-        |                                               | targets for the    |
-        |                                               | install phase      |
-        +-----------------------------------------------+--------------------+
-        | :py:meth:`~.AutotoolsPackage.check`           | Run  build time    |
-        |                                               | tests if required  |
-        +-----------------------------------------------+--------------------+
-
-    """
-    #: Phases of a GNU Autotools package
-    phases = ['autoreconf', 'configure', 'build', 'install']
-    #: This attribute is used in UI queries that need to know the build
-    #: system base class
-    build_system_class = 'AutotoolsPackage'
-    #: Whether or not to update ``config.guess`` on old architectures
-    patch_config_guess = True
-
-    #: Targets for ``make`` during the :py:meth:`~.AutotoolsPackage.build`
-    #: phase
-    build_targets = []
-    #: Targets for ``make`` during the :py:meth:`~.AutotoolsPackage.install`
-    #: phase
-    install_targets = ['install']
-
-    #: Callback names for build-time test
-    build_time_test_callbacks = ['check']
-
-    #: Callback names for install-time test
-    install_time_test_callbacks = ['installcheck']
-
-    #: Set to true to force the autoreconf step even if configure is present
-    force_autoreconf = False
-    #: Options to be passed to autoreconf when using the default implementation
-    autoreconf_extra_args = []
-
-    @run_after('autoreconf')
-    def _do_patch_config_guess(self):
-        """Some packages ship with an older config.guess and need to have
-        this updated when installed on a newer architecture. In particular,
-        config.guess fails for PPC64LE for version prior to a 2013-06-10
-        build date (automake 1.13.4)."""
-
-        if not self.patch_config_guess or not self.spec.satisfies(
-                'target=ppc64le'
-        ):
-            return
-        my_config_guess = None
-        config_guess = None
-        if os.path.exists('config.guess'):
-            # First search the top-level source directory
-            my_config_guess = 'config.guess'
-        else:
-            # Then search in all sub directories.
-            # We would like to use AC_CONFIG_AUX_DIR, but not all packages
-            # ship with their configure.in or configure.ac.
-            d = '.'
-            dirs = [os.path.join(d, o) for o in os.listdir(d)
-                    if os.path.isdir(os.path.join(d, o))]
-            for dirname in dirs:
-                path = os.path.join(dirname, 'config.guess')
-                if os.path.exists(path):
-                    my_config_guess = path
-
-        if my_config_guess is not None:
-            try:
-                check_call([my_config_guess], stdout=PIPE, stderr=PIPE)
-                # The package's config.guess already runs OK, so just use it
-                return
-            except Exception:
-                pass
-        else:
-            return
-
-        # Look for a spack-installed automake package
-        if 'automake' in self.spec:
-            automake_path = os.path.join(self.spec['automake'].prefix, 'share',
-                                         'automake-' +
-                                         str(self.spec['automake'].version))
-            path = os.path.join(automake_path, 'config.guess')
-            if os.path.exists(path):
-                config_guess = path
-        # Look for the system's config.guess
-        if config_guess is None and os.path.exists('/usr/share'):
-            automake_dir = [s for s in os.listdir('/usr/share') if
-                            "automake" in s]
-            if automake_dir:
-                automake_path = os.path.join('/usr/share', automake_dir[0])
-                path = os.path.join(automake_path, 'config.guess')
-                if os.path.exists(path):
-                    config_guess = path
-        if config_guess is not None:
-            try:
-                check_call([config_guess], stdout=PIPE, stderr=PIPE)
-                mod = os.stat(my_config_guess).st_mode & 0o777 | stat.S_IWUSR
-                os.chmod(my_config_guess, mod)
-                shutil.copyfile(config_guess, my_config_guess)
-                return
-            except Exception:
-                pass
-
-        raise RuntimeError('Failed to find suitable config.guess')
-
-    @property
-    def configure_directory(self):
-        """Returns the directory where 'configure' resides.
-
-        :return: directory where to find configure
-        """
-        return self.stage.source_path
-
-    @property
-    def configure_abs_path(self):
-        # Absolute path to configure
-        configure_abs_path = join_path(
-            os.path.abspath(self.configure_directory), 'configure'
-        )
-        return configure_abs_path
-
-    @property
-    def build_directory(self):
-        """Override to provide another place to build the package"""
-        return self.configure_directory
-
-    def default_flag_handler(self, spack_env, flag_val):
-        # Relies on being the first thing that can affect the spack_env
-        # EnvironmentModification after it is instantiated or no other
-        # method trying to affect these variables. Currently both are true
-        # flag_val is a tuple (flag, value_list).
-        spack_env.set(flag_val[0].upper(),
-                      ' '.join(flag_val[1]))
-        return []
-
-    @run_before('autoreconf')
-    def delete_configure_to_force_update(self):
-        if self.force_autoreconf:
-            force_remove(self.configure_abs_path)
-
-    def autoreconf(self, spec, prefix):
-        """Not needed usually, configure should be already there"""
-        # If configure exists nothing needs to be done
-        if os.path.exists(self.configure_abs_path):
-            return
-        # Else try to regenerate it
-        autotools = ['m4', 'autoconf', 'automake', 'libtool']
-        missing = [x for x in autotools if x not in spec]
-        if missing:
-            msg = 'Cannot generate configure: missing dependencies {0}'
-            raise RuntimeError(msg.format(missing))
-        tty.msg('Configure script not found: trying to generate it')
-        tty.warn('*********************************************************')
-        tty.warn('* If the default procedure fails, consider implementing *')
-        tty.warn('*        a custom AUTORECONF phase in the package       *')
-        tty.warn('*********************************************************')
-        with working_dir(self.configure_directory):
-            m = inspect.getmodule(self)
-            # This part should be redundant in principle, but
-            # won't hurt
-            m.libtoolize()
-            m.aclocal()
-            # This line is what is needed most of the time
-            # --install, --verbose, --force
-            autoreconf_args = ['-ivf']
-            if 'pkg-config' in spec:
-                autoreconf_args += [
-                    '-I',
-                    join_path(spec['pkg-config'].prefix, 'share', 'aclocal'),
-                ]
-            autoreconf_args += self.autoreconf_extra_args
-            m.autoreconf(*autoreconf_args)
-
-    @run_after('autoreconf')
-    def set_configure_or_die(self):
-        """Checks the presence of a ``configure`` file after the
-        autoreconf phase. If it is found sets a module attribute
-        appropriately, otherwise raises an error.
-
-        :raises RuntimeError: if a configure script is not found in
-            :py:meth:`~AutotoolsPackage.configure_directory`
-        """
-        # Check if a configure script is there. If not raise a RuntimeError.
-        if not os.path.exists(self.configure_abs_path):
-            msg = 'configure script not found in {0}'
-            raise RuntimeError(msg.format(self.configure_directory))
-
-        # Monkey-patch the configure script in the corresponding module
-        inspect.getmodule(self).configure = Executable(
-            self.configure_abs_path
-        )
-
-    def configure_args(self):
-        """Produces a list containing all the arguments that must be passed to
-        configure, except ``--prefix`` which will be pre-pended to the list.
-
-        :return: list of arguments for configure
-        """
-        return []
-
-    def configure(self, spec, prefix):
-        """Runs configure with the arguments specified in
-        :py:meth:`~.AutotoolsPackage.configure_args`
-        and an appropriately set prefix.
-        """
-        options = ['--prefix={0}'.format(prefix)] + self.configure_args()
-
-        with working_dir(self.build_directory, create=True):
-            inspect.getmodule(self).configure(*options)
-
-    def build(self, spec, prefix):
-        """Makes the build targets specified by
-        :py:attr:``~.AutotoolsPackage.build_targets``
-        """
-        with working_dir(self.build_directory):
-            inspect.getmodule(self).make(*self.build_targets)
-
-    def install(self, spec, prefix):
-        """Makes the install targets specified by
-        :py:attr:``~.AutotoolsPackage.install_targets``
-        """
-        with working_dir(self.build_directory):
-            inspect.getmodule(self).make(*self.install_targets)
-
-    run_after('build')(PackageBase._run_default_build_time_test_callbacks)
-
-    def check(self):
-        """Searches the Makefile for targets ``test`` and ``check``
-        and runs them if found.
-        """
-        with working_dir(self.build_directory):
-            self._if_make_target_execute('test')
-            self._if_make_target_execute('check')
-
-    def _activate_or_not(
-            self,
-            name,
-            activation_word,
-            deactivation_word,
-            activation_value=None
-    ):
-        """This function contains the current implementation details of
-        :py:meth:`~.AutotoolsPackage.with_or_without` and
-        :py:meth:`~.AutotoolsPackage.enable_or_disable`.
-
-        Args:
-            name (str): name of the variant that is being processed
-            activation_word (str): the default activation word ('with' in the
-                case of ``with_or_without``)
-            deactivation_word (str): the default deactivation word ('without'
-                in the case of ``with_or_without``)
-            activation_value (callable): callable that accepts a single
-                value. This value is either one of the allowed values for a
-                multi-valued variant or the name of a bool-valued variant.
-                Returns the parameter to be used when the value is activated.
-
-                The special value 'prefix' can also be assigned and will return
-                ``spec[name].prefix`` as activation parameter.
-
-        Examples:
-
-            Given a package with:
-
-            .. code-block:: python
-
-                variant('foo', values=('x', 'y'), description='')
-                variant('bar', default=True, description='')
-
-            calling this function like:
-
-            .. code-block:: python
-
-                _activate_or_not(
-                    'foo', 'with', 'without', activation_value='prefix'
-                )
-                _activate_or_not('bar', 'with', 'without')
-
-            will generate the following configuration options:
-
-            .. code-block:: console
-
-                --with-x=<prefix-to-x> --without-y --with-bar
-
-            for ``<spec-name> foo=x +bar``
-
-        Returns:
-            list of strings that corresponds to the activation/deactivation
-            of the variant that has been processed
-
-        Raises:
-            KeyError: if name is not among known variants
-        """
-        spec = self.spec
-        args = []
-
-        if activation_value == 'prefix':
-            activation_value = lambda x: spec[x].prefix
-
-        # Defensively look that the name passed as argument is among
-        # variants
-        if name not in self.variants:
-            msg = '"{0}" is not a variant of "{1}"'
-            raise KeyError(msg.format(name, self.name))
-
-        # Create a list of pairs. Each pair includes a configuration
-        # option and whether or not that option is activated
-        if set(self.variants[name].values) == set((True, False)):
-            # BoolValuedVariant carry information about a single option.
-            # Nonetheless, for uniformity of treatment we'll package them
-            # in an iterable of one element.
-            condition = '+{name}'.format(name=name)
-            options = [(name, condition in spec)]
-        else:
-            condition = '{name}={value}'
-            options = [
-                (value, condition.format(name=name, value=value) in spec)
-                for value in self.variants[name].values
-            ]
-
-        # For each allowed value in the list of values
-        for option_value, activated in options:
-            # Search for an override in the package for this value
-            override_name = '{0}_or_{1}_{2}'.format(
-                activation_word, deactivation_word, option_value
-            )
-            line_generator = getattr(self, override_name, None)
-            # If not available use a sensible default
-            if line_generator is None:
-                def _default_generator(is_activated):
-                    if is_activated:
-                        line = '--{0}-{1}'.format(
-                            activation_word, option_value
-                        )
-                        if activation_value is not None and activation_value(option_value):  # NOQA=ignore=E501
-                            line += '={0}'.format(
-                                activation_value(option_value)
-                            )
-                        return line
-                    return '--{0}-{1}'.format(deactivation_word, option_value)
-                line_generator = _default_generator
-            args.append(line_generator(activated))
-        return args
-
-    def with_or_without(self, name, activation_value=None):
-        """Inspects a variant and returns the arguments that activate
-        or deactivate the selected feature(s) for the configure options.
-
-        This function works on all type of variants. For bool-valued variants
-        it will return by default ``--with-{name}`` or ``--without-{name}``.
-        For other kinds of variants it will cycle over the allowed values and
-        return either ``--with-{value}`` or ``--without-{value}``.
-
-        If activation_value is given, then for each possible value of the
-        variant, the option ``--with-{value}=activation_value(value)`` or
-        ``--without-{value}`` will be added depending on whether or not
-        ``variant=value`` is in the spec.
-
-        Args:
-            name (str): name of a valid multi-valued variant
-            activation_value (callable): callable that accepts a single
-                value and returns the parameter to be used leading to an entry
-                of the type ``--with-{name}={parameter}``.
-
-                The special value 'prefix' can also be assigned and will return
-                ``spec[name].prefix`` as activation parameter.
-
-        Returns:
-            list of arguments to configure
-        """
-        return self._activate_or_not(name, 'with', 'without', activation_value)
-
-    def enable_or_disable(self, name, activation_value=None):
-        """Same as :py:meth:`~.AutotoolsPackage.with_or_without` but substitute
-        ``with`` with ``enable`` and ``without`` with ``disable``.
-
-        Args:
-            name (str): name of a valid multi-valued variant
-            activation_value (callable): if present accepts a single value
-                and returns the parameter to be used leading to an entry of the
-                type ``--enable-{name}={parameter}``
-
-                The special value 'prefix' can also be assigned and will return
-                ``spec[name].prefix`` as activation parameter.
-
-        Returns:
-            list of arguments to configure
-        """
-        return self._activate_or_not(
-            name, 'enable', 'disable', activation_value
-        )
-
-    run_after('install')(PackageBase._run_default_install_time_test_callbacks)
-
-    def installcheck(self):
-        """Searches the Makefile for an ``installcheck`` target
-        and runs it if found.
-        """
-        with working_dir(self.build_directory):
-            self._if_make_target_execute('installcheck')
-
-    # Check that self.prefix is there after installation
-    run_after('install')(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/docs/tutorial/examples/Cmake/cmake_class.py b/lib/spack/docs/tutorial/examples/Cmake/cmake_class.py
deleted file mode 100644
index ca9ec6f803c5f027bc271d4738c307927c999aa1..0000000000000000000000000000000000000000
--- a/lib/spack/docs/tutorial/examples/Cmake/cmake_class.py
+++ /dev/null
@@ -1,224 +0,0 @@
-##############################################################################
-# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
-# Produced at the Lawrence Livermore National Laboratory.
-#
-# This file is part of Spack.
-# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
-# LLNL-CODE-647188
-#
-# For details, see https://github.com/spack/spack
-# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License (as
-# published by the Free Software Foundation) version 2.1, February 1999.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
-# conditions of the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-##############################################################################
-
-import inspect
-import os
-import platform
-
-import spack.build_environment
-from llnl.util.filesystem import working_dir, join_path
-from spack.util.environment import filter_system_paths
-from spack.directives import depends_on, variant
-from spack.package import PackageBase, InstallError, run_after
-
-
-class CMakePackage(PackageBase):
-    """Specialized class for packages built using CMake
-
-    For more information on the CMake build system, see:
-    https://cmake.org/cmake/help/latest/
-
-    This class provides three phases that can be overridden:
-
-        1. :py:meth:`~.CMakePackage.cmake`
-        2. :py:meth:`~.CMakePackage.build`
-        3. :py:meth:`~.CMakePackage.install`
-
-    They all have sensible defaults and for many packages the only thing
-    necessary will be to override :py:meth:`~.CMakePackage.cmake_args`.
-    For a finer tuning you may also override:
-
-        +-----------------------------------------------+--------------------+
-        | **Method**                                    | **Purpose**        |
-        +===============================================+====================+
-        | :py:meth:`~.CMakePackage.root_cmakelists_dir` | Location of the    |
-        |                                               | root CMakeLists.txt|
-        +-----------------------------------------------+--------------------+
-        | :py:meth:`~.CMakePackage.build_directory`     | Directory where to |
-        |                                               | build the package  |
-        +-----------------------------------------------+--------------------+
-
-
-    """
-    #: Phases of a CMake package
-    phases = ['cmake', 'build', 'install']
-    #: This attribute is used in UI queries that need to know the build
-    #: system base class
-    build_system_class = 'CMakePackage'
-
-    build_targets = []
-    install_targets = ['install']
-
-    build_time_test_callbacks = ['check']
-
-    #: The build system generator to use.
-    #:
-    #: See ``cmake --help`` for a list of valid generators.
-    #: Currently, "Unix Makefiles" and "Ninja" are the only generators
-    #: that Spack supports. Defaults to "Unix Makefiles".
-    #:
-    #: See https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html
-    #: for more information.
-    generator = 'Unix Makefiles'
-
-    # https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
-    variant('build_type', default='RelWithDebInfo',
-            description='CMake build type',
-            values=('Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel'))
-
-    depends_on('cmake', type='build')
-
-    @property
-    def root_cmakelists_dir(self):
-        """The relative path to the directory containing CMakeLists.txt
-
-        This path is relative to the root of the extracted tarball,
-        not to the ``build_directory``. Defaults to the current directory.
-
-        :return: directory containing CMakeLists.txt
-        """
-        return self.stage.source_path
-
-    @property
-    def std_cmake_args(self):
-        """Standard cmake arguments provided as a property for
-        convenience of package writers
-
-        :return: standard cmake arguments
-        """
-        # standard CMake arguments
-        return CMakePackage._std_args(self)
-
-    @staticmethod
-    def _std_args(pkg):
-        """Computes the standard cmake arguments for a generic package"""
-        try:
-            generator = pkg.generator
-        except AttributeError:
-            generator = 'Unix Makefiles'
-
-        # Make sure a valid generator was chosen
-        valid_generators = ['Unix Makefiles', 'Ninja']
-        if generator not in valid_generators:
-            msg  = "Invalid CMake generator: '{0}'\n".format(generator)
-            msg += "CMakePackage currently supports the following "
-            msg += "generators: '{0}'".format("', '".join(valid_generators))
-            raise InstallError(msg)
-
-        try:
-            build_type = pkg.spec.variants['build_type'].value
-        except KeyError:
-            build_type = 'RelWithDebInfo'
-
-        args = [
-            '-G', generator,
-            '-DCMAKE_INSTALL_PREFIX:PATH={0}'.format(pkg.prefix),
-            '-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type),
-            '-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON'
-        ]
-
-        if platform.mac_ver()[0]:
-            args.append('-DCMAKE_FIND_FRAMEWORK:STRING=LAST')
-
-        # Set up CMake rpath
-        args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=FALSE')
-        rpaths = ':'.join(spack.build_environment.get_rpaths(pkg))
-        args.append('-DCMAKE_INSTALL_RPATH:STRING={0}'.format(rpaths))
-        # CMake's find_package() looks in CMAKE_PREFIX_PATH first, help CMake
-        # to find immediate link dependencies in right places:
-        deps = [d.prefix for d in
-                pkg.spec.dependencies(deptype=('build', 'link'))]
-        deps = filter_system_paths(deps)
-        args.append('-DCMAKE_PREFIX_PATH:STRING={0}'.format(';'.join(deps)))
-        return args
-
-    @property
-    def build_directory(self):
-        """Returns the directory to use when building the package
-
-        :return: directory where to build the package
-        """
-        return join_path(self.stage.source_path, 'spack-build')
-
-    def default_flag_handler(self, spack_env, flag_val):
-        # Relies on being the first thing that can affect the spack_env
-        # EnvironmentModification after it is instantiated or no other
-        # method trying to affect these variables. Currently both are true
-        # flag_val is a tuple (flag, value_list)
-        spack_env.set(flag_val[0].upper(),
-                      ' '.join(flag_val[1]))
-        return []
-
-    def cmake_args(self):
-        """Produces a list containing all the arguments that must be passed to
-        cmake, except:
-
-            * CMAKE_INSTALL_PREFIX
-            * CMAKE_BUILD_TYPE
-
-        which will be set automatically.
-
-        :return: list of arguments for cmake
-        """
-        return []
-
-    def cmake(self, spec, prefix):
-        """Runs ``cmake`` in the build directory"""
-        options = [os.path.abspath(self.root_cmakelists_dir)]
-        options += self.std_cmake_args
-        options += self.cmake_args()
-        with working_dir(self.build_directory, create=True):
-            inspect.getmodule(self).cmake(*options)
-
-    def build(self, spec, prefix):
-        """Make the build targets"""
-        with working_dir(self.build_directory):
-            if self.generator == 'Unix Makefiles':
-                inspect.getmodule(self).make(*self.build_targets)
-            elif self.generator == 'Ninja':
-                inspect.getmodule(self).ninja(*self.build_targets)
-
-    def install(self, spec, prefix):
-        """Make the install targets"""
-        with working_dir(self.build_directory):
-            if self.generator == 'Unix Makefiles':
-                inspect.getmodule(self).make(*self.install_targets)
-            elif self.generator == 'Ninja':
-                inspect.getmodule(self).ninja(*self.install_targets)
-
-    run_after('build')(PackageBase._run_default_build_time_test_callbacks)
-
-    def check(self):
-        """Searches the CMake-generated Makefile for the target ``test``
-        and runs it if found.
-        """
-        with working_dir(self.build_directory):
-            if self.generator == 'Unix Makefiles':
-                self._if_make_target_execute('test')
-            elif self.generator == 'Ninja':
-                self._if_ninja_target_execute('test')
-
-    # Check that self.prefix is there after installation
-    run_after('install')(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/docs/tutorial/examples/Makefile/makefile_class.py b/lib/spack/docs/tutorial/examples/Makefile/makefile_class.py
deleted file mode 100644
index 286b702fc91d8b2ef3f07ec3e50c669f79cf7219..0000000000000000000000000000000000000000
--- a/lib/spack/docs/tutorial/examples/Makefile/makefile_class.py
+++ /dev/null
@@ -1,129 +0,0 @@
-##############################################################################
-# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
-# Produced at the Lawrence Livermore National Laboratory.
-#
-# This file is part of Spack.
-# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
-# LLNL-CODE-647188
-#
-# For details, see https://github.com/spack/spack
-# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License (as
-# published by the Free Software Foundation) version 2.1, February 1999.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
-# conditions of the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-##############################################################################
-
-import inspect
-
-import llnl.util.tty as tty
-from llnl.util.filesystem import working_dir
-from spack.package import PackageBase, run_after
-
-
-class MakefilePackage(PackageBase):
-    """Specialized class for packages that are built using editable Makefiles
-
-    This class provides three phases that can be overridden:
-
-        1. :py:meth:`~.MakefilePackage.edit`
-        2. :py:meth:`~.MakefilePackage.build`
-        3. :py:meth:`~.MakefilePackage.install`
-
-    It is usually necessary to override the :py:meth:`~.MakefilePackage.edit`
-    phase, while :py:meth:`~.MakefilePackage.build` and
-    :py:meth:`~.MakefilePackage.install` have sensible defaults.
-    For a finer tuning you may override:
-
-        +-----------------------------------------------+--------------------+
-        | **Method**                                    | **Purpose**        |
-        +===============================================+====================+
-        | :py:attr:`~.MakefilePackage.build_targets`    | Specify ``make``   |
-        |                                               | targets for the    |
-        |                                               | build phase        |
-        +-----------------------------------------------+--------------------+
-        | :py:attr:`~.MakefilePackage.install_targets`  | Specify ``make``   |
-        |                                               | targets for the    |
-        |                                               | install phase      |
-        +-----------------------------------------------+--------------------+
-        | :py:meth:`~.MakefilePackage.build_directory`  | Directory where the|
-        |                                               | Makefile is located|
-        +-----------------------------------------------+--------------------+
-    """
-    #: Phases of a package that is built with an hand-written Makefile
-    phases = ['edit', 'build', 'install']
-    #: This attribute is used in UI queries that need to know the build
-    #: system base class
-    build_system_class = 'MakefilePackage'
-
-    #: Targets for ``make`` during the :py:meth:`~.MakefilePackage.build`
-    #: phase
-    build_targets = []
-    #: Targets for ``make`` during the :py:meth:`~.MakefilePackage.install`
-    #: phase
-    install_targets = ['install']
-
-    #: Callback names for build-time test
-    build_time_test_callbacks = ['check']
-
-    #: Callback names for install-time test
-    install_time_test_callbacks = ['installcheck']
-
-    @property
-    def build_directory(self):
-        """Returns the directory containing the main Makefile
-
-        :return: build directory
-        """
-        return self.stage.source_path
-
-    def edit(self, spec, prefix):
-        """Edits the Makefile before calling make. This phase cannot
-        be defaulted.
-        """
-        tty.msg('Using default implementation: skipping edit phase.')
-
-    def build(self, spec, prefix):
-        """Calls make, passing :py:attr:`~.MakefilePackage.build_targets`
-        as targets.
-        """
-        with working_dir(self.build_directory):
-            inspect.getmodule(self).make(*self.build_targets)
-
-    def install(self, spec, prefix):
-        """Calls make, passing :py:attr:`~.MakefilePackage.install_targets`
-        as targets.
-        """
-        with working_dir(self.build_directory):
-            inspect.getmodule(self).make(*self.install_targets)
-
-    run_after('build')(PackageBase._run_default_build_time_test_callbacks)
-
-    def check(self):
-        """Searches the Makefile for targets ``test`` and ``check``
-        and runs them if found.
-        """
-        with working_dir(self.build_directory):
-            self._if_make_target_execute('test')
-            self._if_make_target_execute('check')
-
-    run_after('install')(PackageBase._run_default_install_time_test_callbacks)
-
-    def installcheck(self):
-        """Searches the Makefile for an ``installcheck`` target
-        and runs it if found.
-        """
-        with working_dir(self.build_directory):
-            self._if_make_target_execute('installcheck')
-
-    # Check that self.prefix is there after installation
-    run_after('install')(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/docs/tutorial/examples/PyPackage/python_package_class.py b/lib/spack/docs/tutorial/examples/PyPackage/python_package_class.py
deleted file mode 100644
index b07a196fff23b63d6431aa4b517b7b0963808c8b..0000000000000000000000000000000000000000
--- a/lib/spack/docs/tutorial/examples/PyPackage/python_package_class.py
+++ /dev/null
@@ -1,399 +0,0 @@
-##############################################################################
-# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
-# Produced at the Lawrence Livermore National Laboratory.
-#
-# This file is part of Spack.
-# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
-# LLNL-CODE-647188
-#
-# For details, see https://github.com/spack/spack
-# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License (as
-# published by the Free Software Foundation) version 2.1, February 1999.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
-# conditions of the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-##############################################################################
-
-import inspect
-import os
-
-from spack.directives import depends_on, extends
-from spack.package import PackageBase, run_after
-
-from llnl.util.filesystem import working_dir
-
-
-class PythonPackage(PackageBase):
-    """Specialized class for packages that are built using Python
-    setup.py files
-
-    This class provides the following phases that can be overridden:
-
-    * build
-    * build_py
-    * build_ext
-    * build_clib
-    * build_scripts
-    * clean
-    * install
-    * install_lib
-    * install_headers
-    * install_scripts
-    * install_data
-    * sdist
-    * register
-    * bdist
-    * bdist_dumb
-    * bdist_rpm
-    * bdist_wininst
-    * upload
-    * check
-
-    These are all standard setup.py commands and can be found by running:
-
-    .. code-block:: console
-
-       $ python setup.py --help-commands
-
-    By default, only the 'build' and 'install' phases are run, but if you
-    need to run more phases, simply modify your ``phases`` list like so:
-
-    .. code-block:: python
-
-       phases = ['build_ext', 'install', 'bdist']
-
-    Each phase provides a function <phase> that runs:
-
-    .. code-block:: console
-
-       $ python setup.py --no-user-cfg <phase>
-
-    Each phase also has a <phase_args> function that can pass arguments to
-    this call. All of these functions are empty except for the ``install_args``
-    function, which passes ``--prefix=/path/to/installation/directory``.
-
-    If you need to run a phase which is not a standard setup.py command,
-    you'll need to define a function for it like so:
-
-    .. code-block:: python
-
-       def configure(self, spec, prefix):
-           self.setup_py('configure')
-    """
-    # Default phases
-    phases = ['build', 'install']
-
-    # Name of modules that the Python package provides
-    # This is used to test whether or not the installation succeeded
-    # These names generally come from running:
-    #
-    # >>> import setuptools
-    # >>> setuptools.find_packages()
-    #
-    # in the source tarball directory
-    import_modules = []
-
-    # To be used in UI queries that require to know which
-    # build-system class we are using
-    build_system_class = 'PythonPackage'
-
-    #: Callback names for build-time test
-    build_time_test_callbacks = ['test']
-
-    #: Callback names for install-time test
-    install_time_test_callbacks = ['import_module_test']
-
-    extends('python')
-
-    depends_on('python', type=('build', 'run'))
-
-    def setup_file(self):
-        """Returns the name of the setup file to use."""
-        return 'setup.py'
-
-    @property
-    def build_directory(self):
-        """The directory containing the ``setup.py`` file."""
-        return self.stage.source_path
-
-    def python(self, *args, **kwargs):
-        inspect.getmodule(self).python(*args, **kwargs)
-
-    def setup_py(self, *args, **kwargs):
-        setup = self.setup_file()
-
-        with working_dir(self.build_directory):
-            self.python(setup, '--no-user-cfg', *args, **kwargs)
-
-    def _setup_command_available(self, command):
-        """Determines whether or not a setup.py command exists.
-
-        Args:
-            command (str): The command to look for
-
-        Returns:
-            bool: True if the command is found, else False
-        """
-        kwargs = {
-            'output': os.devnull,
-            'error':  os.devnull,
-            'fail_on_error': False
-        }
-
-        python = inspect.getmodule(self).python
-        setup = self.setup_file()
-
-        python(setup, '--no-user-cfg', command, '--help', **kwargs)
-        return python.returncode == 0
-
-    # The following phases and their descriptions come from:
-    #   $ python setup.py --help-commands
-
-    # Standard commands
-
-    def build(self, spec, prefix):
-        """Build everything needed to install."""
-        args = self.build_args(spec, prefix)
-
-        self.setup_py('build', *args)
-
-    def build_args(self, spec, prefix):
-        """Arguments to pass to build."""
-        return []
-
-    def build_py(self, spec, prefix):
-        '''"Build" pure Python modules (copy to build directory).'''
-        args = self.build_py_args(spec, prefix)
-
-        self.setup_py('build_py', *args)
-
-    def build_py_args(self, spec, prefix):
-        """Arguments to pass to build_py."""
-        return []
-
-    def build_ext(self, spec, prefix):
-        """Build C/C++ extensions (compile/link to build directory)."""
-        args = self.build_ext_args(spec, prefix)
-
-        self.setup_py('build_ext', *args)
-
-    def build_ext_args(self, spec, prefix):
-        """Arguments to pass to build_ext."""
-        return []
-
-    def build_clib(self, spec, prefix):
-        """Build C/C++ libraries used by Python extensions."""
-        args = self.build_clib_args(spec, prefix)
-
-        self.setup_py('build_clib', *args)
-
-    def build_clib_args(self, spec, prefix):
-        """Arguments to pass to build_clib."""
-        return []
-
-    def build_scripts(self, spec, prefix):
-        '''"Build" scripts (copy and fixup #! line).'''
-        args = self.build_scripts_args(spec, prefix)
-
-        self.setup_py('build_scripts', *args)
-
-    def clean(self, spec, prefix):
-        """Clean up temporary files from 'build' command."""
-        args = self.clean_args(spec, prefix)
-
-        self.setup_py('clean', *args)
-
-    def clean_args(self, spec, prefix):
-        """Arguments to pass to clean."""
-        return []
-
-    def install(self, spec, prefix):
-        """Install everything from build directory."""
-        args = self.install_args(spec, prefix)
-
-        self.setup_py('install', *args)
-
-    def install_args(self, spec, prefix):
-        """Arguments to pass to install."""
-        args = ['--prefix={0}'.format(prefix)]
-
-        # This option causes python packages (including setuptools) NOT
-        # to create eggs or easy-install.pth files.  Instead, they
-        # install naturally into $prefix/pythonX.Y/site-packages.
-        #
-        # Eggs add an extra level of indirection to sys.path, slowing
-        # down large HPC runs.  They are also deprecated in favor of
-        # wheels, which use a normal layout when installed.
-        #
-        # Spack manages the package directory on its own by symlinking
-        # extensions into the site-packages directory, so we don't really
-        # need the .pth files or egg directories, anyway.
-        if ('py-setuptools' == spec.name or          # this is setuptools, or
-            'py-setuptools' in spec._dependencies):  # it's an immediate dep
-            args += ['--single-version-externally-managed', '--root=/']
-
-        return args
-
-    def install_lib(self, spec, prefix):
-        """Install all Python modules (extensions and pure Python)."""
-        args = self.install_lib_args(spec, prefix)
-
-        self.setup_py('install_lib', *args)
-
-    def install_lib_args(self, spec, prefix):
-        """Arguments to pass to install_lib."""
-        return []
-
-    def install_headers(self, spec, prefix):
-        """Install C/C++ header files."""
-        args = self.install_headers_args(spec, prefix)
-
-        self.setup_py('install_headers', *args)
-
-    def install_headers_args(self, spec, prefix):
-        """Arguments to pass to install_headers."""
-        return []
-
-    def install_scripts(self, spec, prefix):
-        """Install scripts (Python or otherwise)."""
-        args = self.install_scripts_args(spec, prefix)
-
-        self.setup_py('install_scripts', *args)
-
-    def install_scripts_args(self, spec, prefix):
-        """Arguments to pass to install_scripts."""
-        return []
-
-    def install_data(self, spec, prefix):
-        """Install data files."""
-        args = self.install_data_args(spec, prefix)
-
-        self.setup_py('install_data', *args)
-
-    def install_data_args(self, spec, prefix):
-        """Arguments to pass to install_data."""
-        return []
-
-    def sdist(self, spec, prefix):
-        """Create a source distribution (tarball, zip file, etc.)."""
-        args = self.sdist_args(spec, prefix)
-
-        self.setup_py('sdist', *args)
-
-    def sdist_args(self, spec, prefix):
-        """Arguments to pass to sdist."""
-        return []
-
-    def register(self, spec, prefix):
-        """Register the distribution with the Python package index."""
-        args = self.register_args(spec, prefix)
-
-        self.setup_py('register', *args)
-
-    def register_args(self, spec, prefix):
-        """Arguments to pass to register."""
-        return []
-
-    def bdist(self, spec, prefix):
-        """Create a built (binary) distribution."""
-        args = self.bdist_args(spec, prefix)
-
-        self.setup_py('bdist', *args)
-
-    def bdist_args(self, spec, prefix):
-        """Arguments to pass to bdist."""
-        return []
-
-    def bdist_dumb(self, spec, prefix):
-        '''Create a "dumb" built distribution.'''
-        args = self.bdist_dumb_args(spec, prefix)
-
-        self.setup_py('bdist_dumb', *args)
-
-    def bdist_dumb_args(self, spec, prefix):
-        """Arguments to pass to bdist_dumb."""
-        return []
-
-    def bdist_rpm(self, spec, prefix):
-        """Create an RPM distribution."""
-        args = self.bdist_rpm(spec, prefix)
-
-        self.setup_py('bdist_rpm', *args)
-
-    def bdist_rpm_args(self, spec, prefix):
-        """Arguments to pass to bdist_rpm."""
-        return []
-
-    def bdist_wininst(self, spec, prefix):
-        """Create an executable installer for MS Windows."""
-        args = self.bdist_wininst_args(spec, prefix)
-
-        self.setup_py('bdist_wininst', *args)
-
-    def bdist_wininst_args(self, spec, prefix):
-        """Arguments to pass to bdist_wininst."""
-        return []
-
-    def upload(self, spec, prefix):
-        """Upload binary package to PyPI."""
-        args = self.upload_args(spec, prefix)
-
-        self.setup_py('upload', *args)
-
-    def upload_args(self, spec, prefix):
-        """Arguments to pass to upload."""
-        return []
-
-    def check(self, spec, prefix):
-        """Perform some checks on the package."""
-        args = self.check_args(spec, prefix)
-
-        self.setup_py('check', *args)
-
-    def check_args(self, spec, prefix):
-        """Arguments to pass to check."""
-        return []
-
-    # Testing
-
-    def test(self):
-        """Run unit tests after in-place build.
-
-        These tests are only run if the package actually has a 'test' command.
-        """
-        if self._setup_command_available('test'):
-            args = self.test_args(self.spec, self.prefix)
-
-            self.setup_py('test', *args)
-
-    def test_args(self, spec, prefix):
-        """Arguments to pass to test."""
-        return []
-
-    run_after('build')(PackageBase._run_default_build_time_test_callbacks)
-
-    def import_module_test(self):
-        """Attempts to import the module that was just installed.
-
-        This test is only run if the package overrides
-        :py:attr:`import_modules` with a list of module names."""
-
-        # Make sure we are importing the installed modules,
-        # not the ones in the current directory
-        with working_dir('..'):
-            for module in self.import_modules:
-                self.python('-c', 'import {0}'.format(module))
-
-    run_after('install')(PackageBase._run_default_install_time_test_callbacks)
-
-    # Check that self.prefix is there after installation
-    run_after('install')(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/docs/tutorial_buildsystems.rst b/lib/spack/docs/tutorial_buildsystems.rst
index 8f5dce0892384448bb25a455f90d02ab8e269d4d..d92db1306264cc1fb2119cb2aac1f67b833ead5d 100644
--- a/lib/spack/docs/tutorial_buildsystems.rst
+++ b/lib/spack/docs/tutorial_buildsystems.rst
@@ -103,7 +103,7 @@ This will open the :code:`AutotoolsPackage` file in your text editor.
     long examples. We only show what is relevant to the packager.
 
 
-.. literalinclude:: tutorial/examples/Autotools/autotools_class.py
+.. literalinclude:: ../../../lib/spack/spack/build_systems/autotools.py
     :language: python
     :emphasize-lines: 42,45,62
     :lines: 40-95,259-267
@@ -202,7 +202,7 @@ Let's also take a look inside the :code:`MakefilePackage` class:
 Take note of the following:
 
 
-.. literalinclude:: tutorial/examples/Makefile/makefile_class.py
+.. literalinclude:: ../../../lib/spack/spack/build_systems/makefile.py
    :language: python
    :lines: 33-79,89-107
    :emphasize-lines: 48,54,61
@@ -480,7 +480,7 @@ Let's look at these defaults in the :code:`CMakePackage` class:
 And go into a bit of detail on the highlighted sections:
 
 
-.. literalinclude:: tutorial/examples/Cmake/cmake_class.py
+.. literalinclude:: ../../../lib/spack/spack/build_systems/cmake.py
    :language: python
    :lines: 37-92, 94-155, 174-211
    :emphasize-lines: 57,68,86,94,96,99,100,101,102,111,117,135,136
@@ -675,7 +675,7 @@ at the :code:`PythonPackage` class:
 We see the following:
 
 
-.. literalinclude:: tutorial/examples/PyPackage/python_package_class.py
+.. literalinclude:: ../../../lib/spack/spack/build_systems/python.py
    :language: python
    :lines: 35, 161-364
    :linenos:
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index f1cf0d50493ceb17a8e6318a74f669a0e8943979..cab455695ec31a38e0494ea29709fb592dcf745d 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -128,7 +128,6 @@ def __call__(self, *args, **kwargs):
 def set_compiler_environment_variables(pkg, env):
     assert(pkg.spec.concrete)
     compiler = pkg.compiler
-    flags = pkg.spec.compiler_flags
 
     # Set compiler variables used by CMake and autotools
     assert all(key in compiler.link_paths for key in (
@@ -160,11 +159,28 @@ def set_compiler_environment_variables(pkg, env):
     env.set('SPACK_F77_RPATH_ARG', compiler.f77_rpath_arg)
     env.set('SPACK_FC_RPATH_ARG',  compiler.fc_rpath_arg)
 
-    # Add every valid compiler flag to the environment, prefixed with "SPACK_"
+    # Trap spack-tracked compiler flags as appropriate.
+    # env_flags are easy to accidentally override.
+    inject_flags = {}
+    env_flags = {}
+    build_system_flags = {}
+    for flag in spack.spec.FlagMap.valid_compiler_flags():
+        injf, envf, bsf = pkg.flag_handler(flag, pkg.spec.compiler_flags[flag])
+        inject_flags[flag] = injf or []
+        env_flags[flag] = envf or []
+        build_system_flags[flag] = bsf or []
+
+    # Place compiler flags as specified by flag_handler
     for flag in spack.spec.FlagMap.valid_compiler_flags():
         # Concreteness guarantees key safety here
-        if flags[flag] != []:
-            env.set('SPACK_' + flag.upper(), ' '.join(f for f in flags[flag]))
+        if inject_flags[flag]:
+            # variables SPACK_<FLAG> inject flags through wrapper
+            var_name = 'SPACK_{0}'.format(flag.upper())
+            env.set(var_name, ' '.join(f for f in inject_flags[flag]))
+        if env_flags[flag]:
+            # implicit variables
+            env.set(flag.upper(), ' '.join(f for f in env_flags[flag]))
+    pkg.flags_to_build_system_args(build_system_flags)
 
     env.set('SPACK_COMPILER_SPEC', str(pkg.spec.compiler))
 
@@ -559,19 +575,6 @@ def setup_package(pkg, dirty):
     for s in pkg.spec.traverse():
         assert s.package.spec is s
 
-    # Trap spack-tracked compiler flags as appropriate.
-    # Must be before set_compiler_environment_variables
-    # Current implementation of default flag handler relies on this being
-    # the first thing to affect the spack_env (so there is no appending), or
-    # on no other build_environment methods trying to affect these variables
-    # (CFLAGS, CXXFLAGS, etc). Currently both are true, either is sufficient.
-    for flag in spack.spec.FlagMap.valid_compiler_flags():
-        trap_func = getattr(pkg, flag + '_handler',
-                            getattr(pkg, 'default_flag_handler',
-                                    lambda x, y: y[1]))
-        flag_val = pkg.spec.compiler_flags[flag]
-        pkg.spec.compiler_flags[flag] = trap_func(spack_env, (flag, flag_val))
-
     set_compiler_environment_variables(pkg, spack_env)
     set_build_environment_variables(pkg, spack_env, dirty)
     pkg.architecture.platform.setup_platform_environment(pkg, spack_env)
diff --git a/lib/spack/spack/build_systems/autotools.py b/lib/spack/spack/build_systems/autotools.py
index f39eed699744c9f3b97c563d431980867429b776..5ef06754ca1f0ff9517b320b2f3675e2914cde10 100644
--- a/lib/spack/spack/build_systems/autotools.py
+++ b/lib/spack/spack/build_systems/autotools.py
@@ -182,15 +182,6 @@ def build_directory(self):
         """Override to provide another place to build the package"""
         return self.configure_directory
 
-    def default_flag_handler(self, spack_env, flag_val):
-        # Relies on being the first thing that can affect the spack_env
-        # EnvironmentModification after it is instantiated or no other
-        # method trying to affect these variables. Currently both are true
-        # flag_val is a tuple (flag, value_list).
-        spack_env.set(flag_val[0].upper(),
-                      ' '.join(flag_val[1]))
-        return []
-
     @run_before('autoreconf')
     def delete_configure_to_force_update(self):
         if self.force_autoreconf:
@@ -256,12 +247,24 @@ def configure_args(self):
         """
         return []
 
+    def flags_to_build_system_args(self, flags):
+        """Produces a list of all command line arguments to pass specified
+        compiler flags to configure."""
+        # Has to be dynamic attribute due to caching.
+        setattr(self, 'configure_flag_args', [])
+        for flag, values in flags.items():
+            if values:
+                values_str = '{0}={1}'.format(flag.upper(), ' '.join(values))
+                self.configure_flag_args.append(values_str)
+
     def configure(self, spec, prefix):
         """Runs configure with the arguments specified in
         :py:meth:`~.AutotoolsPackage.configure_args`
         and an appropriately set prefix.
         """
-        options = ['--prefix={0}'.format(prefix)] + self.configure_args()
+        options = getattr(self, 'configure_flag_args', [])
+        options += ['--prefix={0}'.format(prefix)]
+        options += self.configure_args()
 
         with working_dir(self.build_directory, create=True):
             inspect.getmodule(self).configure(*options)
diff --git a/lib/spack/spack/build_systems/cmake.py b/lib/spack/spack/build_systems/cmake.py
index 4f8162e6556d3b81bfbd301d1eade3d25a8909c7..e3acb2e8456a267aa398cf1b39494ba2a2bf2d24 100644
--- a/lib/spack/spack/build_systems/cmake.py
+++ b/lib/spack/spack/build_systems/cmake.py
@@ -109,7 +109,9 @@ def std_cmake_args(self):
         :return: standard cmake arguments
         """
         # standard CMake arguments
-        return CMakePackage._std_args(self)
+        std_cmake_args = CMakePackage._std_args(self)
+        std_cmake_args += getattr(self, 'cmake_flag_args', [])
+        return std_cmake_args
 
     @staticmethod
     def _std_args(pkg):
@@ -157,6 +159,44 @@ def _std_args(pkg):
         args.append('-DCMAKE_PREFIX_PATH:STRING={0}'.format(';'.join(deps)))
         return args
 
+    def flags_to_build_system_args(self, flags):
+        """Produces a list of all command line arguments to pass the specified
+        compiler flags to cmake. Note CMAKE does not have a cppflags option,
+        so cppflags will be added to cflags, cxxflags, and fflags to mimic the
+        behavior in other tools."""
+        # Has to be dynamic attribute due to caching
+        setattr(self, 'cmake_flag_args', [])
+
+        flag_string = '-DCMAKE_{0}_FLAGS={1}'
+        langs = {'C': 'c', 'CXX': 'cxx', 'Fortran': 'f'}
+
+        # Handle language compiler flags
+        for lang, pre in langs.items():
+            flag = pre + 'flags'
+            # cmake has no explicit cppflags support -> add it to all langs
+            lang_flags = ' '.join(flags.get(flag, []) + flags.get('cppflags',
+                                                                  []))
+            if lang_flags:
+                self.cmake_flag_args.append(flag_string.format(lang,
+                                                               lang_flags))
+
+        # Cmake has different linker arguments for different build types.
+        # We specify for each of them.
+        if flags['ldflags']:
+            ldflags = ' '.join(flags['ldflags'])
+            ld_string = '-DCMAKE_{0}_LINKER_FLAGS={1}'
+            # cmake has separate linker arguments for types of builds.
+            for type in ['EXE', 'MODULE', 'SHARED', 'STATIC']:
+                self.cmake_flag_args.append(ld_string.format(type, ldflags))
+
+        # CMake has libs options separated by language. Apply ours to each.
+        if flags['ldlibs']:
+            libs_flags = ' '.join(flags['ldlibs'])
+            libs_string = '-DCMAKE_{0}_STANDARD_LIBRARIES={1}'
+            for lang in langs:
+                self.cmake_flag_args.append(libs_string.format(lang,
+                                                               libs_flags))
+
     @property
     def build_directory(self):
         """Returns the directory to use when building the package
@@ -165,15 +205,6 @@ def build_directory(self):
         """
         return join_path(self.stage.source_path, 'spack-build')
 
-    def default_flag_handler(self, spack_env, flag_val):
-        # Relies on being the first thing that can affect the spack_env
-        # EnvironmentModification after it is instantiated or no other
-        # method trying to affect these variables. Currently both are true
-        # flag_val is a tuple (flag, value_list)
-        spack_env.set(flag_val[0].upper(),
-                      ' '.join(flag_val[1]))
-        return []
-
     def cmake_args(self):
         """Produces a list containing all the arguments that must be passed to
         cmake, except:
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 969daa469c2b8c098db7030880bd7e5e00882b57..f0f092015f62a3f3c6a449e14d3a0d2fed2f7a8a 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -1713,6 +1713,47 @@ def setup_dependent_package(self, module, dependent_spec):
         """
         pass
 
+    def inject_flags(self, name, flags):
+        """
+        flag_handler that injects all flags through the compiler wrapper.
+        """
+        return (flags, None, None)
+
+    def env_flags(self, name, flags):
+        """
+        flag_handler that adds all flags to canonical environment variables.
+        """
+        return (None, flags, None)
+
+    def build_system_flags(self, name, flags):
+        """
+        flag_handler that passes flags to the build system arguments.  Any
+        package using `build_system_flags` must also implement
+        `flags_to_build_system_args`, or derive from a class that
+        implements it.  Currently, AutotoolsPackage and CMakePackage
+        implement it.
+        """
+        return (None, None, flags)
+
+    flag_handler = inject_flags
+    # The flag handler method is called for each of the allowed compiler flags.
+    # It returns a triple of inject_flags, env_flags, build_system_flags.
+    # The flags returned as inject_flags are injected through the spack
+    #  compiler wrappers.
+    # The flags returned as env_flags are passed to the build system through
+    #  the environment variables of the same name.
+    # The flags returned as build_system_flags are passed to the build system
+    #  package subclass to be turned into the appropriate part of the standard
+    #  arguments. This is implemented for build system classes where
+    #  appropriate and will otherwise raise a NotImplementedError.
+
+    def flags_to_build_system_args(self, flags):
+        # Takes flags as a dict name: list of values
+        if any(v for v in flags.values()):
+            msg = 'The {0} build system'.format(self.__class__.__name__)
+            msg += ' cannot take command line arguments for compiler flags'
+            raise NotImplementedError(msg)
+
     @staticmethod
     def uninstall_by_spec(spec, force=False):
         if not os.path.isdir(spec.prefix):
diff --git a/lib/spack/spack/test/cc.py b/lib/spack/spack/test/cc.py
index a50af94ac2d2f5a33569f4e4f6272b3524962bb6..671379c717f8df809fa1c9daebcfdc2cde843fd5 100644
--- a/lib/spack/spack/test/cc.py
+++ b/lib/spack/spack/test/cc.py
@@ -211,8 +211,12 @@ def test_flags(self):
                       ' '.join(test_command) + ' ' +
                       '-lfoo')
 
-        os.environ['SPACK_LDFLAGS'] = ''
-        os.environ['SPACK_LDLIBS'] = ''
+        del os.environ['SPACK_CFLAGS']
+        del os.environ['SPACK_CXXFLAGS']
+        del os.environ['SPACK_FFLAGS']
+        del os.environ['SPACK_CPPFLAGS']
+        del os.environ['SPACK_LDFLAGS']
+        del os.environ['SPACK_LDLIBS']
 
     def test_dep_rpath(self):
         """Ensure RPATHs for root package are added."""
diff --git a/lib/spack/spack/test/flag_handlers.py b/lib/spack/spack/test/flag_handlers.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b785a948f513b3575d18a7b014e121cb4731432
--- /dev/null
+++ b/lib/spack/spack/test/flag_handlers.py
@@ -0,0 +1,176 @@
+##############################################################################
+# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/spack/spack
+# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+import pytest
+import os
+
+import spack.spec
+import spack.build_environment
+
+
+@pytest.fixture()
+def temp_env():
+    old_env = os.environ.copy()
+    yield
+    os.environ = old_env
+
+
+def add_O3_to_build_system_cflags(name, flags):
+    build_system_flags = []
+    if name == 'cflags':
+        build_system_flags.append('-O3')
+    return (flags, None, build_system_flags)
+
+
+@pytest.mark.usefixtures('config')
+class TestFlagHandlers(object):
+    def test_no_build_system_flags(self, temp_env):
+        # Test that both autotools and cmake work getting no build_system flags
+        s1 = spack.spec.Spec('callpath')
+        s1.concretize()
+        pkg1 = spack.repo.get(s1)
+        spack.build_environment.setup_package(pkg1, False)
+
+        s2 = spack.spec.Spec('libelf')
+        s2.concretize()
+        pkg2 = spack.repo.get(s2)
+        spack.build_environment.setup_package(pkg2, False)
+
+        # Use cppflags as a canary
+        assert 'SPACK_CPPFLAGS' not in os.environ
+        assert 'CPPFLAGS' not in os.environ
+
+    def test_inject_flags(self, temp_env):
+        s = spack.spec.Spec('mpileaks cppflags=-g')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = pkg.inject_flags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert os.environ['SPACK_CPPFLAGS'] == '-g'
+        assert 'CPPFLAGS' not in os.environ
+
+    def test_env_flags(self, temp_env):
+        s = spack.spec.Spec('mpileaks cppflags=-g')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = pkg.env_flags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert os.environ['CPPFLAGS'] == '-g'
+        assert 'SPACK_CPPFLAGS' not in os.environ
+
+    def test_build_system_flags_cmake(self, temp_env):
+        s = spack.spec.Spec('callpath cppflags=-g')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = pkg.build_system_flags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert 'SPACK_CPPFLAGS' not in os.environ
+        assert 'CPPFLAGS' not in os.environ
+
+        expected = set(['-DCMAKE_C_FLAGS=-g', '-DCMAKE_CXX_FLAGS=-g',
+                        '-DCMAKE_Fortran_FLAGS=-g'])
+        assert set(pkg.cmake_flag_args) == expected
+
+    def test_build_system_flags_autotools(self, temp_env):
+        s = spack.spec.Spec('libelf cppflags=-g')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = pkg.build_system_flags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert 'SPACK_CPPFLAGS' not in os.environ
+        assert 'CPPFLAGS' not in os.environ
+
+        assert 'CPPFLAGS=-g' in pkg.configure_flag_args
+
+    def test_build_system_flags_not_implemented(self, temp_env):
+        s = spack.spec.Spec('mpileaks cppflags=-g')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = pkg.build_system_flags
+
+        # Test the command line flags method raises a NotImplementedError
+        try:
+            spack.build_environment.setup_package(pkg, False)
+            assert False
+        except NotImplementedError:
+            assert True
+
+    def test_add_build_system_flags_autotools(self, temp_env):
+        s = spack.spec.Spec('libelf cppflags=-g')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = add_O3_to_build_system_cflags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert '-g' in os.environ['SPACK_CPPFLAGS']
+        assert 'CPPFLAGS' not in os.environ
+
+        assert pkg.configure_flag_args == ['CFLAGS=-O3']
+
+    def test_add_build_system_flags_cmake(self, temp_env):
+        s = spack.spec.Spec('callpath cppflags=-g')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = add_O3_to_build_system_cflags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert '-g' in os.environ['SPACK_CPPFLAGS']
+        assert 'CPPFLAGS' not in os.environ
+
+        assert pkg.cmake_flag_args == ['-DCMAKE_C_FLAGS=-O3']
+
+    def test_ld_flags_cmake(self, temp_env):
+        s = spack.spec.Spec('callpath ldflags=-mthreads')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = pkg.build_system_flags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert 'SPACK_LDFLAGS' not in os.environ
+        assert 'LDFLAGS' not in os.environ
+
+        expected = set(['-DCMAKE_EXE_LINKER_FLAGS=-mthreads',
+                        '-DCMAKE_MODULE_LINKER_FLAGS=-mthreads',
+                        '-DCMAKE_SHARED_LINKER_FLAGS=-mthreads',
+                        '-DCMAKE_STATIC_LINKER_FLAGS=-mthreads'])
+        assert set(pkg.cmake_flag_args) == expected
+
+    def test_ld_libs_cmake(self, temp_env):
+        s = spack.spec.Spec('callpath ldlibs=-lfoo')
+        s.concretize()
+        pkg = spack.repo.get(s)
+        pkg.flag_handler = pkg.build_system_flags
+        spack.build_environment.setup_package(pkg, False)
+
+        assert 'SPACK_LDLIBS' not in os.environ
+        assert 'LDLIBS' not in os.environ
+
+        expected = set(['-DCMAKE_C_STANDARD_LIBRARIES=-lfoo',
+                        '-DCMAKE_CXX_STANDARD_LIBRARIES=-lfoo',
+                        '-DCMAKE_Fortran_STANDARD_LIBRARIES=-lfoo'])
+        assert set(pkg.cmake_flag_args) == expected