setup.py (214 lines of code) (raw):
#-------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
#--------------------------------------------------------------------------
import os
import sys
import re
import distutils
import subprocess
import platform
from setuptools import find_packages, monkey, setup, Extension
from setuptools.command.build_ext import build_ext as build_ext_orig
from distutils.extension import Extension
from distutils.version import LooseVersion
from distutils import log as logger
try:
from Cython.Build import cythonize
USE_CYTHON = True
except ImportError:
USE_CYTHON = False
# If the C file doesn't exist and no Cython is available, die
if not os.path.exists("uamqp/c_uamqp.c") and not USE_CYTHON:
raise ValueError("You need to install cython==0.29.21 in order to execute this setup.py if 'uamqp/c_uamqp.c' does not exists")
is_x64 = platform.architecture()[0] == '64bit'
is_win = sys.platform.startswith('win')
is_mac = sys.platform.startswith('darwin')
rebuild_pyx = os.environ.get('UAMQP_REBUILD_PYX', False)
use_openssl = not (is_win or is_mac)
# use_openssl = os.environ.get('UAMQP_USE_OPENSSL', not (is_win or is_mac))
supress_link_flags = os.environ.get("UAMQP_SUPPRESS_LINK_FLAGS", False)
# Version extraction inspired from 'requests'
with open(os.path.join('uamqp', '__init__.py'), 'r') as fd:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
fd.read(), re.MULTILINE).group(1)
cwd = os.path.abspath('.')
# Headers
# Do not use an absolute path in include_dirs, otherwise the build
# will vary depending on the build path
pxd_inc_dir = os.path.join(".", "src", "vendor", "inc")
sys.path.insert(0, os.path.join(cwd, pxd_inc_dir))
latest_windows_sdk_shared_include_path = None
include_dirs = [
pxd_inc_dir,
# azure-macro-utils inc
"./src/vendor/azure-uamqp-c/deps/azure-macro-utils-c/inc",
# umock-c inc
"./src/vendor/azure-uamqp-c/deps/umock-c/inc",
# azure-c-shared-utility inc
"./src/vendor/azure-uamqp-c/deps/azure-c-shared-utility/pal/inc",
"./src/vendor/azure-uamqp-c/deps/azure-c-shared-utility/inc",
# azure-uamqp-c inc
"./src/vendor/azure-uamqp-c/inc",
]
if is_win:
include_dirs.append("./src/vendor/azure-uamqp-c/deps/azure-c-shared-utility/pal/windows")
else:
include_dirs.append("./src/vendor/azure-uamqp-c/deps/azure-c-shared-utility/pal/linux")
# Build unique source pyx
def create_cython_file():
content_includes = ""
# Sort the pyx files otherwise the build will vary
# depending on the filesystem
for f in sorted(os.listdir("./src")):
if f.endswith(".pyx"):
print("Adding {}".format(f))
content_includes += "include \"src/" + f + "\"\n"
c_uamqp_src = os.path.join("uamqp", "c_uamqp.pyx")
with open(c_uamqp_src, 'w') as lib_file:
lib_file.write(content_includes)
return c_uamqp_src
def get_build_env():
build_env = os.environ.copy()
return {k.upper(): v for k, v in build_env.items()}
def get_generator_flags():
flags = ["-G"]
if is_win:
flags.append("\"Visual Studio 17 2022\"")
flags.append("-A")
flags.append("ARM64" if platform.machine() == "ARM64" else "x64" if is_x64 else "Win32")
else:
flags.append("\"Unix Makefiles\"")
return " ".join(flags)
def get_msvc_env(vc_ver):
arch = "amd64" if is_x64 else "x86"
msvc_env = distutils.msvc9compiler.query_vcvarsall(vc_ver, arch)
return {str(k.upper()): str(v) for k, v in msvc_env.items()}
# Compile uamqp
# Inspired by https://stackoverflow.com/a/48015772/4074838
class UAMQPExtension(Extension):
def __init__(self, name):
# don't invoke the original build_ext for this special extension
Extension.__init__(self, name, sources=[])
def create_folder_no_exception(foldername):
try:
os.makedirs(foldername)
except Exception: # Assume it's already there, and not impossible to create
pass
class build_ext(build_ext_orig):
def run(self):
monkey.patch_all()
cmake_build_dir = None
for ext in self.extensions:
if isinstance(ext, UAMQPExtension):
self.build_cmake(ext)
# Now I have built in ext.cmake_build_dir
cmake_build_dir = self.cmake_build_dir
else:
ext.library_dirs += [
cmake_build_dir,
cmake_build_dir + "/deps/azure-c-shared-utility/",
cmake_build_dir + "/Debug/",
cmake_build_dir + "/Release/",
cmake_build_dir + "/deps/azure-c-shared-utility/Debug/",
cmake_build_dir + "/deps/azure-c-shared-utility/Release/"
]
build_ext_orig.run(self)
def build_cmake(self, ext):
cwd = os.getcwd()
# these dirs will be created in build_py, so if you don't have
# any python sources to bundle, the dirs will be missing
self.cmake_build_dir = self.build_temp + "/cmake"
create_folder_no_exception(self.cmake_build_dir)
extdir = self.get_ext_fullpath(ext.name)
create_folder_no_exception(extdir)
logger.info("will build uamqp in %s", self.cmake_build_dir)
os.chdir(cwd + "/" + self.cmake_build_dir)
generator_flags = get_generator_flags()
logger.info("Building with generator flags: {}".format(generator_flags))
build_env = get_build_env()
# Configure
configure_command = [
"cmake",
cwd + "/src/vendor/azure-uamqp-c/",
generator_flags,
"-Duse_openssl:bool={}".format("ON" if use_openssl else "OFF"),
"-Duse_default_uuid:bool=ON ", # Should we use libuuid in the system or light one?
"-Duse_builtin_httpapi:bool=ON ", # Should we use libcurl in the system or light one?
"-Dskip_samples:bool=ON", # Don't compile uAMQP samples binaries
"-DCMAKE_POSITION_INDEPENDENT_CODE=TRUE", # ask for -fPIC
"-DCMAKE_BUILD_TYPE=Release"
]
joined_cmd = " ".join(configure_command)
logger.info("calling %s", joined_cmd)
subprocess.check_call(joined_cmd, shell=True, universal_newlines=True, env=build_env)
compile_command = ["cmake", "--build", ".", "--config", "Release"]
joined_cmd = " ".join(compile_command)
logger.info("calling %s", joined_cmd)
subprocess.check_call(joined_cmd, shell=True, universal_newlines=True, env=build_env)
os.chdir(cwd)
if USE_CYTHON:
create_cython_file()
# Libraries and extra compile args
kwargs = {}
if is_win:
kwargs['libraries'] = [
'uamqp',
'aziotsharedutil',
'AdvAPI32',
'Crypt32',
'ncrypt',
'Secur32',
'schannel',
'RpcRT4',
'WSock32',
'WS2_32']
elif is_mac:
kwargs['extra_compile_args'] = ['-g', '-O0', "-std=gnu99", "-fPIC"]
kwargs['libraries'] = ['uamqp', 'aziotsharedutil']
if use_openssl and not supress_link_flags:
kwargs['libraries'].extend(['azssl', 'azcrypto'])
elif not use_openssl:
kwargs['extra_link_args'] = [
'-framework', 'CoreFoundation',
'-framework', 'CFNetwork',
'-framework', 'Security']
else:
kwargs['extra_compile_args'] = ['-g', '-O0', "-std=gnu99", "-fPIC"]
# SSL before crypto matters: https://bugreports.qt.io/browse/QTBUG-62692
kwargs['libraries'] = ['uamqp', 'aziotsharedutil']
if sys.version_info < (3, 5):
kwargs['libraries'].append('rt')
if not supress_link_flags:
kwargs['libraries'].extend(['ssl', 'crypto'])
# If the C file doesn't exist, build the "c_uamqp.c" file
# That's not perfect, since we build it on a "--help", but should be if cloned from source only.
c_uamqp_src = "uamqp/c_uamqp.c"
if not os.path.exists(c_uamqp_src) or rebuild_pyx:
c_uamqp_src = create_cython_file()
sources = [
c_uamqp_src
]
# Configure the extension
extensions = [
UAMQPExtension("cmake_uamqp"),
Extension(
"uamqp.c_uamqp",
sources=sources,
include_dirs=include_dirs,
**kwargs
)
]
with open('README.md') as f: # , encoding='utf-8'
readme = f.read()
with open('HISTORY.md') as f: # , encoding='utf-8'
history = f.read()
if USE_CYTHON:
extensions = cythonize(extensions)
setup(
name='uamqp',
version=version,
description='AMQP 1.0 Client Library for Python',
long_description=readme + '\n\n' + history,
long_description_content_type="text/markdown",
license='MIT License',
author='Microsoft Corporation',
author_email='azpysdkhelp@microsoft.com',
url='https://github.com/Azure/azure-uamqp-python',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Programming Language :: Cython',
'Programming Language :: Python',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'License :: OSI Approved :: MIT License'
],
zip_safe=False,
include_package_data=True,
packages=find_packages(exclude=["tests"]),
ext_modules=extensions,
install_requires=[
"certifi>=2017.4.17",
],
cmdclass={
'build_ext': build_ext,
},
python_requires=">=3.7",
)