diff --git a/.gitignore b/.gitignore
index c80e00ac7..ca0430898 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,179 @@
-# python artifacts
-*.pyc
-docs/projectq.*.rst
+# Python
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
 *.so
+
+# Distribution / packaging
+.Python
 build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
 var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+docs/doxygen
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# ==============================================================================
+# Prerequisites
+*.d
+
+# C++
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# ==============================================================================
+
+# Windows artifacts
+thumbs.db
+
+# Mac OSX artifacts
+*.DS_Store
\ No newline at end of file
diff --git a/docs/tutorials.rst b/docs/tutorials.rst
index 289af1aef..cec2e75e7 100755
--- a/docs/tutorials.rst
+++ b/docs/tutorials.rst
@@ -25,13 +25,9 @@ or, alternatively, `clone/download <https://github.com/projectq-framework>`_ thi
 ProjectQ comes with a high-performance quantum simulator written in C++. Please see the detailed OS specific installation instructions below to make sure that you are installing the fastest version.
 
 .. note::
-	The setup will try to build a C++-Simulator, which is much faster than the Python implementation. If it fails, you may use the `--without-cppsimulator` parameter, i.e., 
-	
-	.. code-block:: bash
-	
-		python -m pip install --user --global-option=--without-cppsimulator .
-	
-	and the framework will use the **slow Python simulator instead**. Note that this only works if the installation has been tried once without the `--without-cppsimulator` parameter and hence all requirements are now installed. See the instructions below if you want to run larger simulations. The Python simulator works perfectly fine for the small examples (e.g., running Shor's algorithm for factoring 15 or 21).
+	The setup will try to build a C++-Simulator, which is much faster than the Python implementation. If the C++ compilation were to fail, the setup will install a pure Python implementation of the simulator instead. The Python simulator should work fine for small examples (e.g., running Shor's algorithm for factoring 15 or 21).
+
+	If you want to skip the installation of the C++-Simulator altogether, you can define the ``DISABLE_PROJECTQ_CEXT`` environment variable to avoid any compilation steps.
 
 .. note::
 	If building the C++-Simulator does not work out of the box, consider specifying a different compiler. For example:
@@ -40,13 +36,13 @@ ProjectQ comes with a high-performance quantum simulator written in C++. Please
 	
 			env CC=g++-5 python -m pip install --user projectq
 
-	Please note that the compiler you specify must support **C++11**!
+	Please note that the compiler you specify must support at leaste **C++11**!
 
 .. note::
 	Please use pip version v6.1.0 or higher as this ensures that dependencies are installed in the `correct order <https://pip.pypa.io/en/stable/reference/pip_install/#installation-order>`_.
 
 .. note::
-	ProjectQ should be installed on each computer individually as the C++ simulator compilation creates binaries which are optimized for the specific hardware on which it is being installed (potentially using our AVX version and `-march=native`). Therefore, sharing the same ProjectQ installation across different hardware can cause problems.
+	ProjectQ should be installed on each computer individually as the C++ simulator compilation creates binaries which are optimized for the specific hardware on which it is being installed (potentially using our AVX version and `-march=native`). Therefore, sharing the same ProjectQ installation across different hardware may cause some problems.
 
 
 Detailed instructions and OS-specific hints
@@ -70,38 +66,75 @@ Detailed instructions and OS-specific hints
 	
 	.. code-block:: bash
 		
-		sudo pip3 install --user projectq
+		sudo python3 -m pip install --user projectq
+	
+	all dependencies (such as numpy and pybind11) should be installed automatically.
+
+
+**ArchLinux/Manjaro**:
+
+        Make sure that you have a C/C++ compiler installed:
+
+	.. code-block:: bash
+	
+		sudo pacman -Syu gcc
+	
+	You only need to install Python (and the package manager). For version 3, run
+	
+	.. code-block:: bash
+	
+		sudo pacman -Syu python python-pip
+	
+	When you then run
+	
+	.. code-block:: bash
+		
+		sudo python3 -m pip install --user projectq
 	
 	all dependencies (such as numpy and pybind11) should be installed automatically.
 
 
 **Windows**:
 
-	It is easiest to install a pre-compiled version of Python, including numpy and many more useful packages. One way to do so is using, e.g., the Python3.5 installers from `python.org <https://www.python.org/downloads>`_ or `ANACONDA <https://www.continuum.io/downloads>`_. Installing ProjectQ right away will succeed for the (slow) Python simulator (i.e., with the `--without-cppsimulator` flag). For a compiled version of the simulator, install the Visual C++ Build Tools and the Microsoft Windows SDK prior to doing a pip install. The built simulator will not support multi-threading due to the limited OpenMP support of msvc.
+	It is easiest to install a pre-compiled version of Python, including numpy and many more useful packages. One way to do so is using, e.g., the Python 3.7 installers from `python.org <https://www.python.org/downloads>`_ or `ANACONDA <https://www.continuum.io/downloads>`_. Installing ProjectQ right away will succeed for the (slow) Python simulator. For a compiled version of the simulator, install the Visual C++ Build Tools and the Microsoft Windows SDK prior to doing a pip install. The built simulator will not support multi-threading due to the limited OpenMP support of the Visual Studio compiler.
+
+	If the Python executable is added to your PATH (option normally suggested at the end of the Python installation procedure), you can then open a cmdline window (WIN + R, type "cmd" and click *OK*) and enter the following in order to install ProjectQ:
+
+	.. code-block:: batch
+		
+		python -m pip install --user projectq
+	
 
 	Should you want to run multi-threaded simulations, you can install a compiler which supports newer OpenMP versions, such as MinGW GCC and then manually build the C++ simulator with OpenMP enabled.
 
 
 **macOS**:
 
-	These are the steps to install ProjectQ on a new Mac:
+        Similarly to the other platforms, installing ProjectQ without the C++ simulator is really easy:
 
-	In order to install the fast C++ simulator, we require that your system has a C++ compiler (see option 3 below on how to only install the slower Python simulator via the `--without-cppsimulator` parameter)
+	.. code-block:: bash
 
-	Below you will find two options to install the fast C++ simulator. The first one is the easiest and requires only the standard compiler which Apple distributes with XCode. The second option uses macports to install the simulator with additional support for multi-threading by using OpenMP, which makes it slightly faster. We show how to install the required C++ compiler (clang) which supports OpenMP and additionally, we show how to install a newer python version.
+		python3 -m pip install --user projectq
 
-.. note::
-	Depending on your system you might need to use `sudo` for the installation.
+	
+	In order to install the fast C++ simulator, we require that a C++ compiler is installed on your system. There are essentially three options you can choose from:
+
+	1. Using the compiler provided by Apple through the XCode command line tools.
+	2. Using Homebrew
+	3. Using MacPorts
+
+	For both options 2 and 3, you will be required to first install the XCode command line tools
 
-1. Installation using XCode and the default python:
 
-	Install XCode by opening a terminal and running the following command:
+	**Apple XCode command line tool**
+
+	Install the XCode command line tools by opening a terminal window and running the following command:
 
 	.. code-block:: bash
 
 		xcode-select --install
-
-	Next, you will need to install Python and pip. See option 2 for information on how to install a newer python version with macports. Here, we are using the standard python which is preinstalled with macOS. Pip can be installed by:
+	
+	Next, you will need to install Python and pip. See options 2 and 3 for information on how to install a newer python version with either Homebrew or MacPorts. Here, we are using the standard python which is preinstalled with macOS. Pip can be installed by:
 
 	.. code-block:: bash
 
@@ -111,56 +144,64 @@ Detailed instructions and OS-specific hints
 
 	.. code-block:: bash
 
-		python -m pip install --user projectq
+		python3 -m pip install --user projectq
 
+	Note that the compiler provided by Apple is currently not able to compile ProjectQ's multi-threaded code.
 
-2. Installation using macports:
+        **Homebrew**
 
-	Either use the standard python and install pip as shown in option 1 or better use macports to install a newer python version, e.g., Python 3.5 and the corresponding pip. Visit `macports.org <https://www.macports.org/install.php>`_ and install the latest version (afterwards open a new terminal). Then, use macports to install Python 3.5 by
+	First install the XCode command line tools. Then install Homebrew with the following command:
 
 	.. code-block:: bash
 
-		sudo port install python35
+		/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
 
-	It might show a warning that if you intend to use python from the terminal, you should also install
+	Then proceed to install Python as well as a C/C++ compiler (note: gcc installed via Homebrew may lead to some issues):
 
 	.. code-block:: bash
 
-		sudo port install py35-readline
-
-	Install pip by
+		brew install python llvm
+        
+	You should now be able to install ProjectQ with the C++ simulator using the following command:
 
 	.. code-block:: bash
 
-		sudo port install py35-pip
+		env P=/usr/local/opt/llvm/bin CC=$P/clang CXX=$P/clang++ python3 -m pip install --user projectq
+
+
+	**MacPorts**
+
+	Visit `macports.org <https://www.macports.org/install.php>`_ and install the latest version that corresponds to your operating system's version. Afterwards, open a new terminal window.
 
-	Next, we can install ProjectQ with the high performance simulator written in C++. First, we will need to install a suitable compiler with support for **C++11**, OpenMP, and instrinsics. The best option is to install clang 3.9 also using macports (note: gcc installed via macports does not work)
+	Then, use macports to install Python 3.7 by entering the following command
 
 	.. code-block:: bash
 
-		sudo port install clang-3.9
+		sudo port install python37
 
-	ProjectQ is now installed by:
+	It might show a warning that if you intend to use python from the terminal. In this case, you should also install
 
 	.. code-block:: bash
 
-		env CC=clang-mp-3.9 env CXX=clang++-mp-3.9 python3.5 -m pip install --user projectq
+		sudo port install py37-gnureadline
+
+	Install pip by
 
-3. Installation with only the slow Python simulator:
+	.. code-block:: bash
 
-	While this simulator works fine for small examples, it is suggested to install the high performance simulator written in C++.
+		sudo port install py37-pip
 
-	If you just want to install ProjectQ with the (slow) Python simulator and no compiler, then first try to install ProjectQ with the default compiler 
+	Next, we can install ProjectQ with the high performance simulator written in C++. First, we will need to install a suitable compiler with support for **C++11**, OpenMP, and instrinsics. The best option is to install clang 9.0 also using macports (note: gcc installed via macports does not work).
 
 	.. code-block:: bash
 
-		python -m pip install --user projectq
+		sudo port install clang-9.0
 
-	which most likely will fail. Then, try again with the flag ``--without-cppsimulator``:
+	ProjectQ is now installed by:
 
 	.. code-block:: bash
 
-		python -m pip install --user --global-option=--without-cppsimulator projectq
+		env CC=clang-mp-9.0 env CXX=clang++-mp-9.0 /opt/local/bin/python3.7 -m pip install --user projectq
 
 
 The ProjectQ syntax
diff --git a/setup.py b/setup.py
index a0254b0e1..b1cb36321 100755
--- a/setup.py
+++ b/setup.py
@@ -1,29 +1,60 @@
-from setuptools import setup, Extension, find_packages, Feature
-from setuptools.command.build_ext import build_ext
-import sys
-import os
-import setuptools
+# Some of the setup.py code is inspired or copied from SQLAlchemy
 
+# SQLAlchemy was created by Michael Bayer.
 
-# This reads the __version__ variable from projectq/_version.py
-exec(open('projectq/_version.py').read())
+# Major contributing authors include:
 
-# Readme file as long_description:
-long_description = open('README.rst').read()
+# - Michael Bayer <mike_mp@zzzcomputing.com>
+# - Jason Kirtland <jek@discorporate.us>
+# - Gaetan de Menten <gdementen@gmail.com>
+# - Diana Clarke <diana.joan.clarke@gmail.com>
+# - Michael Trier <mtrier@gmail.com>
+# - Philip Jenvey <pjenvey@underboss.org>
+# - Ants Aasma <ants.aasma@gmail.com>
+# - Paul Johnston <paj@pajhome.org.uk>
+# - Jonathan Ellis <jbellis@gmail.com>
 
-# Read in requirements.txt
-with open('requirements.txt', 'r') as f_requirements:
-    requirements = f_requirements.readlines()
-requirements = [r.strip() for r in requirements]
+# Copyright 2005-2019 SQLAlchemy authors and contributors (see above)
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+from __future__ import print_function
+from setuptools import setup, Extension, find_packages
+from distutils.errors import (CompileError, LinkError, CCompilerError,
+                              DistutilsExecError, DistutilsPlatformError)
+from setuptools import Distribution as _Distribution
+from setuptools.command.build_ext import build_ext
+import sys
+import os
+import subprocess
+import platform
+
+# ==============================================================================
+# Helper functions and classes
 
 
 class get_pybind_include(object):
-    """Helper class to determine the pybind11 include path
+    '''Helper class to determine the pybind11 include path
 
     The purpose of this class is to postpone importing pybind11
     until it is actually installed, so that the ``get_include()``
-    method can be invoked. """
-
+    method can be invoked. '''
     def __init__(self, user=False):
         self.user = user
 
@@ -32,142 +63,409 @@ def __str__(self):
         return pybind11.get_include(self.user)
 
 
-cppsim = Feature(
-    'C++ Simulator',
-    standard=True,
-    ext_modules=[
-        Extension(
-            'projectq.backends._sim._cppsim',
-            ['projectq/backends/_sim/_cppsim.cpp'],
-            include_dirs=[
-                # Path to pybind11 headers
-                get_pybind_include(),
-                get_pybind_include(user=True)
-            ],
-            language='c++'
-        ),
-    ],
-)
-
-
-def has_flag(compiler, flagname=None):
-    """
+def important_msgs(*msgs):
+    print('*' * 75)
+    for msg in msgs:
+        print(msg)
+    print('*' * 75)
+
+
+def status_msgs(*msgs):
+    print('-' * 75)
+    for msg in msgs:
+        print('# INFO: ', msg)
+    print('-' * 75)
+
+
+def compiler_test(compiler,
+                  flagname=None,
+                  link=False,
+                  include='',
+                  body='',
+                  postargs=None):
+    '''
     Return a boolean indicating whether a flag name is supported on the
     specified compiler.
-    """
+    '''
     import tempfile
     f = tempfile.NamedTemporaryFile('w', suffix='.cpp', delete=False)
-    f.write('int main (int argc, char **argv) { return 0; }')
+    f.write('{}\nint main (int argc, char **argv) {{ {} return 0; }}'.format(
+        include, body))
     f.close()
     ret = True
-    try:
-        if flagname is None:
-            compiler.compile([f.name])
-        else:
-            compiler.compile([f.name], extra_postargs=[flagname])
-    except:
-        ret = False
-    os.unlink(f.name)
-    return ret
 
+    if postargs is None:
+        postargs = [flagname] if flagname is not None else None
+    elif flagname is not None:
+        postargs.append(flagname)
 
-def knows_intrinsics(compiler):
-    """
-    Return a boolean indicating whether the compiler can handle intrinsics.
-    """
-    import tempfile
-    f = tempfile.NamedTemporaryFile('w', suffix='.cpp', delete=False)
-    f.write('#include <immintrin.h>\nint main (int argc, char **argv) '
-            '{ __m256d neg = _mm256_set1_pd(1.0); }')
-    f.close()
-    ret = True
     try:
-        compiler.compile([f.name], extra_postargs=['-march=native'])
-    except setuptools.distutils.errors.CompileError:
+        exec_name = os.path.join(tempfile.mkdtemp(), 'test')
+
+        if compiler.compiler_type == 'msvc':
+            olderr = os.dup(sys.stderr.fileno())
+            err = open('err.txt', 'w')
+            os.dup2(err.fileno(), sys.stderr.fileno())
+
+        obj_file = compiler.compile([f.name], extra_postargs=postargs)
+        if not os.path.exists(obj_file[0]):
+            raise RuntimeError('')
+        if link:
+            compiler.link_executable(obj_file,
+                                     exec_name,
+                                     extra_postargs=postargs)
+
+        if compiler.compiler_type == 'msvc':
+            err.close()
+            os.dup2(olderr, sys.stderr.fileno())
+            with open('err.txt', 'r') as err_file:
+                if err_file.readlines():
+                    raise RuntimeError('')
+    except (CompileError, LinkError, RuntimeError):
         ret = False
     os.unlink(f.name)
     return ret
 
 
+def _fix_macosx_header_paths(*args):
+    # Fix path to SDK headers if necessary
+    _MACOSX_XCODE_REF_PATH = ('/Applications/Xcode.app/Contents/'
+                              + 'Developer/Platforms/MacOSX.platform/'
+                              + 'Developer')
+    _MACOSX_DEVTOOLS_REF_PATH = '/Library/Developer/CommandLineTools/'
+    _has_xcode = os.path.exists(_MACOSX_XCODE_REF_PATH)
+    _has_devtools = os.path.exists(_MACOSX_DEVTOOLS_REF_PATH)
+    if not _has_xcode and not _has_devtools:
+        important_msgs('ERROR: Must install either Xcode or '
+                       + 'CommandLineTools!')
+        raise BuildFailed()
+
+    def _do_replace(idx, item):
+        if not _has_xcode and _MACOSX_XCODE_REF_PATH in item:
+            compiler_args[idx] = item.replace(_MACOSX_XCODE_REF_PATH,
+                                              _MACOSX_DEVTOOLS_REF_PATH)
+
+        if not _has_devtools and _MACOSX_DEVTOOLS_REF_PATH in item:
+            compiler_args[idx] = item.replace(_MACOSX_DEVTOOLS_REF_PATH,
+                                              _MACOSX_XCODE_REF_PATH)
+
+    for compiler_args in args:
+        for idx, item in enumerate(compiler_args):
+            _do_replace(idx, item)
+
+
+# ------------------------------------------------------------------------------
+
+
+class BuildFailed(Exception):
+    def __init__(self):
+        self.cause = sys.exc_info()[1]  # work around py 2/3 different syntax
+
+
+# ------------------------------------------------------------------------------
+# Python build related variable
+
+cpython = platform.python_implementation() == 'CPython'
+ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
+if sys.platform == 'win32':
+    # 2.6's distutils.msvc9compiler can raise an IOError when failing to
+    # find the compiler
+    ext_errors += (IOError, )
+
+# ==============================================================================
+
+# This reads the __version__ variable from projectq/_version.py
+exec(open('projectq/_version.py').read())
+
+# Readme file as long_description:
+long_description = open('README.rst').read()
+
+# Read in requirements.txt
+with open('requirements.txt', 'r') as f_requirements:
+    requirements = f_requirements.readlines()
+requirements = [r.strip() for r in requirements]
+
+# ------------------------------------------------------------------------------
+# ProjectQ C++ extensions
+
+ext_modules = [
+    Extension(
+        'projectq.backends._sim._cppsim',
+        ['projectq/backends/_sim/_cppsim.cpp'],
+        include_dirs=[
+            # Path to pybind11 headers
+            get_pybind_include(),
+            get_pybind_include(user=True)
+        ],
+        language='c++'),
+]
+
+# ==============================================================================
+
+
 class BuildExt(build_ext):
-    """A custom build extension for adding compiler-specific options."""
+    '''A custom build extension for adding compiler-specific options.'''
     c_opts = {
         'msvc': ['/EHsc'],
         'unix': [],
     }
 
+    def run(self):
+        try:
+            build_ext.run(self)
+        except DistutilsPlatformError:
+            raise BuildFailed()
+
     def build_extensions(self):
+        self._configure_compiler()
+        for ext in self.extensions:
+            ext.extra_compile_args = self.opts
+            ext.extra_link_args = self.link_opts
+        try:
+            build_ext.build_extensions(self)
+        except ext_errors:
+            raise BuildFailed()
+        except ValueError:
+            # this can happen on Windows 64 bit, see Python issue 7511
+            if "'path'" in str(sys.exc_info()[1]):  # works with both py 2/3
+                raise BuildFailed()
+            raise
+
+    def _configure_compiler(self):
         if sys.platform == 'darwin':
-            self.c_opts['unix'] += ['-mmacosx-version-min=10.7']
-            if has_flag(self.compiler, '-stdlib=libc++'):
+            _fix_macosx_header_paths(self.compiler.compiler,
+                                     self.compiler.compiler_so)
+
+            if compiler_test(self.compiler, '-stdlib=libc++'):
                 self.c_opts['unix'] += ['-stdlib=libc++']
 
         ct = self.compiler.compiler_type
-        opts = self.c_opts.get(ct, [])
+        self.opts = self.c_opts.get(ct, [])
+        self.link_opts = []
 
-        if not has_flag(self.compiler):
-            self.warning("Something is wrong with your C++ compiler.\n"
-                         "Failed to compile a simple test program!\n")
-            return
+        if not compiler_test(self.compiler):
+            important_msgs(
+                'ERROR: something is wrong with your C++ compiler.\n'
+                'Failed to compile a simple test program!')
+            raise BuildFailed()
+
+        # ------------------------------
+
+        status_msgs('Configuring OpenMP')
+        self._configure_openmp()
+        status_msgs('Configuring compiler intrinsics')
+        self._configure_intrinsics()
+        status_msgs('Configuring C++ standard')
+        self._configure_cxx_standard()
 
-        openmp = ''
-        if has_flag(self.compiler, '-fopenmp'):
-            openmp = '-fopenmp'
-        elif has_flag(self.compiler, '-qopenmp'):
-            openmp = '-qopenmp'
-        if ct == 'msvc':
-            openmp = ''  # supports only OpenMP 2.0
-
-        if knows_intrinsics(self.compiler):
-            opts.append('-DINTRIN')
-            if ct == 'msvc':
-                opts.append('/arch:AVX')
-            else:
-                opts.append('-march=native')
-                opts.append('-ffast-math')
-
-        opts.append(openmp)
+        # ------------------------------
+        # Other compiler tests
+
+        status_msgs('Other compiler tests')
         if ct == 'unix':
-            if not has_flag(self.compiler, '-std=c++11'):
-                self.warning("Compiler needs to have C++11 support!")
+            if compiler_test(self.compiler, '-fvisibility=hidden'):
+                self.opts.append('-fvisibility=hidden')
+            self.opts.append("-DVERSION_INFO=\"{}\"".format(
+                self.distribution.get_version()))
+        elif ct == 'msvc':
+            self.opts.append("/DVERSION_INFO=\\'{}\\'".format(
+                self.distribution.get_version()))
+
+        status_msgs('Finished configuring compiler!')
+
+    def _configure_openmp(self):
+        if self.compiler.compiler_type == 'msvc':
+            return
+
+        kwargs = {
+            'link': True,
+            'include': '#include <omp.h>',
+            'body': 'int a = omp_get_num_threads(); ++a;'
+        }
+
+        for flag in ['-openmp', '-fopenmp', '-qopenmp', '/Qopenmp']:
+            if compiler_test(self.compiler, flag, **kwargs):
+                self.opts.append(flag)
+                self.link_opts.append(flag)
                 return
 
-            opts.append('-DVERSION_INFO="%s"'
-                        % self.distribution.get_version())
-            opts.append('-std=c++11')
-            if has_flag(self.compiler, '-fvisibility=hidden'):
-                opts.append('-fvisibility=hidden')
-        elif ct == 'msvc':
-            opts.append('/DVERSION_INFO=\\"%s\\"'
-                        % self.distribution.get_version())
-        for ext in self.extensions:
-            ext.extra_compile_args = opts
-            ext.extra_link_args = [openmp]
-        try:
-            build_ext.build_extensions(self)
-        except setuptools.distutils.errors.CompileError:
-            self.warning("")
-
-    def warning(self, warning_text):
-        raise Exception(warning_text + "\nCould not install the C++-Simulator."
-                        "\nProjectQ will default to the (slow) Python "
-                        "simulator.\nUse --without-cppsimulator to skip "
-                        "building the (faster) C++ version of the simulator.")
-
-
-setup(
-    name='projectq',
-    version=__version__,
-    author='ProjectQ',
-    author_email='info@projectq.ch',
-    url='http://www.projectq.ch',
-    description=('ProjectQ - '
-                 'An open source software framework for quantum computing'),
-    long_description=long_description,
-    features={'cppsimulator': cppsim},
-    install_requires=requirements,
-    cmdclass={'build_ext': BuildExt},
-    zip_safe=False,
-    license='Apache 2',
-    packages=find_packages()
-)
+        flag = '-fopenmp'
+        if (sys.platform == 'darwin' and compiler_test(self.compiler, flag)):
+            try:
+                llvm_root = subprocess.check_output(
+                    ['brew', '--prefix', 'llvm']).decode('utf-8')[:-1]
+                compiler_root = subprocess.check_output(
+                    ['which', self.compiler.compiler[0]]).decode('utf-8')[:-1]
+
+                # Only add the flag if the compiler we are using is the one
+                # from HomeBrew
+                if llvm_root in compiler_root:
+                    l_arg = '-L{}/lib'.format(llvm_root)
+                    if compiler_test(self.compiler,
+                                     flag,
+                                     postargs=[l_arg],
+                                     **kwargs):
+                        self.opts.append(flag)
+                        self.link_opts.extend((l_arg, flag))
+                        return
+            except subprocess.CalledProcessError:
+                pass
+
+            try:
+                # Only relevant for MacPorts users with clang-3.7
+                port_path = subprocess.check_output(['which', 'port'
+                                                     ]).decode('utf-8')[:-1]
+                macports_root = os.path.dirname(os.path.dirname(port_path))
+                compiler_root = subprocess.check_output(
+                    ['which', self.compiler.compiler[0]]).decode('utf-8')[:-1]
+
+                # Only add the flag if the compiler we are using is the one
+                # from MacPorts
+                if macports_root in compiler_root:
+                    c_arg = '-I{}/include/libomp'.format(macports_root)
+                    l_arg = '-L{}/lib/libomp'.format(macports_root)
+
+                    if compiler_test(self.compiler,
+                                     flag,
+                                     postargs=[c_arg, l_arg],
+                                     **kwargs):
+                        self.opts.extend((c_arg, flag))
+                        self.link_opts.extend((l_arg, flag))
+                        return
+            except subprocess.CalledProcessError:
+                pass
+
+        important_msgs('WARNING: compiler does not support OpenMP!')
+
+    def _configure_intrinsics(self):
+        for flag in [
+                '-march=native', '-mavx2', '/arch:AVX2', '/arch:CORE-AVX2',
+                '/arch:AVX'
+        ]:
+            if compiler_test(
+                    self.compiler,
+                    flagname=flag,
+                    link=False,
+                    include='#include <immintrin.h>',
+                    body='__m256d neg = _mm256_set1_pd(1.0); (void)neg;'):
+
+                if sys.platform == 'win32':
+                    self.opts.extend(('/DINTRIN', flag))
+                else:
+                    self.opts.extend(('-DINTRIN', flag))
+                break
+
+        for flag in ['-ffast-math', '-fast', '/fast', '/fp:precise']:
+            if compiler_test(self.compiler, flagname=flag):
+                self.opts.append(flag)
+                break
+
+    def _configure_cxx_standard(self):
+        if self.compiler.compiler_type == 'msvc':
+            return
+
+        cxx_standards = [17, 14, 11]
+        if sys.version_info[0] < 3:
+            cxx_standards = [year for year in cxx_standards if year < 17]
+
+        if sys.platform == 'darwin':
+            _, minor_version, _ = [
+                int(i) for i in platform.mac_ver()[0].split('.')
+            ]
+            if minor_version < 14:
+                cxx_standards = [year for year in cxx_standards if year < 17]
+
+        for year in cxx_standards:
+            flag = '-std=c++{}'.format(year)
+            if compiler_test(self.compiler, flag):
+                self.opts.append(flag)
+                return
+            flag = '/Qstd=c++{}'.format(year)
+            if compiler_test(self.compiler, flag):
+                self.opts.append(flag)
+                return
+
+        important_msgs('ERROR: compiler needs to have at least C++11 support!')
+        raise BuildFailed()
+
+
+class Distribution(_Distribution):
+    def has_ext_modules(self):
+        # We want to always claim that we have ext_modules. This will be fine
+        # if we don't actually have them (such as on PyPy) because nothing
+        # will get built, however we don't want to provide an overally broad
+        # Wheel package when building a wheel without C support. This will
+        # ensure that Wheel knows to treat us as if the build output is
+        # platform specific.
+        return True
+
+
+# ==============================================================================
+
+
+def run_setup(with_cext):
+    kwargs = {}
+    if with_cext:
+        kwargs['ext_modules'] = ext_modules
+    else:
+        kwargs['ext_modules'] = []
+
+    setup(name='projectq',
+          version=__version__,
+          author='ProjectQ',
+          author_email='info@projectq.ch',
+          url='http://www.projectq.ch',
+          project_urls={
+              'Documentation': 'https://projectq.readthedocs.io/en/latest/',
+              'Issue Tracker':
+              'https://github.com/ProjectQ-Framework/ProjectQ/',
+          },
+          description=(
+              'ProjectQ - '
+              'An open source software framework for quantum computing'),
+          long_description=long_description,
+          install_requires=requirements,
+          cmdclass={'build_ext': BuildExt},
+          zip_safe=False,
+          license='Apache 2',
+          packages=find_packages(),
+          distclass=Distribution,
+          **kwargs)
+
+
+# ==============================================================================
+
+if not cpython:
+    run_setup(False)
+    important_msgs(
+        'WARNING: C/C++ extensions are not supported on '
+        + 'some features are disabled (e.g. C++ simulator).',
+        'Plain-Python build succeeded.',
+    )
+elif os.environ.get('DISABLE_PROJECTQ_CEXT'):
+    run_setup(False)
+    important_msgs(
+        'DISABLE_PROJECTQ_CEXT is set; '
+        + 'not attempting to build C/C++ extensions.',
+        'Plain-Python build succeeded.',
+    )
+
+else:
+    try:
+        run_setup(True)
+    except BuildFailed as exc:
+        important_msgs(
+            exc.cause,
+            'WARNING: Some C/C++ extensions could not be compiled, '
+            + 'some features are disabled (e.g. C++ simulator).',
+            'Failure information, if any, is above.',
+            'Retrying the build without the C/C++ extensions now.',
+        )
+
+        run_setup(False)
+
+        important_msgs(
+            'WARNING: Some C/C++ extensions could not be compiled, '
+            + 'some features are disabled (e.g. C++ simulator).',
+            'Plain-Python build succeeded.',
+        )
