Dear Cython users/developers,
I apologize if this is not the right forum for this question, but maybe some kind soul here has a suggestion on how to bypass this issue. I feel it is more setuptools's fault than anything Cython-related, but any suggestion would be welcome.
I am porting an old Python 2.7 codebase to Python 3.9.10, and I want to use gcc as a compiler. I do not have the MS VC14+ runtime and I'd rather do without.
I am using the standard "Hello World" example from the Cython documentation, but in reality my application is much more complex and I also require passing gcc flags. So I built this setup.py file:
import numpy as np
from setuptools import setup, Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext
module_name = 'helloworld'
extra_compile_args = ['-Ofast', '-funroll-loops', '-ftree-vectorize',
'-march=native', '-fno-asynchronous-unwind-tables',
'-DMS_WIN64']
extra_link_args = ['-flto', '-shared', '-static']
define_macros = [('WIN32', 1)]
include_dirs = [np.get_include()]
extension = Extension(module_name, [module_name + '.pyx'],
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
include_dirs=include_dirs,
define_macros=define_macros)
setup(
ext_modules=cythonize(extension,
compiler_directives={'embedsignature' : False,
'boundscheck' : False,
'wraparound' : False,
'initializedcheck': False,
'cdivision' : True,
'nonecheck' : False,
'language_level' : '3str'},
force=True,
cache=False,
quiet=False)
)
When I run it, I get this:
python setup.py build_ext --inplace --compiler=mingw32
[1/1] Cythonizing helloworld.pyx
running build_ext
gcc -mdll -O -Wall -DWIN32=1 -IC:\Users\J0514162\WinPython39\WPy64-39100\python-3.9.10.amd64\lib\site-packages\numpy\core\include -IC:\Users\J0514162\WinPython39\WPy64-39100\python-3.9.10.amd64\include -IC:\Users\J0514162\WinPython39\WPy64-39100\python-3.9.10.amd64\Include -c helloworld.c -o build\temp.win-amd64-3.9\Release\helloworld.o -Ofast -funroll-loops -ftree-vectorize -march=native -fno-asynchronous-unwind-tables -DMS_WIN64
writing build\temp.win-amd64-3.9\Release\helloworld.cp39-win_amd64.def
gcc -shared -s build\temp.win-amd64-3.9\Release\helloworld.o build\temp.win-amd64-3.9\Release\helloworld.cp39-win_amd64.def -LC:\Users\J0514162\WinPython39\WPy64-39100\python-3.9.10.amd64\libs -LC:\Users\J0514162\WinPython39\WPy64-39100\python-3.9.10.amd64 -LC:\Users\J0514162\WinPython39\WPy64-39100\python-3.9.10.amd64\PCbuild\amd64 -lpython39 -lucrt -lvcruntime140 -o build\lib.win-amd64-3.9\helloworld.cp39-win_amd64.pyd -flto -shared -static
c:/users/j0514162/tools/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lvcruntime140
collect2.exe: error: ld returned 1 exit status
error: command 'C:\\Users\\J0514162\\Tools\\MinGW64\\bin\\gcc.exe' failed with exit code 1
I am not clear why setuptools is looking for the vcruntime140 library (and the ucrt, for what is worth). It seems to me the culprit is inside distutils/setuptools, and specifically this line in cygwinccompiler.py/Mingw32CCompiler:
# no additional libraries needed
self.dll_libraries=[]
# Include the appropriate MSVC runtime library if Python was built
# with MSVC 7.0 or later.
self.dll_libraries = get_msvcr()
get_msvcr() unconditionally returns ['ucrt', 'vcruntime140'] for Python 3.9. Which I didn't ask for and I don't need.
One solution is to override the cmdclass input argument to setup(), like this:
class CustomBuildExt(build_ext):
def build_extensions(self):
self.compiler.set_executable("compiler_so", "gcc -mdll -O -Wall -DMS_WIN64")
self.compiler.set_executable("compiler_cxx", "g++ -O -Wall -DMS_WIN64")
self.compiler.set_executable("linker_so", "gcc -shared -static")
# Force override of dll_libraries
self.compiler.dll_libraries = []
build_ext.build_extensions(self)
And then call setup like this:
setup_kwargs = {'cmdclass': {'build_ext': CustomBuildExt},
'ext_modules': cythonize(extension,
compiler_directives={'embedsignature' : False,
'boundscheck' : False,
'wraparound' : False,
'initializedcheck': False,
'cdivision' : True,
'nonecheck' : False,
'language_level' : '3str'},
force=True,
cache=False,
quiet=False)}
setup(**setup_kwargs)
But it feels like a hack and I am not sure it's not going to break other things. Of course I may be misunderstanding everything here, but any suggestion would be much appreciated.
Thank you in advance.
Andrea.