diff --git a/buildconfig/CMake/Bootstrap.cmake b/buildconfig/CMake/Bootstrap.cmake
index adc1c365176dab0e2dd9d7a054c6040dc9aa7dda..2b24dd2fe46ca287594b54a4baa6484f06829592 100644
--- a/buildconfig/CMake/Bootstrap.cmake
+++ b/buildconfig/CMake/Bootstrap.cmake
@@ -1,20 +1,28 @@
-################################################################################
+# ##############################################################################
 # Configure required dependencies if necessary
-################################################################################
-if( MSVC )
+# ##############################################################################
+option(WITH_PYTHON3 "If true build against Python 3, else use Python 2" OFF)
+
+if(MSVC)
   # Git LFS does not work properly with <= 1.9
-  find_package ( Git 1.9.5 REQUIRED )
-  find_package ( GitLFS REQUIRED)
+  find_package(Git 1.9.5 REQUIRED)
+  find_package(GitLFS REQUIRED)
 
   # Use ExternalProject functionality as it already knows how to do clone/update
-  include ( ExternalProject )
-  set( EXTERNAL_ROOT ${PROJECT_SOURCE_DIR}/external CACHE PATH "Location to clone third party dependencies to" )
-  set( THIRD_PARTY_GIT_URL "https://github.com/mantidproject/thirdparty-msvc2015.git" )
-  set ( THIRD_PARTY_GIT_SHA1 a7bd18f35c8d67e68c3a965a07057efa266fc7d7 )
-  set ( THIRD_PARTY_DIR ${EXTERNAL_ROOT}/src/ThirdParty )
+  include(ExternalProject)
+  set(EXTERNAL_ROOT
+      ${PROJECT_SOURCE_DIR}/external
+      CACHE PATH "Location to clone third party dependencies to"
+  )
+  set(THIRD_PARTY_GIT_URL
+      "https://github.com/mantidproject/thirdparty-msvc2015.git"
+  )
+  set(THIRD_PARTY_GIT_SHA1 a7bd18f35c8d67e68c3a965a07057efa266fc7d7)
+  set(THIRD_PARTY_DIR ${EXTERNAL_ROOT}/src/ThirdParty)
   # Generates a script to do the clone/update in tmp
