diff --git a/.travis.yml b/.travis.yml index 4ca7d9b0..33108a6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,25 +19,25 @@ stages: if: tag is PRESENT script: -- if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then sudo sed -i '1i 127.0.0.1 test.box' /etc/hosts; fi -- if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then sudo hostname test.box; fi -- if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then source ./.travis/lib-util.sh; fi -- if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then util::docker-run $DISTRO ./.travis/build.sh; fi -- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/build.sh; fi +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sudo sed -i '1i 127.0.0.1 test.box' /etc/hosts; + sudo hostname test.box; + source ./.travis/lib-util.sh; + util::docker-run $DISTRO ./.travis/build.sh; + fi +- if [[ "$TRAVIS_OS_NAME" != "linux" ]]; then ./.travis/build.sh; fi jobs: include: - - stage: verify + - &docker_verify + stage: verify env: DISTRO=fedora:latest PYTHON="2" script: - source ./.travis/lib-util.sh - util::docker-run $DISTRO ./.travis/verify.sh - - stage: verify + - <<: *docker_verify env: DISTRO=fedora:latest PYTHON="3" - script: - - source ./.travis/lib-util.sh - - util::docker-run $DISTRO ./.travis/verify.sh # need to explictly define each builder for test due to different os types @@ -59,17 +59,27 @@ jobs: - stage: test env: DISTRO=fedora:latest PYTHON="2" - - stage: test + - &osx_test + stage: test env: PYTHON="2" KRB5_VER="heimdal" PYENV="2.7.14" os: osx osx_image: xcode9.2 language: generic # causes issues with pyenv installer when set to python - - stage: test + - <<: *osx_test env: PYTHON="3" KRB5_VER="heimdal" PYENV="3.6.3" - os: osx - osx_image: xcode9.2 - language: generic # causes issues with pyenv installer when set to python + + - &win_test + stage: test + env: PYTHON="2" PYENV="2.7.16" EXTRA_BUILDEXT="--compiler=mingw32" + os: windows + language: sh # Windows not supported yet + + - <<: *win_test + env: PYTHON="3" PYENV="3.6.8" + + - <<: *win_test + env: PYTHON="3" PYENV="3.7.3" - stage: deploy latest docs @@ -129,3 +139,27 @@ jobs: skip_cleanup: true on: all_branches: true + + - &win_deploy + stage: deploy + os: windows + script: skip + env: PYTHON="2" PYENV="2.7.16" EXTRA_BUILDEXT="--compiler=mingw32" + before_deploy: + - ./.travis/before-deploy-windows-wheels.sh + deploy: + - provider: pypi + user: + secure: "jUAMucBq+9xH8x9u0I0LOwrs3Zb++KN7FwIIwz2CyAt/+TyyrJzeGJaV+dTiJ1OqcUIFqQG6jopzpnAe4biL1O68PEwz9BphKetFLpLHiFNm/n67LYno6NFonWmxndIy99pOP6NZu29nzSNeYq/KgEHo/5OkqEGOxk//lh7X/OY=" + password: + secure: "ZqywwnR+G5VeM2sStwfLeutOvqbULHtnStjrdYc8WcC/FBVwmH/W48fTlvxrnswmfKx7Eljv0nN4VcBpoFf1tvz4O2oK/tCRpf0N8SvpT0jBx8bLGUxJ1/3Po6rFgBRWgSb/mzKHPKI6fLlQNcNg8lrd9e1j/zgbVRSwNeMUOR8=" + skip_cleanup: true + on: + all_branches: true + distributions: "check" # Hack, see above + + - <<: *win_deploy + env: PYTHON="3" PYENV="3.6.8" + + - <<: *win_deploy + env: PYTHON="3" PYENV="3.7.3" diff --git a/.travis/before-deploy-windows-wheels.sh b/.travis/before-deploy-windows-wheels.sh new file mode 100644 index 00000000..ea8c276b --- /dev/null +++ b/.travis/before-deploy-windows-wheels.sh @@ -0,0 +1,29 @@ +#!/bin/bash -ex + +# See before-deploy.sh for anything unexplained + +source ./.travis/lib-setup.sh +source ./.travis/lib-deploy.sh + +./.travis/build.sh + +# build the wheel +python -m pip install wheel +python setup.py bdist_wheel + +cd dist + +# Rename and checksum the wheel +if [ x"${TRAVIS_TAG#v[0-9]}" = "x${TRAVIS_TAG}" ]; then + PYTHON_GSSAPI_VERSION=${TRAVIS_TAG} +else + PYTHON_GSSAPI_VERSION=${TRAVIS_TAG#v} +fi + +PKG_NAME_VER=$(ls *.whl | sed "s/gssapi-[^-]*-\(.*\)\.whl/python-gssapi-${PYTHON_GSSAPI_VERSION}-\1/") + +cp *.whl "${PKG_NAME_VER}.whl" + +sha512sum --binary ./${PKG_NAME_VER}.whl > ./${PKG_NAME_VER}.sha512sum + +cd .. diff --git a/.travis/build.sh b/.travis/build.sh index 6f5e6445..4d16b597 100755 --- a/.travis/build.sh +++ b/.travis/build.sh @@ -5,7 +5,7 @@ source ./.travis/lib-setup.sh setup::install # always build in-place so that Sphinx can find the modules -python setup.py build_ext --inplace +python setup.py build_ext --inplace $EXTRA_BUILDEXT BUILD_RES=$? if [ x"$KRB5_VER" = "xheimdal" ]; then @@ -13,6 +13,11 @@ if [ x"$KRB5_VER" = "xheimdal" ]; then exit $BUILD_RES fi +if [ "$TRAVIS_OS_NAME" == "windows" ]; then + # Windows can't run tests yet, so just exit + exit $BUILD_RES +fi + if [ $BUILD_RES -ne 0 ]; then # if the build failed, don't run the tests exit $BUILD_RES diff --git a/.travis/lib-setup.sh b/.travis/lib-setup.sh index dd1edbe7..be069454 100644 --- a/.travis/lib-setup.sh +++ b/.travis/lib-setup.sh @@ -75,6 +75,24 @@ setup::macos::install() { pip install --install-option='--no-cython-compile' cython } +setup::windows::install() { + # Install the right Python version and MIT Kerberos + choco install python"${PYENV:0:1}" --version $PYENV + choco install mitkerberos --install-arguments "'ADDLOCAL=ALL'" || true + PYPATH="/c/Python${PYENV:0:1}${PYENV:2:1}" + # Update path to include them + export PATH="$PYPATH:$PYPATH/Scripts:/c/Program Files/MIT/Kerberos/bin:$PATH" + + if [ "${PYENV:0:1}" == "2" ]; then + choco install vcredist2008 + # Skip dotnet dependency: + # https://github.com/fredrikaverpil/vcpython27/pull/3 + choco install --ignore-dependencies vcpython27 + fi + + python -m pip install --upgrade pip +} + setup::install() { if [ -f /etc/debian_version ]; then setup::debian::install @@ -82,6 +100,8 @@ setup::install() { setup::rh::install elif [ "$(uname)" == "Darwin" ]; then setup::macos::install + elif [ "$TRAVIS_OS_NAME" == "windows" ]; then + setup::windows::install else echo "Distro not found!" false diff --git a/gssapi/raw/ext_cred_imp_exp.pyx b/gssapi/raw/ext_cred_imp_exp.pyx index 2f980603..ce525245 100644 --- a/gssapi/raw/ext_cred_imp_exp.pyx +++ b/gssapi/raw/ext_cred_imp_exp.pyx @@ -48,7 +48,7 @@ def export_cred(Creds creds not None): maj_stat = gss_export_cred(&min_stat, creds.raw_creds, &exported_creds) if maj_stat == GSS_S_COMPLETE: - res = exported_creds.value[:exported_creds.length] + res = (exported_creds.value)[:exported_creds.length] gss_release_buffer(&min_stat, &exported_creds) return res else: diff --git a/gssapi/raw/ext_dce.pyx b/gssapi/raw/ext_dce.pyx index 747f59d7..afc56545 100644 --- a/gssapi/raw/ext_dce.pyx +++ b/gssapi/raw/ext_dce.pyx @@ -209,7 +209,7 @@ cdef class IOV: new_val = b'\x00' * new_len else: new_len = self._iov[i].buffer.length - new_val = self._iov[i].buffer.value[:new_len] + new_val = (self._iov[i].buffer.value)[:new_len] alloc = False if self._iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATE: @@ -503,7 +503,7 @@ def wrap_aead(SecurityContext context not None, bytes message not None, &conf_used, &output_buffer) if maj_stat == GSS_S_COMPLETE: - output_message = output_buffer.value[:output_buffer.length] + output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return WrapResult(output_message, conf_used) else: @@ -553,7 +553,7 @@ def unwrap_aead(SecurityContext context not None, bytes message not None, &conf_state, &qop_state) if maj_stat == GSS_S_COMPLETE: - output_message = output_buffer.value[:output_buffer.length] + output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return UnwrapResult(output_message, conf_state, qop_state) else: diff --git a/gssapi/raw/ext_ggf.pyx b/gssapi/raw/ext_ggf.pyx index 7029d3db..7da82c93 100644 --- a/gssapi/raw/ext_ggf.pyx +++ b/gssapi/raw/ext_ggf.pyx @@ -74,7 +74,7 @@ def inquire_cred_by_oid(Creds cred_handle not None, if data_set != GSS_C_NO_BUFFER_SET: for i in range(data_set.count): token = data_set.elements[i] - py_tokens.append(token.value[:token.length]) + py_tokens.append((token.value)[:token.length]) gss_release_buffer_set(&min_stat, &data_set) @@ -124,7 +124,7 @@ def inquire_sec_context_by_oid(SecurityContext context not None, if data_set != GSS_C_NO_BUFFER_SET: for i in range(data_set.count): token = data_set.elements[i] - py_tokens.append(token.value[:token.length]) + py_tokens.append((token.value)[:token.length]) gss_release_buffer_set(&min_stat, &data_set) diff --git a/gssapi/raw/ext_rfc5587.pyx b/gssapi/raw/ext_rfc5587.pyx index 88a6b4ce..a061690c 100644 --- a/gssapi/raw/ext_rfc5587.pyx +++ b/gssapi/raw/ext_rfc5587.pyx @@ -140,9 +140,9 @@ def display_mech_attr(OID attr): &long_desc) if maj_stat == GSS_S_COMPLETE: - out_name = name.value[:name.length] - out_short = short_desc.value[:short_desc.length] - out_long = long_desc.value[:long_desc.length] + out_name = (name.value)[:name.length] + out_short = (short_desc.value)[:short_desc.length] + out_long = (long_desc.value)[:long_desc.length] gss_release_buffer(&min_stat, &name) gss_release_buffer(&min_stat, &short_desc) diff --git a/gssapi/raw/ext_rfc5801.pyx b/gssapi/raw/ext_rfc5801.pyx index 314db1db..772c8f55 100644 --- a/gssapi/raw/ext_rfc5801.pyx +++ b/gssapi/raw/ext_rfc5801.pyx @@ -50,9 +50,9 @@ def inquire_saslname_for_mech(OID mech not None): &mech_name, &mech_desc) if maj_stat == GSS_S_COMPLETE: - out_smn = sasl_mech_name.value[:sasl_mech_name.length] - out_mn = mech_name.value[:mech_name.length] - out_md = mech_desc.value[:mech_desc.length] + out_smn = (sasl_mech_name.value)[:sasl_mech_name.length] + out_mn = (mech_name.value)[:mech_name.length] + out_md = (mech_desc.value)[:mech_desc.length] gss_release_buffer(&min_stat, &sasl_mech_name) gss_release_buffer(&min_stat, &mech_name) diff --git a/gssapi/raw/ext_rfc6680.pyx b/gssapi/raw/ext_rfc6680.pyx index 1d8a0a2a..bfd52319 100644 --- a/gssapi/raw/ext_rfc6680.pyx +++ b/gssapi/raw/ext_rfc6680.pyx @@ -67,7 +67,7 @@ def display_name_ext(Name name not None, OID name_type not None): &name_type.raw_oid, &output_name) if maj_stat == GSS_S_COMPLETE: - name_text = output_name.value[:output_name.length] + name_text = (output_name.value)[:output_name.length] gss_release_buffer(&min_stat, &output_name) return name_text else: @@ -126,7 +126,9 @@ def inquire_name(Name name not None, mech_name=True, attrs=True): if attr_names != GSS_C_NO_BUFFER_SET: for i in range(attr_names.count): attr_name = attr_names.elements[i] - py_attr_names.append(attr_name.value[:attr_name.length]) + py_attr_names.append( + (attr_name.value)[:attr_name.length] + ) gss_release_buffer_set(&min_stat, &attr_names) @@ -233,9 +235,9 @@ def get_name_attribute(Name name not None, attr not None, more=None): &more_val) if maj_stat == GSS_S_COMPLETE: - py_vals.append(val_buff.value[:val_buff.length]) + py_vals.append((val_buff.value)[:val_buff.length]) py_displ_vals.append( - displ_val_buff.value[:displ_val_buff.length]) + (displ_val_buff.value)[:displ_val_buff.length]) gss_release_buffer(&min_stat, &val_buff) gss_release_buffer(&min_stat, &displ_val_buff) @@ -310,7 +312,7 @@ def export_name_composite(Name name not None): maj_stat = gss_export_name_composite(&min_stat, name.raw_name, &res) if maj_stat == GSS_S_COMPLETE: - py_res = res.value[:res.length] + py_res = (res.value)[:res.length] gss_release_buffer(&min_stat, &res) return py_res else: diff --git a/gssapi/raw/message.pyx b/gssapi/raw/message.pyx index 97972082..d45e430c 100644 --- a/gssapi/raw/message.pyx +++ b/gssapi/raw/message.pyx @@ -82,7 +82,7 @@ def get_mic(SecurityContext context not None, message, qop=None): &message_buffer, &token_buffer) if maj_stat == GSS_S_COMPLETE: - res = token_buffer.value[:token_buffer.length] + res = (token_buffer.value)[:token_buffer.length] gss_release_buffer(&min_stat, &token_buffer) return res else: @@ -220,7 +220,7 @@ def wrap(SecurityContext context not None, message, confidential=True, &message_buffer, &conf_used, &output_buffer) if maj_stat == GSS_S_COMPLETE: - output_message = output_buffer.value[:output_buffer.length] + output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return WrapResult(output_message, conf_used) else: @@ -268,7 +268,7 @@ def unwrap(SecurityContext context not None, message): &output_buffer, &conf_state, &qop_state) if maj_stat == GSS_S_COMPLETE: - output_message = output_buffer.value[:output_buffer.length] + output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return UnwrapResult(output_message, conf_state, qop_state) else: diff --git a/gssapi/raw/misc.pyx b/gssapi/raw/misc.pyx index 4ea0c557..ca1be8ca 100644 --- a/gssapi/raw/misc.pyx +++ b/gssapi/raw/misc.pyx @@ -165,7 +165,7 @@ def _display_status(unsigned int error_code, bint is_major_code, if maj_stat == GSS_S_COMPLETE: call_again = bool(msg_ctx_out) - msg_out = msg_buff.value[:msg_buff.length] + msg_out = (msg_buff.value)[:msg_buff.length] gss_release_buffer(&min_stat, &msg_buff) return (msg_out, msg_ctx_out, call_again) else: diff --git a/gssapi/raw/names.pyx b/gssapi/raw/names.pyx index 8c63eb51..cfeaef77 100644 --- a/gssapi/raw/names.pyx +++ b/gssapi/raw/names.pyx @@ -151,7 +151,7 @@ def display_name(Name name not None, name_type=True): cdef OID py_name_type if maj_stat == GSS_S_COMPLETE: - text = output_buffer.value[:output_buffer.length] + text = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) if name_type: if output_name_type == GSS_C_NO_OID: @@ -240,7 +240,7 @@ def export_name(Name name not None): if maj_stat == GSS_S_COMPLETE: # force conversion to a python string with the specified length # (we use the slice to tell cython that we know the length already) - res = exported_name.value[:exported_name.length] + res = (exported_name.value)[:exported_name.length] gss_release_buffer(&min_stat, &exported_name) return res else: diff --git a/gssapi/raw/python_gssapi.h b/gssapi/raw/python_gssapi.h index fcede79a..94c5776c 100644 --- a/gssapi/raw/python_gssapi.h +++ b/gssapi/raw/python_gssapi.h @@ -1,6 +1,6 @@ #ifdef OSX_HAS_GSS_FRAMEWORK #include -#elif __MINGW32__ +#elif defined(__MINGW32__) && defined(__MSYS__) #include #else #include diff --git a/gssapi/raw/python_gssapi_ext.h b/gssapi/raw/python_gssapi_ext.h index 17fbeb56..3d32a4b8 100644 --- a/gssapi/raw/python_gssapi_ext.h +++ b/gssapi/raw/python_gssapi_ext.h @@ -1,7 +1,7 @@ #ifdef OSX_HAS_GSS_FRAMEWORK #include #else -#ifdef __MINGW32__ +#if defined(__MINGW32__) && defined(__MSYS__) #include #else #ifdef HAS_GSSAPI_EXT_H diff --git a/gssapi/raw/python_gssapi_krb5.h b/gssapi/raw/python_gssapi_krb5.h index b64e1c66..c67af3b9 100644 --- a/gssapi/raw/python_gssapi_krb5.h +++ b/gssapi/raw/python_gssapi_krb5.h @@ -1,6 +1,6 @@ #ifdef OSX_HAS_GSS_FRAMEWORK #include -#elif __MINGW32__ +#elif defined(__MINGW32__) && defined(__MSYS__) #include #else #include diff --git a/gssapi/raw/sec_contexts.pyx b/gssapi/raw/sec_contexts.pyx index e585bd9e..c18da3fa 100644 --- a/gssapi/raw/sec_contexts.pyx +++ b/gssapi/raw/sec_contexts.pyx @@ -223,7 +223,8 @@ flags=None, lifetime=None, channel_bindings=None, input_token=None) output_token = None if output_token_buffer.length: - output_token = output_token_buffer.value[:output_token_buffer.length] + output_token = \ + (output_token_buffer.value)[:output_token_buffer.length] cdef OM_uint32 tmp_min_stat gss_release_buffer(&tmp_min_stat, &output_token_buffer) @@ -330,7 +331,8 @@ channel_bindings=None) output_token = None if output_token_buffer.length: - output_token = output_token_buffer.value[:output_token_buffer.length] + output_token = \ + (output_token_buffer.value)[:output_token_buffer.length] cdef OM_uint32 tmp_min_stat gss_release_buffer(&tmp_min_stat, &output_token_buffer) @@ -620,7 +622,7 @@ def export_sec_context(SecurityContext context not None): &output_token) if maj_stat == GSS_S_COMPLETE: - res_token = output_token.value[:output_token.length] + res_token = (output_token.value)[:output_token.length] gss_release_buffer(&min_stat, &output_token) return res_token else: @@ -665,7 +667,7 @@ def delete_sec_context(SecurityContext context not None, local_only=True): GSS_C_NO_BUFFER) if maj_stat == GSS_S_COMPLETE: - res = output_token.value[:output_token.length] + res = (output_token.value)[:output_token.length] context.raw_ctx = NULL return res else: diff --git a/setup.py b/setup.py index 0d185042..7dbe17e3 100755 --- a/setup.py +++ b/setup.py @@ -5,10 +5,13 @@ 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' @@ -26,63 +29,106 @@ file=sys.stderr) SOURCE_EXT = 'c' -get_output = None -try: - import commands - get_output = commands.getoutput -except ImportError: - import subprocess +def get_output(*args, **kwargs): + res = subprocess.check_output(*args, shell=True, **kwargs) + decoded = res.decode('utf-8') + return decoded.strip() - def _get_output(*args, **kwargs): - res = subprocess.check_output(*args, shell=True, **kwargs) - decoded = res.decode('utf-8') - return decoded.strip() - - get_output = _get_output # get the compile and link args -link_args = os.environ.get('GSSAPI_LINKER_ARGS', None) -compile_args = os.environ.get('GSSAPI_COMPILER_ARGS', None) +link_args, compile_args = [ + shlex.split(os.environ[e]) 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 link_args is None: if osx_has_gss_framework: - link_args = '-framework GSS' + 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' + link_args = ['-lgss'] else: - link_args = get_output('krb5-config --libs gssapi') + link_args = shlex.split(get_output('krb5-config --libs gssapi')) if compile_args is None: if osx_has_gss_framework: - compile_args = '-framework GSS -DOSX_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' + compile_args = ['-fPIC'] else: - compile_args = get_output('krb5-config --cflags gssapi') - -link_args = link_args.split() -compile_args = compile_args.split() + compile_args = shlex.split(get_output('krb5-config --cflags gssapi')) # add in the extra workarounds for different include structures -try: - prefix = get_output('krb5-config gssapi --prefix') -except Exception: - print("WARNING: couldn't find krb5-config; assuming prefix of %s" - % str(sys.prefix)) - prefix = sys.prefix +if winkrb_path: + prefix = winkrb_path +else: + try: + prefix = get_output('krb5-config 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 -library_dirs = [arg[2:] for arg in link_args if arg.startswith('-L')] -link_args = [arg for arg in link_args if not arg.startswith('-L')] +# 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') @@ -96,11 +142,25 @@ def _get_output(*args, **kwargs): 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: - if opt.startswith('-lgssapi'): - main_lib = 'lib%s.so' % opt[2:] - # To support Heimdal on Debian, read the linker path. if opt.startswith('-Wl,/'): main_path = opt[4:] + "/" @@ -110,7 +170,7 @@ def _get_output(*args, **kwargs): "try setting GSSAPI_MAIN_LIB yourself or setting " "ENABLE_SUPPORT_DETECTION to 'false'") - GSSAPI_LIB = ctypes.CDLL(main_path + main_lib) + GSSAPI_LIB = ctypes.CDLL(os.path.join(main_path, main_lib)) # add in the flag that causes us not to compile from Cython when @@ -146,7 +206,10 @@ def ext_modules(self): return self._ext_modules if getattr(self, '_cythonized_ext_modules', None) is None: - self._cythonized_ext_modules = cythonize(self._ext_modules) + self._cythonized_ext_modules = cythonize( + self._ext_modules, + language_level=2, + ) return self._cythonized_ext_modules @@ -161,13 +224,25 @@ def ext_modules(self): 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 Extension('gssapi.raw.%s' % module, - extra_link_args=link_args, - extra_compile_args=compile_args, - library_dirs=library_dirs, - sources=['gssapi/raw/%s.%s' % (module, SOURCE_EXT)]) + return make_extension('gssapi.raw.%s', module) ENUM_EXTS = [] @@ -177,25 +252,17 @@ 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 None - else: - enum_ext_path = 'gssapi/raw/_enum_extensions/ext_%s.%s' % (module, - SOURCE_EXT) - if os.path.exists(enum_ext_path): - ENUM_EXTS.append( - Extension('gssapi.raw._enum_extensions.ext_%s' % module, - extra_link_args=link_args, - extra_compile_args=compile_args, - sources=[enum_ext_path], - library_dirs=library_dirs, - include_dirs=['gssapi/raw/'])) - - return Extension('gssapi.raw.ext_%s' % module, - extra_link_args=link_args, - extra_compile_args=compile_args, - library_dirs=library_dirs, - sources=['gssapi/raw/ext_%s.%s' % (module, - SOURCE_EXT)]) + 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): @@ -203,14 +270,10 @@ def gssapi_modules(lst): res = [mod for mod in lst if mod is not None] # add in supported mech files - MECHS_SUPPORTED = os.environ.get('GSSAPI_MECHS', 'krb5').split(',') - for mech in MECHS_SUPPORTED: - res.append(Extension('gssapi.raw.mech_%s' % mech, - extra_link_args=link_args, - extra_compile_args=compile_args, - library_dirs=library_dirs, - sources=['gssapi/raw/mech_%s.%s' % (mech, - SOURCE_EXT)])) + 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)