17 views
Skip to first unread message

Andrea Gavana

unread,
Nov 16, 2022, 5:00:57 AM11/16/22
to cython...@googlegroups.com
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.




Reply all
Reply to author
Forward
0 new messages