-  set ( _project_name ThirdParty )
-  ExternalProject_Add( ${_project_name}
+  set(_project_name ThirdParty)
+  externalproject_add(
+    ${_project_name}
     PREFIX ${EXTERNAL_ROOT}
     GIT_REPOSITORY ${THIRD_PARTY_GIT_URL}
     GIT_TAG ${THIRD_PARTY_GIT_SHA1}
@@ -23,74 +31,150 @@ if( MSVC )
     INSTALL_COMMAND ""
     TEST_COMMAND ""
   )
-  set_target_properties ( ${_project_name} PROPERTIES
-                           EXCLUDE_FROM_DEFAULT_BUILD 1
-                           EXCLUDE_FROM_ALL 1)
+  set_target_properties(
+    ${_project_name} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1 EXCLUDE_FROM_ALL 1
+  )
 
   # Do fetch/update now as we need the dependencies to configure
-  set ( _tmp_dir ${EXTERNAL_ROOT}/tmp )
-  if ( NOT EXISTS ${THIRD_PARTY_DIR}/.git )
-    message ( STATUS "Fetching third party dependencies" )
-    # As of git lfs 1.02 the default 'git checkout' behaviour is very slow for a large amount of data. Running the
-    # 'git lfs fetch' command however produces better suitable performance as it downloads everything in parallel.
-    # We there for first clone the bare repository containing the data pointers and update them manually
-    # see https://github.com/github/git-lfs/issues/376 for more information
-    set ( ENV{GIT_LFS_SKIP_SMUDGE} 1 )
-    execute_process ( COMMAND ${CMAKE_COMMAND} ARGS -P ${_tmp_dir}/${_project_name}-gitclone.cmake
-                      RESULT_VARIABLE error_code )
-    if ( error_code )
-      message(FATAL_ERROR "Failed to clone repository: '${THIRD_PARTY_GIT_URL}'")
-    endif ()
-    unset ( ENV{GIT_LFS_SKIP_SMUDGE} )
+  set(_tmp_dir ${EXTERNAL_ROOT}/tmp)
+  if(NOT EXISTS ${THIRD_PARTY_DIR}/.git)
+    message(STATUS "Fetching third party dependencies")
+    # As of git lfs 1.02 the default 'git checkout' behaviour is very slow for a
+    # large amount of data. Running the 'git lfs fetch' command however produces
+    # better suitable performance as it downloads everything in parallel. We
+    # there for first clone the bare repository containing the data pointers and
+    # update them manually see https://github.com/github/git-lfs/issues/376 for
+    # more information
+    set(ENV{GIT_LFS_SKIP_SMUDGE} 1)
+    execute_process(
+      COMMAND ${CMAKE_COMMAND} ARGS -P
+              ${_tmp_dir}/${_project_name}-gitclone.cmake
+      RESULT_VARIABLE error_code
+    )
+    if(error_code)
+      message(
+        FATAL_ERROR "Failed to clone repository: '${THIRD_PARTY_GIT_URL}'"
+      )
+    endif()
+    unset(ENV{GIT_LFS_SKIP_SMUDGE})
     # Fetch the binary data
-    execute_process ( COMMAND ${GIT_EXECUTABLE} lfs fetch
-                      WORKING_DIRECTORY ${THIRD_PARTY_DIR}
-                      RESULT_VARIABLE error_code )
-    if ( error_code )
-      message(FATAL_ERROR "Failed to download third party binary data. Check your network connection")
-    endif ()
+    execute_process(
+      COMMAND ${GIT_EXECUTABLE} lfs fetch
+      WORKING_DIRECTORY ${THIRD_PARTY_DIR}
+      RESULT_VARIABLE error_code
+    )
+    if(error_code)
+      message(
+        FATAL_ERROR
+          "Failed to download third party binary data. Check your network connection"
+      )
+    endif()
     # Checkout the data from the index to the working directory
-    execute_process ( COMMAND ${GIT_EXECUTABLE} lfs checkout
-                      WORKING_DIRECTORY ${THIRD_PARTY_DIR}
-                      RESULT_VARIABLE error_code )
-  else ()
-    message ( STATUS "Updating third party dependencies" )
+    execute_process(
+      COMMAND ${GIT_EXECUTABLE} lfs checkout
+      WORKING_DIRECTORY ${THIRD_PARTY_DIR}
+      RESULT_VARIABLE error_code
+    )
+  else()
+    message(STATUS "Updating third party dependencies")
     # Assume the updates are small & don't run git lfs fetch
-    execute_process ( COMMAND ${CMAKE_COMMAND} ARGS -P ${_tmp_dir}/${_project_name}-gitupdate.cmake
-                      RESULT_VARIABLE error_code )
-    if ( error_code )
-      message(FATAL_ERROR "Failed to update repository: '${THIRD_PARTY_GIT_URL}'")
-    endif ()
-  endif ()
-  unset ( _tmp_dir )
+    execute_process(
+      COMMAND ${CMAKE_COMMAND} ARGS -P
+              ${_tmp_dir}/${_project_name}-gitupdate.cmake
+      RESULT_VARIABLE error_code
+    )
+    if(error_code)
+      message(
+        FATAL_ERROR "Failed to update repository: '${THIRD_PARTY_GIT_URL}'"
+      )
+    endif()
+  endif()
+  unset(_tmp_dir)
 
   # Print out where we are looking for 3rd party stuff
-  option ( WITH_PYTHON3 "If true then build against Python 3.8" OFF )
-  if ( WITH_PYTHON3 )
-    set ( PYTHON_VERSION_MAJOR 3 )
-    set ( PYTHON_VERSION_MINOR 8 )
+  if(WITH_PYTHON3)
+    set(PYTHON_VERSION_MAJOR 3)
+    set(PYTHON_VERSION_MINOR 8)
   else()
-    set ( PYTHON_VERSION_MAJOR 2 )
-    set ( PYTHON_VERSION_MINOR 7 )
+    set(PYTHON_VERSION_MAJOR 2)
+    set(PYTHON_VERSION_MINOR 7)
   endif()
-  set ( THIRD_PARTY_BIN "${THIRD_PARTY_DIR}/bin;${THIRD_PARTY_DIR}/lib/qt4/bin;${THIRD_PARTY_DIR}/lib/qt5/bin;${THIRD_PARTY_DIR}/lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" )
-  message ( STATUS "Third party dependencies are in ${THIRD_PARTY_DIR}" )
-  # Add to the path so that cmake can configure correctly without the user having to do it
-  set ( ENV{PATH} "${THIRD_PARTY_BIN};$ENV{PATH}" )
+  set(PYTHON_EXECUTABLE
+      ${THIRD_PARTY_DIR}/lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/python.exe
+  )
+  set(THIRD_PARTY_BIN
+      "${THIRD_PARTY_DIR}/bin;${THIRD_PARTY_DIR}/lib/qt4/bin;${THIRD_PARTY_DIR}/lib/qt5/bin;${THIRD_PARTY_DIR}/lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}"
+  )
+  message(STATUS "Third party dependencies are in ${THIRD_PARTY_DIR}")
+  # Add to the path so that cmake can configure correctly without the user
+  # having to do it
+  set(ENV{PATH} "${THIRD_PARTY_BIN};$ENV{PATH}")
 
   # Set variables to help CMake find components
-  set ( CMAKE_INCLUDE_PATH "${THIRD_PARTY_DIR}/include" )
-  include_directories ( ${THIRD_PARTY_DIR}/include )
-  set ( CMAKE_LIBRARY_PATH "${THIRD_PARTY_DIR}/lib" )
-  set ( CMAKE_PREFIX_PATH "${THIRD_PARTY_DIR};${THIRD_PARTY_DIR}/lib/qt4" )
-  set ( BOOST_INCLUDEDIR "${CMAKE_INCLUDE_PATH}" )
-  set ( BOOST_LIBRARYDIR "${CMAKE_LIBRARY_PATH}" )
-  set ( Boost_NO_SYSTEM_PATHS TRUE )
+  set(CMAKE_INCLUDE_PATH "${THIRD_PARTY_DIR}/include")
+  include_directories(${THIRD_PARTY_DIR}/include)
+  set(CMAKE_LIBRARY_PATH "${THIRD_PARTY_DIR}/lib")
+  set(CMAKE_PREFIX_PATH "${THIRD_PARTY_DIR};${THIRD_PARTY_DIR}/lib/qt4")
+  set(BOOST_INCLUDEDIR "${CMAKE_INCLUDE_PATH}")
+  set(BOOST_LIBRARYDIR "${CMAKE_LIBRARY_PATH}")
+  set(Boost_NO_SYSTEM_PATHS TRUE)
 else()
+  unset(PYTHON_EXECUTABLE CACHE)
+  if(WITH_PYTHON3)
+    find_program(PYTHON_EXECUTABLE python3)
+    if(NOT PYTHON_EXECUTABLE)
+      message(FATAL_ERROR "Unable to find python3 executable")
+    endif()
+  else()
+    find_program(PYTHON_EXECUTABLE python)
+    if(NOT PYTHON_EXECUTABLE)
+      message(FATAL_ERROR "Unable to find python executable")
+    endif()
+  endif()
+
   if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-    # Homebrew adds qt4 here and we require it to be unlinked
-    # from /usr/local to avoid qt4/qt5 cross talk
+    # Homebrew adds qt4 here and we require it to be unlinked from /usr/local to
+    # avoid qt4/qt5 cross talk
     list(APPEND CMAKE_PREFIX_PATH /usr/local/opt/qt@4)
   endif()
-  find_package ( Git )
+  find_package(Git)
+endif()
+
+# Clean out python variables set from a previous build so they can be
+# rediscovered again
+function(unset_cached_python_variables)
+  foreach(
+    _var
+    PYTHON_INCLUDE_DIR
+    PYTHON_LIBRARY
+    PYTHON_NUMPY_INCLUDE_DIR
+    SIP_INCLUDE_DIR
+    PYQT4_PYUIC
+    PYQT4_SIP_DIR
+    PYQT4_SIP_FLAGS
+    PYQT4_VERSION
+    PYQT4_VERSION_STR
+    PYQT4_VERSION_TAG
+    PYQT5_PYUIC
+    PYQT5_SIP_DIR
+    PYQT5_SIP_FLAGS
+    PYQT5_VERSION
+    PYQT5_VERSION_STR
+    PYQT5_VERSION_TAG
+    PYRCC5_CMD
+  )
+    unset(${_var} CACHE)
+  endforeach()
+endfunction()
+
+# Find python interpreter
+find_package(PythonInterp REQUIRED)
+message(STATUS "Python version is " ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH})
+# Ensure FindPythonLibs finds the correct libraries
+set(Python_ADDITIONAL_VERSIONS ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR})
+
+# Handle switching between previously configured Python 2 & Python 3 builds
+if(PYTHON_INCLUDE_DIR AND NOT PYTHON_INCLUDE_DIR MATCHES ".*${PYTHON_VERSION_MAJOR}\.${PYTHON_VERSION_MINOR}.*" )
+  message(STATUS "Python version has changed. Clearing previous Python configuration." )
+  unset_cached_python_variables()
 endif()
