PROTON-2102: [Python] Work on proton build and python setup to build better python packages
- Now rely on setuptools
  This gives us bdist_wheel
- Slim down source pacakge add swigged files to sources
- Never try to swig at build/install time only use source package swig files
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index aacb665..6414a31 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -174,7 +174,25 @@
                    COMMAND ${CMAKE_COMMAND} -E copy_directory ${PN_C_INCLUDE_DIR} "${py_dist_dir}/include")
 
 add_custom_command(TARGET py_src_dist
-                   COMMAND ${CMAKE_COMMAND} -E copy_directory ${PN_C_SOURCE_DIR} "${py_dist_dir}/src")
+                   COMMAND ${CMAKE_COMMAND} -E copy_directory ${PN_C_SOURCE_DIR}/core "${py_dist_dir}/src/core")
+
+add_custom_command(TARGET py_src_dist
+                   COMMAND ${CMAKE_COMMAND} -E copy_directory ${PN_C_SOURCE_DIR}/compiler "${py_dist_dir}/src/compiler")
+
+add_custom_command(TARGET py_src_dist
+                   COMMAND ${CMAKE_COMMAND} -E copy_directory ${PN_C_SOURCE_DIR}/platform "${py_dist_dir}/src/platform")
+
+add_custom_command(TARGET py_src_dist
+                   COMMAND ${CMAKE_COMMAND} -E copy_directory ${PN_C_SOURCE_DIR}/ssl "${py_dist_dir}/src/ssl")
+
+add_custom_command(TARGET py_src_dist
+                   COMMAND ${CMAKE_COMMAND} -E copy_directory ${PN_C_SOURCE_DIR}/sasl "${py_dist_dir}/src/sasl")
+
+add_custom_command(TARGET py_src_dist
+                   COMMAND ${CMAKE_COMMAND} -E copy ${PN_C_SOURCE_DIR}/encodings.h "${py_dist_dir}/src")
+
+add_custom_command(TARGET py_src_dist
+                   COMMAND ${CMAKE_COMMAND} -E copy ${PN_C_SOURCE_DIR}/protocol.h "${py_dist_dir}/src")
 
 add_custom_command(TARGET py_src_dist
                    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/README.rst "${py_dist_dir}")
diff --git a/python/setup.py.in b/python/setup.py.in
index 16fff1b..c5e1bbb 100644
--- a/python/setup.py.in
+++ b/python/setup.py.in
@@ -45,13 +45,13 @@
 import os
 import shutil
 
+from setuptools import setup, Extension
+from setuptools.command.sdist import sdist
+from setuptools.command.build_ext import build_ext
+from setuptools.command.build_py import build_py
+
 import distutils.sysconfig as ds_sys
 from distutils.ccompiler import new_compiler, get_default_compiler
-from distutils.core import setup, Extension
-from distutils.command.build import build
-from distutils.command.build_ext import build_ext
-from distutils.command.sdist import sdist
-from distutils import errors
 
 from setuputils import log
 from setuputils import misc
@@ -63,22 +63,26 @@
 _PROTON_VERSION_STR = "%d.%d.%d" % _PROTON_VERSION
 
 
-class CheckSDist(sdist):
-
+class Swig(build_ext):
     def run(self):
-        self.distribution.run_command('configure')
+        """Run swig against the sources.  This will cause swig to compile the
+        cproton.i file into a .c file called cproton_wrap.c, and create
+        cproton.py.
+        """
+        ext = Extension('_cproton',
+                             sources=['cproton.i'],
+                             swig_opts=['-threads', '-Iinclude'])
 
-        # Append the source that was removed during
-        # the configuration step.
-        _cproton = self.distribution.ext_modules[-1]
-        _cproton.sources.append('cproton.i')
+        if 'SWIG' in os.environ:
+            self.swig = os.environ['SWIG']
 
-        try:
-            sdist.run(self)
-        finally:
-            for src in ['cproton.py', 'cproton_wrap.c']:
-                if os.path.exists(src):
-                    os.remove(src)
+        self.swig_sources(ext.sources, ext)
+
+
+class CheckSDist(sdist):
+    def run(self):
+        self.distribution.run_command('swig')
+        sdist.run(self)
 
 
 class Configure(build_ext):
