From 3c63f7e65270f74e65b4de119bce0067ea4c3861 Mon Sep 17 00:00:00 2001 From: Kelly Peterson Date: Fri, 29 Jul 2022 10:27:07 -0600 Subject: [PATCH] Removing conda windows requirement (#10) * Making two changes so that quickumls_simstring can build again on Windows. Since Windows default locale encoding for open() is cp1252, the encoding for reading a file is now explicit as utf8. Also, there was a "unresolved external" link time error once the extension was changed to 'quickumls_simstring/_simstring' but is now set to '_simstring' for Windows. * After previous commit to fix linking error, realized that .PYD files were being installed to install_dir instead of install_dir/quickumls_simstring which would prevent the lib from being found in Windows after install. Tried several other options to make this work but since the documentation for Extension says that the name of an extension should not be a filename (https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension) this was the best solution I could find. * Changing requirements that conda must be the current environment for finding libiconv. Conda is still the easiest way to find it, but changing this up so that libiconv can be installed with conda but quickumls_simstring can still be installed with pip. --- setup.py | 55 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/setup.py b/setup.py index b6029a0..1e18cbf 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ """ import sys +import shutil import re import os.path from distutils.core import setup, Extension @@ -41,6 +42,17 @@ def install(self): # for each file, match string between # second last and last dot and trim it matcher = re.compile('\.([^.]+)\.so$') + + for i, outfile in enumerate(outfiles): + # NOTE that since Windows cannot link with an extention name like 'quickumls_simstring/_simstring' + # we must work around an install-time issue to make sure that PYD libs still go to installdir/quickumls_simstring + if '.pyd' in outfile.lower(): + # let's copy any .PYD files from the root into the installdir/quickumls_simstring + source_path = os.path.join(self.build_dir, pyd_file) + target_path = os.path.join(self.install_dir, 'quickumls_simstring', pyd_file) + print('Manually copying a Windows PYD from {0} to {1}'.format(source_path, target_path)) + shutil.copy(source_path, target_path) + return [batch_rename(file, re.sub(matcher, '.so', file)) for file in outfiles] @@ -48,34 +60,39 @@ def install(self): library_dirs = None extra_compile_args = None libs = [] +extension_name = 'quickumls_simstring/_simstring' if sys.platform.startswith("darwin") or sys.platform.startswith("cygwin"): libs = ['-liconv'] -elif 'conda' in sys.version.lower() and sys.platform.startswith("win"): - # The conda/Windows-specific setup below assumes that the current conda environment has run something like this : +elif sys.platform.startswith("win"): + # The conda/Windows-specific setup below assumes that the current conda environment has run something like this : # conda install -c conda-forge libiconv print('Setting up assuming Anaconda was used to install iconv (conda install -c conda-forge libiconv)') python_executable_dir = os.path.dirname(sys.executable) - anaconda_include_dir = os.path.join(python_executable_dir, 'Library/include') - anaconda_lib_dir = os.path.join(python_executable_dir, 'Library/lib') - use_conda_deps = True - + include_dir = os.path.join(python_executable_dir, 'Library/include') + lib_dir = os.path.join(python_executable_dir, 'Library/lib') + use_existing_libiconv = True + + # this extension name needs to be changed for Windows or there will + # be an unresolved external error at linking time + extension_name = '_simstring' + # let's check if these pieces are actually here before we try to give the include/lib hints below - if not os.path.isfile(os.path.join(anaconda_include_dir, 'iconv.h')): - print('Could not find header iconv.h at [{0}] so bypassing setup hints. Verify that iconv was installed with conda.'.format(anaconda_include_dir)) - use_conda_deps = False - elif not os.path.isfile(os.path.join(anaconda_lib_dir, 'iconv.lib')): - print('Could not find library iconv.lib at [{0}] so bypassing setup hints. Verify that iconv was installed with conda.'.format(anaconda_lib_dir)) - use_conda_deps = False - - if use_conda_deps: + if not os.path.isfile(os.path.join(include_dir, 'iconv.h')): + print('Could not find header iconv.h at [{0}] so bypassing setup hints. Verify that iconv was installed with conda.'.format(include_dir)) + use_existing_libiconv = False + elif not os.path.isfile(os.path.join(lib_dir, 'iconv.lib')): + print('Could not find library iconv.lib at [{0}] so bypassing setup hints. Verify that iconv was installed with conda.'.format(lib_dir)) + use_existing_libiconv = False + + if use_existing_libiconv: libs = ['iconv.lib'] - additional_include_dirs = [anaconda_include_dir] - library_dirs = [anaconda_lib_dir] + additional_include_dirs = [include_dir] + library_dirs = [lib_dir] # The = at the end of this with nothing after causes the symbol to have no associated value (instead of 'const') # so that this will compile under MSVC more info here: # https://docs.microsoft.com/en-us/cpp/build/reference/d-preprocessor-definitions?view=vs-2017 extra_compile_args = ['/DICONV_CONST='] - print('Assuming Python executable [{0}], additional include dir [{1}], additional lib dir [{2}]'.format(python_executable_dir, anaconda_include_dir, anaconda_lib_dir)) + print('Assuming Python executable [{0}], additional include dir [{1}], additional lib dir [{2}]'.format(python_executable_dir, include_dir, lib_dir)) print('Setting extra_compile_args to {0}'.format(extra_compile_args)) print('NOTE: If there is a failure that rc.exe cannot be found, add the appropriate "WindowsKits" directory to the PATH for either x86 or x64.') @@ -89,11 +106,11 @@ def install(self): libs += ["-stdlib=libc++", '-Wl,-undefined,dynamic_lookup'] extra_compile_args = ["-stdlib=libc++"] -with open('README.md') as reader: +with open('README.md', encoding = 'utf8') as reader: readme = reader.read() simstring_module = Extension( - 'quickumls_simstring/_simstring', + extension_name, sources = [ 'quickumls_simstring/export.cpp', 'quickumls_simstring/export_wrap.cpp',