diff --git a/buildconfig/CMake/CommonSetup.cmake b/buildconfig/CMake/CommonSetup.cmake
index 77cefcf267855375405401abad037b903646d809..7e60742bc6c5cf86cfbafabf9c4d1e4a79bba1e8 100644
--- a/buildconfig/CMake/CommonSetup.cmake
+++ b/buildconfig/CMake/CommonSetup.cmake
@@ -105,9 +105,6 @@ else()
   )
 endif()
 
-find_package(PythonInterp)
-set(Python_ADDITIONAL_VERSIONS ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR})
-
 find_package(OpenSSL REQUIRED)
 
 # ##############################################################################
diff --git a/buildconfig/CMake/DarwinSetup.cmake b/buildconfig/CMake/DarwinSetup.cmake
index 076069db34c4f434db7893d78a9bfbb017af0c13..5e42e21096a94a04d3e964cbae75d1cd44907909 100644
--- a/buildconfig/CMake/DarwinSetup.cmake
+++ b/buildconfig/CMake/DarwinSetup.cmake
@@ -59,26 +59,8 @@ set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem ")
 # Set Qt5 dir according to homebrew location
 set(Qt5_DIR /usr/local/opt/qt/lib/cmake/Qt5)
 
-# ##############################################################################
-# Use python libraries associated with PYTHON_EXECUTABLE If unspecified, use
-# first python executable in the PATH.
-# ##############################################################################
-
-# Find the python interpreter to get the version we're using (needed for install
-# commands below)
-find_package(PythonInterp)
-if(PYTHON_VERSION_MAJOR)
-  set(PY_VER "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}")
-  message(
-    STATUS
-      "Python version is "
-      ${PY_VER}
-  )
-else()
-  # Older versions of CMake don't set these variables so just assume 2.7
-  set(PY_VER 2.7)
-endif()
-
+# Python flags
+set(PY_VER "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}")
 execute_process(
   COMMAND
     python${PY_VER}-config
diff --git a/buildconfig/Jenkins/buildscript b/buildconfig/Jenkins/buildscript
index 62ea69f2a8413ad3531e6d72caee7c385ac74680..ebaca8df14cb5935ace46118e59fb026932f40d6 100755
--- a/buildconfig/Jenkins/buildscript
+++ b/buildconfig/Jenkins/buildscript
@@ -40,10 +40,10 @@ function run_with_xvfb {
 ###############################################################################
 if [ $(command -v xvfb-run) ]; then
     echo "Terminating existing Xvfb sessions"
-    
+
     # Kill Xvfb processes
     killall Xvfb || true
-    
+
     # Remove Xvfb X server lock files
     rm -f /tmp/.X${XVFB_SERVER_NUM}-lock
 fi
@@ -143,7 +143,7 @@ if [[ ${PRBUILD} == true ]]; then
     DO_BUILD_DEVDOCS=false
     DO_BUILD_PKG=${BUILD_PACKAGE:-false}
     DO_SYSTEMTESTS=false
-    
+
     if [[ ${ON_RHEL7} == true ]]; then
         # rhel does system testing
         if ! ${SCRIPT_DIR}/check_for_changes docs-gui-only; then
@@ -252,11 +252,11 @@ PY2_BUILD=false
 PY3_BUILD=false
 if [[ ${JOB_NAME} == *python3* ]]; then
     PY3_BUILD=true
-    PYTHON3_EXECUTABLE=$(which python3)
-    DIST_FLAGS="${DIST_FLAGS} -DPYTHON_EXECUTABLE=$PYTHON3_EXECUTABLE"
+    DIST_FLAGS="${DIST_FLAGS} -DWITH_PYTHON3=ON"
     PARAVIEW_DIR="${PARAVIEW_DIR}-python3"
 else
     PY2_BUILD=true
+    DIST_FLAGS="${DIST_FLAGS} -DWITH_PYTHON3=OFF"
 fi
 
 ###############################################################################
@@ -280,12 +280,12 @@ if [[ ${DO_BUILD_PKG} == true ]]; then
         else
             PACKAGE_SUFFIX="unstable"
         fi
-        
+
         if [[ ${PY3_BUILD} == true ]]; then
             # Add '-python3' to package name and install path
             PACKAGE_SUFFIX=${PACKAGE_SUFFIX}-python3
         fi
-        
+
         if [[ ${JOB_NAME} == *release* ]] && [[ ${PY2_BUILD} == true ]] && [[ -z "${PACKAGE_SUFFIX}" ]]; then
             # Traditional install path for python 2 release build
             PACKAGINGVARS="${PACKAGINGVARS} -DCMAKE_INSTALL_PREFIX=/opt/Mantid -DCPACK_PACKAGE_SUFFIX="
@@ -293,7 +293,7 @@ if [[ ${DO_BUILD_PKG} == true ]]; then
             # everything else uses lower-case values
             PACKAGINGVARS="${PACKAGINGVARS} -DCMAKE_INSTALL_PREFIX=/opt/mantid${PACKAGE_SUFFIX} -DCPACK_PACKAGE_SUFFIX=${PACKAGE_SUFFIX}"
         fi
-        
+
         if [[ ${ON_RHEL7} == true ]]; then
             if [[ -n "${RELEASE_NUMBER}" ]]; then
                 RELEASE_NUMBER="1"
@@ -430,7 +430,7 @@ fi
 if [[ ${DO_BUILD_PKG} == true ]]; then
     run_with_xvfb ${CMAKE_EXE} --build . --target docs-qthelp
     ${CPACK_EXE}
-    
+
     # Source tarball on clean build (arbitrarily choose rhel7)
     # Also, parcel up the documentation into a tar file that is easier to move around
     # and labelled by the commit id it was built with. This assumes the Jenkins git plugin
diff --git a/buildconfig/Jenkins/buildscript.bat b/buildconfig/Jenkins/buildscript.bat
index 648328df1502be70f14cc7a6748eaee15b28a056..95f0b3c4f1597be3447a42e29657f14c76aa6e18 100755
--- a/buildconfig/Jenkins/buildscript.bat
+++ b/buildconfig/Jenkins/buildscript.bat
@@ -185,7 +185,7 @@ if not "%JOB_NAME%"=="%JOB_NAME:debug=%" (
   set VATES_OPT_VAL=ON
 )
 
-call cmake.exe -G "%CM_GENERATOR%" -A %CM_ARCH% -DCMAKE_SYSTEM_VERSION=%SDK_VERS% -DCONSOLE=OFF -DENABLE_CPACK=ON -DMAKE_VATES=%VATES_OPT_VAL% -DParaView_DIR=%PARAVIEW_DIR% -DMANTID_DATA_STORE=!MANTID_DATA_STORE! -DUSE_PRECOMPILED_HEADERS=ON %PACKAGE_OPTS% ..
+call cmake.exe -G "%CM_GENERATOR%" -A %CM_ARCH% -DCMAKE_SYSTEM_VERSION=%SDK_VERS% -DCONSOLE=OFF -DENABLE_CPACK=ON -DWITH_PYTHON3=OFF -DMAKE_VATES=%VATES_OPT_VAL% -DParaView_DIR=%PARAVIEW_DIR% -DMANTID_DATA_STORE=!MANTID_DATA_STORE! -DUSE_PRECOMPILED_HEADERS=ON %PACKAGE_OPTS% ..
 
 if ERRORLEVEL 1 exit /B %ERRORLEVEL%