diff --git a/setup.py b/setup.py
index c9ca4362325581ba670317c044f59be7be14ea3e..0cdb0241f280a5ab8607465fa5eed8cb25ba0520 100644
--- a/setup.py
+++ b/setup.py
@@ -27,10 +27,15 @@ from distutils.command.build_ext import build_ext as _build_ext
 
 from setuptools import Distribution, setup
 
+from distutils.command.build import build
+from setuptools.command.install import install
+
 
 SODIUM_MAJOR = 4
 SODIUM_MINOR = 5
 
+CFFI_DEPENDENCY = "cffi>=0.8"
+
 
 def here(*paths):
     return os.path.abspath(os.path.join(os.path.dirname(__file__), *paths))
@@ -38,7 +43,7 @@ def here(*paths):
 sodium = functools.partial(here, "src/libsodium/src/libsodium")
 
 
-sys.path.append(here("src"))
+sys.path.insert(0, here("src"))
 
 
 import nacl
@@ -61,17 +66,168 @@ def which(name, flags=os.X_OK):  # Taken from twisted
     return result
 
 
-# This hack exists so that we can import nacl here
-sys.path += glob.glob("*.egg")
-
-try:
+def get_ext_modules():
     import nacl._lib
-except ImportError:
-    # installing - there is no cffi yet
-    ext_modules = []
-else:
-    # building bdist - cffi is here!
-    ext_modules = [nacl._lib.ffi.verifier.get_extension()]
+    return [nacl._lib.ffi.verifier.get_extension()]
+
+
+class CFFIBuild(build):
+    """
+    This class exists, instead of just providing ``ext_modules=[...]`` directly
+    in ``setup()`` because importing cryptography requires we have several
+    packages installed first.
+
+    By doing the imports here we ensure that packages listed in
+    ``setup_requires`` are already installed.
+    """
+
+    def finalize_options(self):
+        self.distribution.ext_modules = get_ext_modules()
+        build.finalize_options(self)
+
+
+class CFFIInstall(install):
+    """
+    As a consequence of CFFIBuild and it's late addition of ext_modules, we
+    need the equivalent for the ``install`` command to install into platlib
+    install-dir rather than purelib.
+    """
+
+    def finalize_options(self):
+        self.distribution.ext_modules = get_ext_modules()
+        install.finalize_options(self)
+
+def keywords_with_side_effects(argv):
+    """
+    Get a dictionary with setup keywords that (can) have side effects.
+
+    :param argv: A list of strings with command line arguments.
+    :returns: A dictionary with keyword arguments for the ``setup()`` function.
+
+    This setup.py script uses the setuptools 'setup_requires' feature because
+    this is required by the cffi package to compile extension modules. The
+    purpose of ``keywords_with_side_effects()`` is to avoid triggering the cffi
+    build process as a result of setup.py invocations that don't need the cffi
+    module to be built (setup.py serves the dual purpose of exposing package
+    metadata).
+
+    All of the options listed by ``python setup.py --help`` that print
+    information should be recognized here. The commands ``clean``,
+    ``egg_info``, ``register``, ``sdist`` and ``upload`` are also recognized.
+    Any combination of these options and commands is also supported.
+
+    This function was originally based on the `setup.py script`_ of SciPy (see
+    also the discussion in `pip issue #25`_).
+
+    .. _pip issue #25: https://github.com/pypa/pip/issues/25
+    .. _setup.py script: https://github.com/scipy/scipy/blob/master/setup.py
+    """
+    no_setup_requires_arguments = (
+        '-h', '--help',
+        '-n', '--dry-run',
+        '-q', '--quiet',
+        '-v', '--verbose',
+        '-V', '--version',
+        '--author',
+        '--author-email',
+        '--classifiers',
+        '--contact',
+        '--contact-email',
+        '--description',
+        '--egg-base',
+        '--fullname',
+        '--help-commands',
+        '--keywords',
+        '--licence',
+        '--license',
+        '--long-description',
+        '--maintainer',
+        '--maintainer-email',
+        '--name',
+        '--no-user-cfg',
+        '--obsoletes',
+        '--platforms',
+        '--provides',
+        '--requires',
+        '--url',
+        'clean',
+        'egg_info',
+        'register',
+        'sdist',
+        'upload',
+    )
+
+    def is_short_option(argument):
+        """Check whether a command line argument is a short option."""
+        return len(argument) >= 2 and argument[0] == '-' and argument[1] != '-'
+
+    def expand_short_options(argument):
+        """Expand combined short options into canonical short options."""
+        return ('-' + char for char in argument[1:])
+
+    def argument_without_setup_requirements(argv, i):
+        """Check whether a command line argument needs setup requirements."""
+        if argv[i] in no_setup_requires_arguments:
+            # Simple case: An argument which is either an option or a command
+            # which doesn't need setup requirements.
+            return True
+        elif (is_short_option(argv[i]) and
+              all(option in no_setup_requires_arguments
+                  for option in expand_short_options(argv[i]))):
+            # Not so simple case: Combined short options none of which need
+            # setup requirements.
+            return True
+        elif argv[i - 1:i] == ['--egg-base']:
+            # Tricky case: --egg-info takes an argument which should not make
+            # us use setup_requires (defeating the purpose of this code).
+            return True
+        else:
+            return False
+    if all(argument_without_setup_requirements(argv, i)
+           for i in range(1, len(argv))):
+        return {
+            "cmdclass": {
+                "build": DummyCFFIBuild,
+                "install": DummyCFFIInstall,
+                "build_clib": build_clib,
+                "build_ext": build_ext,
+            }
+        }
+    else:
+        return {
+            "setup_requires": [CFFI_DEPENDENCY],
+            "cmdclass": {
+                "build": CFFIBuild,
+                "install": CFFIInstall,
+                "build_clib": build_clib,
+                "build_ext": build_ext,
+            }
+        }
+
+setup_requires_error = ("Requested setup command that needs 'setup_requires' "
+                        "while command line arguments implied a side effect "
+                        "free command or option.")
+
+class DummyCFFIBuild(build):
+    """
+    This class makes it very obvious when ``keywords_with_side_effects()`` has
+    incorrectly interpreted the command line arguments to ``setup.py build`` as
+    one of the 'side effect free' commands or options.
+    """
+
+    def run(self):
+        raise RuntimeError(setup_requires_error)
+
+
+class DummyCFFIInstall(install):
+    """
+    This class makes it very obvious when ``keywords_with_side_effects()`` has
+    incorrectly interpreted the command line arguments to ``setup.py install``
+    as one of the 'side effect free' commands or options.
+    """
+
+    def run(self):
+        raise RuntimeError(setup_requires_error)
 
 
 def use_system():
@@ -213,11 +369,8 @@ setup(
     author=nacl.__author__,
     author_email=nacl.__email__,
 
-    setup_requires=[
-        "cffi>=0.8",
-    ],
     install_requires=[
-        "cffi>=0.8",
+        CFFI_DEPENDENCY,
         "six",
     ],
     extras_require={
@@ -234,12 +387,7 @@ setup(
     package_data={"nacl._lib": ["*.h"]},
 
     ext_package="nacl._lib",
-    ext_modules=ext_modules,
 
-    cmdclass={
-        "build_clib": build_clib,
-        "build_ext": build_ext,
-    },
     distclass=Distribution,
     zip_safe=False,
 
@@ -253,5 +401,6 @@ setup(
         "Programming Language :: Python :: 3.2",
         "Programming Language :: Python :: 3.3",
         "Programming Language :: Python :: 3.4",
-    ]
+    ],
+    **keywords_with_side_effects(sys.argv)
 )