无法将Cython生成的C语言代码转换为可执行文件。

4
我正在使用Windows操作系统,拥有Visual Studio及其相关的所有构建工具。我有一个简单的Python脚本,并使用Cython生成了一个“C”文件。我正在尝试将此C文件转换为可执行文件,但不想使用其他模块,如pyinstaller、py2exe或cxFreeze。
已成功将.py文件转换为C文件,但是在将此C文件转换为可执行文件时遇到了一些错误。请问是否有人能够帮助我?我查阅了许多关于此问题的其他问题,但它们都没有这个问题。
以下是我的(.pyx脚本):
from tkinter import Tk

root = Tk()
root.mainloop()

我在CMD中使用以下命令构建了这个脚本的C扩展:
python setup.py build_ext --inplace -DMS_WIN64

然后我在CMD中使用这个命令来构建可执行文件:

gcc -DMS_WIN64 C:/Users/Siva/Desktop/Scripts/TEST/test.c -IC:/"Program Files"/Python37/include -LC:/"Program Files"/Python37/libs -lpython37 -o output

这会产生以下错误:
C:\Users\Siva\Desktop\Scripts\TEST>gcc -DMS_WIN64 C:/Users/Siva/Desktop/Scripts/TEST/test.c -IC:/"Program Files"/Python37/include -LC:/"Program Files"/Python37/libs -lpython37.a -o output
C:/Users/Siva/Desktop/Scripts/TEST/test.c:213:41: warning: division by zero [-Wdiv-by-zero]
     enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
                                         ^
C:/Users/Siva/Desktop/Scripts/TEST/test.c:213:12: error: enumerator value for '__pyx_check_sizeof_voidp' is not an integer constant
     enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
            ^~~~~~~~~~~~~~~~~~~~~~~~

我已附上下面的错误屏幕截图。
请参见图片描述

我不知道它与Cython有什么关系,但在Python 3.x中,除法返回一个浮点数。例如:>>> 1 / 1 输出 1.0。这在PEP 238中被称为“真除法”。而Python 2.x则使用“经典除法”。 - martineau
我在我的代码中添加了-DMS_WIN64标志并进行了检查。问题仍然存在。我已经在我的问题中进行了编辑。他们提供的另一个解决方案是使用gendef ../vcruntime140.def命令。当我这样做时,它会显示*** [vcruntime140.def] failed to open()。请在这里指导我。 - user11186745
1
看起来像是编译时检查失败了。可以期望 SIZEOF_VOID_P 与 sizeof(void*) 相匹配,因此结果应该为1,但这里却失败了...对我而言,代码只是想要告诉你有些错误正在发生。你确定你没有在32/64位上搞错了什么吗? - OznOg
我的Python版本是“Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32”。我的GCC版本是“gcc (i686-posix-dwarf-rev0,由MinGW-W64项目构建) 8.1.0”,我的操作系统是64位的。你能告诉我其他可以检查版本的地方吗? - user11186745
附注:确保在主脚本中使用 if __name__ == '__main__': 保护;如果不这样做,可能会导致许多问题(例如,对于使用 multiprocessing 的任何脚本等原因)。 - ShadowRanger
显示剩余2条评论
2个回答

8

经过长期的奋斗,我终于找到了解决方案。感谢Joshua的大力支持。

.PYX代码需要进行一些小修改。看起来我们必须在一个函数内定义所有内容,这样当代码转换为C语言时,它会有一个主函数。

因此,第一步是编辑.PYX文件:

from tkinter import Tk

cdef public void function():
    root = Tk()
    root.mainloop()

if __name__ == "__main__":
    function()

然后我使用以下命令生成头文件(.h)和C程序文件

python -m cython test.pyx --embed

这是诀窍:

进入生成的C程序文件,搜索wmain函数并将其移除"w"。或者你可以使用Ctrl + F搜索wmain并将其实例替换为main。

例如,在我的程序中只有一个wmain实例。我将其更改为main。代码如下所示。只需将wmain更改为main即可。

#if PY_MAJOR_VERSION < 3
int main(int argc, char** argv) {
#elif defined(WIN32) || defined(MS_WINDOWS)
int wmain(int argc, wchar_t **argv) 

然后我使用GCC命令生成可执行文件。具体命令如下。我不明白为什么它有效,但它确实有效。

x86_64-w64-mingw32-gcc-8.1.0 -mconsole -DSIZEOF_VOID_P=8 -DMS_WIN64 C:/Users/Siva/Desktop/Scripts/CYTHON/test.c -IC:/"Program Files"/Python37/include -LC:/"Program Files"/Python37/libs -lpython37 -o output

我只希望有人能解释为什么这种方法有效。

3

环境检测失败,可能是自动检测出现问题。尝试以下方法覆盖它:

gcc -DSIZEOF_VOID_P=8 -DMS_WIN64 ...

gcc -DSIZEOF_VOID_P=4 ...

8常量表示64位输出,4常量表示32位输出。很可能您配置了64位输出,实际上却调用了32位的gcc编译器。如果是这样,假设您也安装了面向64位的gcc,下面的调用应该可以工作。

x86_64-w64-mingw32-gcc -DSIZEOF_VOID_P=8 -DMS_WIN64 ...

完美,这样就解决了SIZE_OF_VOIDP错误。但是现在它显示:C:/mingw-w64/x86_64-8.1.0-win32-seh-rt_v6-rev0/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o):crt0_c.c:(.text.startup+0x2e): 对“WinMain”的未定义引用 collect2.exe: error: ld 返回了 1 个退出状态 - user11186745
@SwaroopMajipatti:也许你需要使用“-mconsole”,但我已经没有更多的经验了。 - Joshua
它也失败了。我添加了mconsole,mwindows,但一切都失败了。无论如何,非常感谢您的帮助。我会继续探索。 - user11186745
我应该在哪里做这个?怎么做?你能给我指示一个例子吗? - user11186745
@SwaroopMajipatti:将WinMain()转换为main()其实是一个非常好的问题。 - Joshua
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接