@@ -94,30 +98,6 @@
         else:
             return compiler.compiler_type
 
-    def prepare_swig_wrap(self):
-        """Run swig against the sources.  This will cause swig to compile the
-        cproton.i file into a .c file called cproton_wrap.c, and create
-        cproton.py.
-        """
-        ext = self.distribution.ext_modules[-1]
-
-        if 'SWIG' in os.environ:
-            self.swig = os.environ['SWIG']
-
-        try:
-            # This will actually call swig to generate the files
-            # and list the sources.
-            self.swig_sources(ext.sources, ext)
-        except (errors.DistutilsExecError, errors.DistutilsPlatformError) as e:
-            if not (os.path.exists('cproton_wrap.c') or
-                    os.path.exists('cproton.py')):
-                raise e
-
-        # now remove the cproton.i file from the source list so we don't run
-        # swig again.
-        ext.sources = ext.sources[1:]
-        ext.swig_opts = []
-
     def use_bundled_proton(self):
         """The proper version of libqpid-proton-core is not installed on the system,
         so use the included proton-c sources to build the extension
@@ -247,10 +227,6 @@
         _cproton.include_dirs.append(build_include)
         _cproton.include_dirs.append(proton_include)
 
-        # swig will need to access the proton headers:
-        _cproton.swig_opts.append('-I%s' % build_include)
-        _cproton.swig_opts.append('-I%s' % proton_include)
-
         # lastly replace the libqpid-proton-core dependency with libraries required
         # by the Proton objects:
         _cproton.libraries=libraries
@@ -283,38 +259,22 @@
         else:
             # Proton not installed or compatible, use bundled proton-c sources
             self.use_bundled_proton()
-        self.prepare_swig_wrap()
 
 
-class CustomBuildOrder(build):
-    # The sole purpose of this class is to re-order
-    # the commands execution so that `build_ext` is executed *before*
-    # build_py. We need this to make sure `cproton.py` is generated
-    # before the python modules are collected. Otherwise, it won't
-    # be installed.
-    sub_commands = [
-        ('build_ext', build.has_ext_modules),
-        ('build_py', build.has_pure_modules),
-        ('build_clib', build.has_c_libraries),
-        ('build_scripts', build.has_scripts),
-    ]
+class BuildExtFirst(build_py):
+    def run(self):
+        # Make sure swig runs first and adds file etc
+        self.distribution.run_command('build_ext')
+        build_py.run(self)
 
 
 class CheckingBuildExt(build_ext):
-    """Subclass build_ext to build qpid-proton using `cmake`"""
-
     def run(self):
-        # Discover qpid-proton in the system
+        # Discover qpid-proton and prerequisites in the system
         self.distribution.run_command('configure')
         build_ext.run(self)
 
 
-# Override `build_ext` and add `configure`
-cmdclass = {'configure': Configure,
-            'build': CustomBuildOrder,
-            'build_ext': CheckingBuildExt,
-            'sdist': CheckSDist}
-
 setup(name='python-qpid-proton',
       version=_PROTON_VERSION_STR + os.environ.get('PROTON_VERSION_SUFFIX', ''),
       description='An AMQP based messaging library.',
@@ -335,7 +295,13 @@
                    "Programming Language :: Python :: 3.6",
                    "Programming Language :: Python :: 3.7",
                    "Programming Language :: Python :: 3.8"],
-      cmdclass=cmdclass,
+      cmdclass = {
+          'configure': Configure,
+          'swig': Swig,
+          'build_py': BuildExtFirst,
+          'build_ext': CheckingBuildExt,
+          'sdist': CheckSDist
+      },
       extras_require={
           'opentracing': ['opentracing', 'jaeger_client']
       },
@@ -343,7 +309,6 @@
       # installation!  If you make changes below, you may need to update the
       # Configure class above
       ext_modules=[Extension('_cproton',
-                             sources=['cproton.i', 'cproton_wrap.c'],
-                             swig_opts=['-threads'],
+                             sources=['cproton_wrap.c'],
                              extra_compile_args=['-pthread'],
                              libraries=['qpid-proton-core'])])