Example #1
0
    def bundle_libqpid_proton_extension(self):
        bundledir = "bundled"
        ext_modules = self.distribution.ext_modules

        log.info("Using bundled libqpid-proton")

        if not os.path.exists(bundledir):
            os.makedirs(bundledir)

        bundle.fetch_libqpid_proton(bundledir)

        libqpid_proton_dir = os.path.join(bundledir, 'qpid-proton')

        # NOTE(flaper87): Find prefix. We need to run make ourselves
        libqpid_proton = Extension(
            'cmake_cproton',
            sources = [libqpid_proton_dir],

            # NOTE(flaper87): Disable all bindings, set the prefix to whatever
            # is in `distutils.sysconfig.PREFIX` and finally, disable testing
            # as well. The python binding will be built by this script later
            # on. Don't let cmake do it.
            extra_compile_args = ['-DCMAKE_INSTALL_PREFIX:PATH=%s' % ds_sys.PREFIX,
                                  '-DBUILD_PYTHON=False',
                                  '-DBUILD_JAVA=False',
                                  '-DBUILD_PERL=False',
                                  '-DBUILD_RUBY=False',
                                  '-DBUILD_PHP=False',
                                  '-DBUILD_JAVASCRIPT=False',
                                  '-DBUILD_TESTING=False',
            ]
        )

        # NOTE(flaper87): Register this new extension and make
        # sure it's built and installed *before* `_cproton`.
        self.distribution.ext_modules.insert(0, libqpid_proton)
    def bundle_libqpid_proton_extension(self):
        """The proper version of libqpid-proton is not present on the system,
        so attempt to retrieve the proper libqpid-proton sources and
        include them in the extension.
        """
        setup_path = os.path.dirname(os.path.realpath(__file__))
        base = self.get_finalized_command('build').build_base
        build_include = os.path.join(base, 'include')

        log.info("Bundling qpid-proton into the extension")

        # QPID_PROTON_SRC - (optional) pathname to the Proton C sources.  Can
        # be used to override where this setup gets the Proton C sources from
        # (see bundle.fetch_libqpid_proton())
        if 'QPID_PROTON_SRC' not in os.environ:
            if not os.path.exists(os.path.join(setup_path, 'tox.ini')):
                bundledir = os.path.join(base, "bundled")
                if not os.path.exists(bundledir):
                    os.makedirs(bundledir)
                bundle.fetch_libqpid_proton(bundledir)
                libqpid_proton_dir = os.path.abspath(os.path.join(bundledir, 'qpid-proton'))
            else:
                # This should happen just in **dev** environemnts since
                # tox.ini is not shipped with the driver. It should only
                # be triggered when calling `setup.py`. This can happen either
                # manually or when calling `tox` in the **sdist** step. Tox will
                # defined the `QPID_PROTON_SRC` itself.
                proton_c = os.path.join(setup_path, 'qpid-proton')
                libqpid_proton_dir = os.path.abspath(proton_c)
        else:
            libqpid_proton_dir = os.path.abspath(os.environ['QPID_PROTON_SRC'])

        log.debug("Using libqpid-proton src: %s" % libqpid_proton_dir)

        proton_base = os.path.join(libqpid_proton_dir, 'proton-c')
        proton_src = os.path.join(proton_base, 'src')
        proton_include = os.path.join(proton_base, 'include')

        #
        # Create any generated header files, and put them in build_include:
        #
        if not os.path.exists(build_include):
            os.makedirs(build_include)
            os.mkdir(os.path.join(build_include, 'proton'))

        # Generate `protocol.h` by calling the python
        # script found in the source dir.
        with open(os.path.join(build_include, 'protocol.h'), 'wb') as header:
            subprocess.Popen([sys.executable, os.path.join(proton_src, 'protocol.h.py')],
                              env={'PYTHONPATH': proton_base}, stdout=header)

        # Generate `encodings.h` by calling the python
        # script found in the source dir.
        with open(os.path.join(build_include, 'encodings.h'), 'wb') as header:
            subprocess.Popen([sys.executable,
                              os.path.join(proton_src, 'codec', 'encodings.h.py')],
                              env={'PYTHONPATH': proton_base}, stdout=header)

        # Create a custom, temporary, version.h file mapping the
        # major and minor versions from the downloaded tarbal. This version should
        # match the ones in the bundle module
        with open(os.path.join(build_include, 'proton', 'version.h'), "wb") as ver:
            version_text = """
#ifndef _PROTON_VERSION_H
#define _PROTON_VERSION_H 1
#define PN_VERSION_MAJOR %i
#define PN_VERSION_MINOR %i
#endif /* version.h */
""" % bundle.min_qpid_proton
            ver.write(version_text.encode('utf-8'))

        # Collect all the Proton C files that need to be built.
        # we could've used `glob(.., '*', '*.c')` but I preferred going
        # with an explicit list of subdirs that we can control and expand
        # depending on the version. Specifically, lets avoid adding things
        # we don't need.

        sources = []
        for subdir in ['object', 'framing', 'codec', 'dispatcher',
                       'engine', 'events', 'transport',
                       'message', 'reactor', 'messenger',
                       'handlers', 'posix']:

            sources.extend(glob.glob(os.path.join(proton_src, subdir, '*.c')))

        sources.extend(filter(lambda x: not x.endswith('dump.c'),
                       glob.iglob(os.path.join(proton_src, '*.c'))))

        # Look for any optional libraries that proton needs, and adjust the
        # source list as necessary.
        libraries = []

        # Check whether openssl is installed by poking
        # pkg-config for a minimum version 0. If it's installed, it should
        # return True and we'll use it. Otherwise, we'll use the stub.
        if misc.pkg_config_version(atleast='0', module='openssl'):
            libraries += ['ssl', 'crypto']
            sources.append(os.path.join(proton_src, 'ssl', 'openssl.c'))
        else:
            sources.append(os.path.join(proton_src, 'ssl', 'ssl_stub.c'))

        # create a temp compiler to check for optional compile-time features
        cc = new_compiler(compiler=self.compiler_type)
        cc.output_dir = self.build_temp

        # Some systems need to link to
        # `rt`. Check whether `clock_getttime` is around
        # and if not, link on rt.
        if not cc.has_function('clock_getttime'):
            libraries.append('rt')

        # 0.10 added an implementation for cyrus. Check
        # if it is available before adding the implementation to the sources
        # list. Eventually, `sasl.c` will be added and one of the existing
        # implementations will be used.
        if cc.has_function('sasl_client_done', includes=['sasl/sasl.h'],
                           libraries=['sasl2']):
            libraries.append('sasl2')
            sources.append(os.path.join(proton_src, 'sasl', 'cyrus_sasl.c'))
        else:
            sources.append(os.path.join(proton_src, 'sasl', 'none_sasl.c'))

        sources.append(os.path.join(proton_src, 'sasl', 'sasl.c'))

        # compile all the proton sources.  We'll add the resulting list of
        # objects to the _cproton extension as 'extra objects'.  We do this
        # instead of just lumping all the sources into the extension to prevent
        # any proton-specific compilation flags from affecting the compilation
        # of the generated swig code

        cc = new_compiler(compiler=self.compiler_type)
        ds_sys.customize_compiler(cc)

        objects = cc.compile(sources,
                             # -D flags (None means no value, just define)
                             macros=[('qpid_proton_EXPORTS', None),
                                     ('USE_ATOLL', None),
                                     ('USE_CLOCK_GETTIME', None),
                                     ('USE_STRERROR_R', None)],
                             include_dirs=[build_include,
                                           proton_include,
                                           proton_src],
                             # compiler command line options:
                             extra_postargs=['-std=gnu99'],
                             output_dir=self.build_temp)

        #
        # Now update the _cproton extension instance to include the objects and
        # libraries
        #
        _cproton = self.distribution.ext_modules[-1]
        _cproton.extra_objects = objects
        _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 dependency with libraries required
        # by the Proton objects:
        _cproton.libraries=libraries
