#!/usr/bin/env python
from __future__ import print_function
from setuptools import setup
from setuptools import Distribution
from setuptools.command.sdist import sdist
from setuptools.extension import Extension
import subprocess
import platform
import re
import sys
import os
import shutil
import shlex
SKIP_CYTHON_FILE = '__dont_use_cython__.txt'
if os.path.exists(SKIP_CYTHON_FILE):
print("In distributed package, building from C files...", file=sys.stderr)
SOURCE_EXT = 'c'
else:
try:
from Cython.Build import cythonize
print("Building from Cython files...", file=sys.stderr)
SOURCE_EXT = 'pyx'
except ImportError:
print("Cython not found, building from C files...",
file=sys.stderr)
SOURCE_EXT = 'c'
def get_output(*args, **kwargs):
res = subprocess.check_output(*args, shell=True, **kwargs)
decoded = res.decode('utf-8')
return decoded.strip()
# get the compile and link args
kc = "krb5-config"
posix = os.name != 'nt'
link_args, compile_args = [
shlex.split(os.environ[e], posix=posix) if e in os.environ else None
for e in ['GSSAPI_LINKER_ARGS', 'GSSAPI_COMPILER_ARGS']
]
osx_has_gss_framework = False
if sys.platform == 'darwin':
mac_ver = [int(v) for v in platform.mac_ver()[0].split('.')]
osx_has_gss_framework = (mac_ver >= [10, 7, 0])
winkrb_path = None
if os.name == 'nt':
# Try to find location of MIT kerberos
# First check program files of the appropriate architecture
_pf_path = os.path.join(os.environ['ProgramFiles'], 'MIT', 'Kerberos')
if os.path.exists(_pf_path):
winkrb_path = _pf_path
else:
# Try to detect kinit in PATH
_kinit_path = shutil.which('kinit')
if _kinit_path is None:
print("Failed find MIT kerberos!")
else:
winkrb_path = os.path.dirname(os.path.dirname(_kinit_path))
# Monkey patch distutils if it throws errors getting msvcr.
# For MinGW it won't need it.
from distutils import cygwinccompiler
try:
cygwinccompiler.get_msvcr()
except ValueError:
cygwinccompiler.get_msvcr = lambda *a, **kw: []
if sys.platform.startswith("freebsd"):
# FreeBSD does $PATH backward, for our purposes. That is, the package
# manager's version of the software is in /usr/local, which is in PATH
# *after* the version in /usr. We prefer the package manager's version
# because the Heimdal in base is truly ancient, but this can be overridden
# - either in the "normal" fashion by putting something in PATH in front
# of it, or by removing /usr/local from PATH.
bins = []
for b in os.environ["PATH"].split(":"):
p = f"{b}/krb5-config"
if not os.path.exists(p):
continue
bins.append(p)
if len(bins) > 1 and bins[0] == "/usr/bin/krb5-config" and \
"/usr/local/bin/krb5-config" in bins:
kc = "/usr/local/bin/krb5-config"
print(f"Detected: {kc}")
if link_args is None:
if osx_has_gss_framework:
link_args = ['-framework', 'GSS']
elif winkrb_path:
_libs = os.path.join(
winkrb_path, 'lib', 'amd64' if sys.maxsize > 2 ** 32 else 'i386'
)
link_args = (
['-L%s' % _libs]
+ ['-l%s' % os.path.splitext(lib)[0] for lib in os.listdir(_libs)]
)
elif os.environ.get('MINGW_PREFIX'):
link_args = ['-lgss']
else:
link_args = shlex.split(get_output(f"{kc} --libs gssapi"))
if compile_args is None:
if osx_has_gss_framework:
compile_args = ['-framework', 'GSS', '-DOSX_HAS_GSS_FRAMEWORK']
elif winkrb_path:
compile_args = [
'-I%s' % os.path.join(winkrb_path, 'include'),
'-DMS_WIN64'
]
elif os.environ.get('MINGW_PREFIX'):
compile_args = ['-fPIC']
else:
compile_args = shlex.split(get_output(f"{kc} --cflags gssapi"))
# add in the extra workarounds for different include structures
if winkrb_path:
prefix = winkrb_path
else:
try:
prefix = get_output(f"{kc} gssapi --prefix")
except Exception:
print("WARNING: couldn't find krb5-config; assuming prefix of %s"
% str(sys.prefix))
prefix = sys.prefix
gssapi_ext_h = os.path.join(prefix, 'include/gssapi/gssapi_ext.h')
if os.path.exists(gssapi_ext_h):
compile_args.append("-DHAS_GSSAPI_EXT_H")
# Create a define to detect msys in the headers
if sys.platform == 'msys':
compile_args.append('-D__MSYS__')
# ensure that any specific directories are listed before any generic system
# directories inserted by setuptools
# Also separate out specified libraries as MSBuild requires different args
_link_args = link_args
library_dirs, libraries, link_args = [], [], []
for arg in _link_args:
if arg.startswith('-L'):
library_dirs.append(arg[2:])
elif arg.startswith('-l'):
libraries.append(arg[2:])
else:
link_args.append(arg)
ENABLE_SUPPORT_DETECTION = \
(os.environ.get('GSSAPI_SUPPORT_DETECT', 'true').lower() == 'true')
if ENABLE_SUPPORT_DETECTION:
import ctypes.util
main_lib = os.environ.get('GSSAPI_MAIN_LIB', None)
main_path = ""
if main_lib is None and osx_has_gss_framework:
main_lib = ctypes.util.find_library('GSS')
elif os.environ.get('MINGW_PREFIX'):
main_lib = os.environ.get('MINGW_PREFIX')+'/bin/libgss-3.dll'
elif sys.platform == 'msys':
# Plain msys, not running in MINGW_PREFIX. Try to get the lib from one
_main_lib = (
'/mingw%d/bin/libgss-3.dll'
% (64 if sys.maxsize > 2 ** 32 else 32)
)
if os.path.exists(_main_lib):
main_lib = _main_lib
os.environ['PATH'] += os.pathsep + os.path.dirname(main_lib)
elif main_lib is None:
for opt in libraries:
if opt.startswith('gssapi'):
if os.name == 'nt':
main_lib = '%s.dll' % opt
if winkrb_path:
main_path = os.path.join(winkrb_path, 'bin')
else:
main_lib = 'lib%s.so' % opt
for opt in link_args:
# To support Heimdal on Debian, read the linker path.
if opt.startswith('-Wl,/'):
main_path = opt[4:] + "/"
if main_path == "":
for d in library_dirs:
if os.path.exists(os.path.join(d, main_lib)):
main_path = d
break
if main_lib is None:
raise Exception("Could not find main GSSAPI shared library. Please "
"try setting GSSAPI_MAIN_LIB yourself or setting "
"GSSAPI_SUPPORT_DETECT to 'false'")
GSSAPI_LIB = ctypes.CDLL(os.path.join(main_path, main_lib))
# add in the flag that causes us not to compile from Cython when
# installing from an sdist
class sdist_gssapi(sdist):
def run(self):
if not self.dry_run:
with open(SKIP_CYTHON_FILE, 'w') as flag_file:
flag_file.write('COMPILE_FROM_C_ONLY')
sdist.run(self)
os.remove(SKIP_CYTHON_FILE)
DONT_CYTHONIZE_FOR = ('clean',)
class GSSAPIDistribution(Distribution, object):
def run_command(self, command):
self._last_run_command = command
Distribution.run_command(self, command)
@property
def ext_modules(self):
if SOURCE_EXT != 'pyx':
return getattr(self, '_ext_modules', None)
if getattr(self, '_ext_modules', None) is None:
return None
if getattr(self, '_last_run_command', None) in DONT_CYTHONIZE_FOR:
return self._ext_modules
if getattr(self, '_cythonized_ext_modules', None) is None:
self._cythonized_ext_modules = cythonize(
self._ext_modules,
language_level=2,
)
return self._cythonized_ext_modules
@ext_modules.setter
def ext_modules(self, mods):
self._cythonized_ext_modules = None
self._ext_modules = mods
@ext_modules.deleter
def ext_modules(self):
del self._ext_modules
del self._cythonized_ext_modules
def make_extension(name_fmt, module, **kwargs):
"""Helper method to remove the repetition in extension declarations."""
source = name_fmt.replace('.', '/') % module + '.' + SOURCE_EXT
if not os.path.exists(source):
raise OSError(source)
return Extension(
name_fmt % module,
extra_link_args=link_args,
extra_compile_args=compile_args,
library_dirs=library_dirs,
libraries=libraries,
sources=[source],
**kwargs
)
# detect support
def main_file(module):
return make_extension('gssapi.raw.%s', module)
ENUM_EXTS = []
def extension_file(module, canary):
if ENABLE_SUPPORT_DETECTION and not hasattr(GSSAPI_LIB, canary):
print('Skipping the %s extension because it '
'is not supported by your GSSAPI implementation...' % module)
return
try:
ENUM_EXTS.append(
make_extension('gssapi.raw._enum_extensions.ext_%s', module,
include_dirs=['gssapi/raw/'])
)
except OSError:
pass
return make_extension('gssapi.raw.ext_%s', module)
def gssapi_modules(lst):
# filter out missing files
res = [mod for mod in lst if mod is not None]
# add in supported mech files
res.extend(
make_extension('gssapi.raw.mech_%s', mech)
for mech in os.environ.get('GSSAPI_MECHS', 'krb5').split(',')
)
# add in any present enum extension files
res.extend(ENUM_EXTS)
return res
long_desc = re.sub(r'\.\. role:: \w+\(code\)\s*\n\s*.+', '',
re.sub(r':(python|bash|code):', '',
re.sub(r'\.\. code-block:: \w+', '::',
open('README.txt').read())))
install_requires = [
'decorator',
]
setup(
name='gssapi',
version='1.6.11',
author='The Python GSSAPI Team',
author_email='rharwood@redhat.com',
packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
'gssapi.tests'],
description='Python GSSAPI Wrapper',
long_description=long_desc,
license='LICENSE.txt',
url="https://github.com/pythongssapi/python-gssapi",
python_requires=">=3.6.*",
classifiers=[
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Intended Audience :: Developers',
'License :: OSI Approved :: ISC License (ISCL)',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Cython',
'Topic :: Security',
'Topic :: Software Development :: Libraries :: Python Modules'
],
distclass=GSSAPIDistribution,
cmdclass={'sdist': sdist_gssapi},
ext_modules=gssapi_modules([
main_file('misc'),
main_file('exceptions'),
main_file('creds'),
main_file('names'),
main_file('sec_contexts'),
main_file('types'),
main_file('message'),
main_file('oids'),
main_file('cython_converters'),
main_file('chan_bindings'),
extension_file('s4u', 'gss_acquire_cred_impersonate_name'),
extension_file('cred_store', 'gss_store_cred_into'),
extension_file('rfc4178', 'gss_set_neg_mechs'),
extension_file('rfc5587', 'gss_indicate_mechs_by_attrs'),
extension_file('rfc5588', 'gss_store_cred'),
extension_file('rfc5801', 'gss_inquire_saslname_for_mech'),
extension_file('cred_imp_exp', 'gss_import_cred'),
extension_file('dce', 'gss_wrap_iov'),
extension_file('iov_mic', 'gss_get_mic_iov'),
extension_file('ggf', 'gss_inquire_sec_context_by_oid'),
extension_file('set_cred_opt', 'gss_set_cred_option'),
# see ext_rfc6680_comp_oid for more information on this split
extension_file('rfc6680', 'gss_display_name_ext'),
extension_file('rfc6680_comp_oid', 'GSS_C_NT_COMPOSITE_EXPORT'),
# see ext_password{,_add}.pyx for more information on this split
extension_file('password', 'gss_acquire_cred_with_password'),
extension_file('password_add', 'gss_add_cred_with_password'),
]),
keywords=['gssapi', 'security'],
install_requires=install_requires
)