Example #3
0
    def bundle_libqpid_proton_extension(self):
        base = self.get_finalized_command('build').build_base
        build_include = os.path.join(base, 'include')
        install = self.get_finalized_command('install').install_base
        install_lib = self.get_finalized_command('install').install_lib
        ext_modules = self.distribution.ext_modules

        log.info("Using bundled libqpid-proton")

        if 'QPID_PROTON_SRC' not in os.environ:
            bundledir = os.path.join(base, "bundled")
            if not os.path.exists(bundledir):
                os.makedirs(bundledir)
            bundle.fetch_libqpid_proton(bundledir)
            libqpid_proton_dir = os.path.abspath(os.path.join(bundledir, 'qpid-proton'))
        else:
            libqpid_proton_dir = os.path.abspath(os.environ['QPID_PROTON_SRC'])

        log.debug("Using libqpid-proton src: %s" % libqpid_proton_dir)

        proton_base = os.path.join(libqpid_proton_dir, 'proton-c')
        proton_src = os.path.join(proton_base, 'src')
        proton_include = os.path.join(proton_base, 'include')

        if not os.path.exists(build_include):
            os.makedirs(build_include)
            os.mkdir(os.path.join(build_include, 'proton'))

        # Generate `protocol.h` by calling the python
        # script found in the source dir.
        with open(os.path.join(build_include, 'protocol.h'), 'wb') as header:
            subprocess.Popen([sys.executable, os.path.join(proton_src, 'protocol.h.py')],
                              env={'PYTHONPATH': proton_base}, stdout=header)

        # Generate `encodings.h` by calling the python
        # script found in the source dir.
        with open(os.path.join(build_include, 'encodings.h'), 'wb') as header:
            subprocess.Popen([sys.executable,
                              os.path.join(proton_src, 'codec', 'encodings.h.py')],
                              env={'PYTHONPATH': proton_base}, stdout=header)

        # Create a custom, temporary, version.h file mapping the
        # major and minor versions from the downloaded tarbal. This version should
        # match the ones in the bundle module
        with open(os.path.join(build_include, 'proton', 'version.h'), "wb") as ver:
            version_text = """
#ifndef _PROTON_VERSION_H
#define _PROTON_VERSION_H 1
#define PN_VERSION_MAJOR %i
#define PN_VERSION_MINOR %i
#endif /* version.h */
""" % bundle.min_qpid_proton
            ver.write(version_text)

        # Collect all the C files that need to be built.
        # we could've used `glob(.., '*', '*.c')` but I preferred going
        # with an explicit list of subdirs that we can control and expand
        # depending on the version. Specifically, lets avoid adding things
        # we don't need.
        sources = []
        libraries = []
        extra_compile_args = [
            '-std=gnu99',
            '-Dqpid_proton_EXPORTS',
            '-DUSE_ATOLL',
            '-DUSE_CLOCK_GETTIME',
            '-DUSE_STRERROR_R',
        ]

        for subdir in ['object', 'framing', 'codec', 'dispatcher',
                       'engine', 'events', 'transport',
                       'message', 'reactor', 'messenger',
                       'handlers', 'posix']:

            sources.extend(glob.glob(os.path.join(proton_src, subdir, '*.c')))

        sources.extend(filter(lambda x: not x.endswith('dump.c'),
                       glob.iglob(os.path.join(proton_src, '*.c'))))

        # Check whether openssl is installed by poking
        # pkg-config for a minimum version 0. If it's installed, it should
        # return True and we'll use it. Otherwise, we'll use the stub.
        if misc.pkg_config_version(atleast='0', module='openssl'):
            libraries += ['ssl', 'crypto']
            sources.append(os.path.join(proton_src, 'ssl', 'openssl.c'))
        else:
            sources.append(os.path.join(proton_src, 'ssl', 'ssl_stub.c'))

        cc = new_compiler(compiler=self.compiler_type)
        cc.output_dir = self.build_temp

        # Some systems need to link to
        # `rt`. Check whether `clock_getttime` is around
        # and if not, link on rt.
        if not cc.has_function('clock_getttime'):
            libraries.append('rt')

        # 0.10 added an implementation for cyrus. Check
        # if it is available before adding the implementation to the sources
        # list. Eventually, `sasl.c` will be added and one of the existing
        # implementations will be used.
        if cc.has_function('sasl_client_done', includes=['sasl/sasl.h'],
                           libraries=['sasl2']):
            libraries.append('sasl2')
            sources.append(os.path.join(proton_src, 'sasl', 'cyrus_sasl.c'))
        else:
            sources.append(os.path.join(proton_src, 'sasl', 'none_sasl.c'))

        sources.append(os.path.join(proton_src, 'sasl', 'sasl.c'))

        # Create an extension for the bundled qpid-proton
        # library and let distutils do the build step for us.
        # This is not the `swig` library... What's going to be built by this
        # `Extension` is qpid-proton itself. For the swig library, pls, see the
        # dependencies in the `setup` function call and how it's extended further
        # down this method.
        libqpid_proton = Extension(
            'libqpid-proton',

            # List of `.c` files that will be compiled.
            # `sources` is defined earlier on in this method and it's mostly
            # filled dynamically except for a few cases where files are added
            # depending on the presence of some libraries.
            sources=sources,

            # Libraries that need to be linked to should
            # be added to this list. `libraries` is defined earlier on
            # in this same method and it's filled depending on some
            # conditions. You'll find comments on each of those.
            libraries=libraries,

            # Changes to this list should be rare.
            # However, this is where new headers' dirs are added.
            # This list translates to `-I....` flags.
            include_dirs=[build_include, proton_src, proton_include],

            # If you need to add a default flag, this is
            # the place. All these compile arguments will be appended to
            # the GCC command. This list of flags is not used during the
            # linking phase.
            extra_compile_args = extra_compile_args,

            # If you need to add flags to the linking phase
            # this is the right place to do it. Just like the compile flags,
            # this is a list of flags that will be appended to the link
            # command.
            extra_link_args = []
        )


        # Extend the `swig` module `Extension` and add a few
        # extra options. For instance, we'll add new library dirs where `swig`
        # should look for headers and libraries. In addition to this, we'll
        # also append a `runtime path` where the qpid-proton library for this
        # swig extension should be looked up from whenever the proton bindings
        # are imported. We need this because the library will live in the
        # site-packages along with the proton bindings instead of being in the
        # common places like `/usr/lib` or `/usr/local/lib`.
        #
        # This is not the place where you'd add "default" flags. If you need to
        # add flags like `-thread` please read the `setup` function call at the
        # bottom of this file and see the examples there.
        _cproton = self.distribution.ext_modules[-1]
        _cproton.library_dirs.append(self.build_lib)
        _cproton.include_dirs.append(proton_include)
        _cproton.include_dirs.append(build_include)
        _cproton.include_dirs.append(os.path.join(proton_src, 'bindings', 'python'))

        _cproton.swig_opts.append('-I%s' % build_include)
        _cproton.swig_opts.append('-I%s' % proton_include)

        _cproton.runtime_library_dirs.extend([install_lib])

        # Register this new extension and make
        # sure it's built and installed *before* `_cproton`.
        self.distribution.ext_modules.insert(0, libqpid_proton)