Windows下CMake构建含.cxx/.py/依赖DLL项目的EXE打包及Py转DLL方法
针对你的需求,我整理了几个可行的方案,都是在Windows+CMake环境下适配Qt/PyQt/VTK/ITK这类依赖的,能帮你把Python脚本转换成DLL来保护源码:
方案1:用Cython将Python脚本编译为C扩展DLL
Cython可以把Python代码转成C/C++代码,再编译成DLL,既能保护源码,又能和你的CMake项目无缝集成。
步骤:
安装Cython
先通过pip安装:pip install cython将Python脚本转为
.pyx文件
把你要保护的Python脚本(比如my_script.py)重命名为my_script.pyx,如果需要调用C库(比如Qt/VTK/ITK的C接口),还可以在.pyx里添加Cython的类型声明来提升性能。在CMake中集成Cython编译
在你的CMakeLists.txt里添加以下配置,用来调用Cython生成C++代码,再编译成DLL:# 找到Python开发环境 find_package(Python3 REQUIRED COMPONENTS Development) find_package(Cython REQUIRED) # 定义要编译的pyx文件 set(PYX_FILES my_script.pyx) # 用Cython生成C++代码 foreach(pyx_file ${PYX_FILES}) get_filename_component(pyx_name ${pyx_file} NAME_WE) add_custom_command( OUTPUT ${pyx_name}.cxx COMMAND ${Python3_EXECUTABLE} -m cython ${pyx_file} --cplus -o ${pyx_name}.cxx DEPENDS ${pyx_file} COMMENT "Compiling ${pyx_file} to ${pyx_name}.cxx" ) endforeach() # 编译生成DLL add_library(my_python_dll SHARED ${pyx_name}.cxx) target_link_libraries(my_python_dll PRIVATE Python3::Python # 添加你的Qt/VTK/ITK依赖库,比如: Qt5::Core VTK::CommonCore ITK::Common ) # 设置DLL输出路径,和主程序一致 set_target_properties(my_python_dll PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )在主程序中调用DLL
编译完成后,你可以在原来的C++/Python主程序中通过Python的C API或者直接导入这个DLL(如果是Python调用的话)来执行原来的逻辑,不再需要暴露原始.py文件。
方案2:将Python字节码嵌入C++ DLL
这个方案是把Python脚本编译成字节码(.pyc),再嵌入到C++代码中编译成DLL,完全避免脚本文件暴露。
步骤:
生成Python字节码
打开命令行,运行以下命令生成.pyc文件:python -m py_compile my_script.py生成的字节码会在
__pycache__目录下,比如my_script.cpython-310.pyc(后缀和Python版本相关)。将字节码转为C数组
用xxd工具(Windows下可以通过Git Bash或者MinGW安装)把.pyc文件转成C头文件:xxd -i my_script.cpython-310.pyc > script_bytecode.h这个头文件里会生成一个
unsigned char数组,包含字节码的二进制数据。编写C++代码加载字节码
创建一个python_embed.cpp文件,用Python C API加载内存中的字节码:#include <Python.h> #include "script_bytecode.h" extern "C" __declspec(dllexport) void run_my_script() { // 初始化Python环境 Py_Initialize(); // 从内存加载字节码 PyObject* code_obj = PyMarshal_ReadObjectFromString( (const char*)my_script_cpython_310_pyc, my_script_cpython_310_pyc_len ); if (!code_obj) { PyErr_Print(); return; } // 执行字节码 PyObject* module = PyImport_ExecCodeModule("my_script", code_obj); if (!module) { PyErr_Print(); Py_DECREF(code_obj); return; } // 清理资源 Py_DECREF(module); Py_DECREF(code_obj); Py_Finalize(); }CMake编译为DLL
在CMakeLists.txt中添加配置:find_package(Python3 REQUIRED COMPONENTS Development) add_library(embedded_python_dll SHARED python_embed.cpp) target_link_libraries(embedded_python_dll PRIVATE Python3::Python # 添加Qt/VTK/ITK依赖 Qt5::Core VTK::CommonCore ITK::Common ) set_target_properties(embedded_python_dll PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )主程序调用
主程序通过LoadLibrary或者直接链接DLL,调用run_my_script函数即可执行原来的Python逻辑。
方案3:用Nuitka编译Python为C++ DLL
Nuitka是一个Python到C++的编译器,可以直接把Python脚本编译成独立的DLL,自动处理依赖。
步骤:
安装Nuitka
pip install nuitka编译Python脚本为DLL
运行以下命令,把脚本编译成DLL,同时打包依赖的库:nuitka --module --standalone --include-package=PyQt5 --include-package=vtk --include-package=itk my_script.py--module:生成DLL而不是EXE--standalone:打包所有依赖--include-package:指定需要包含的依赖包(根据你的实际依赖调整)
集成到CMake项目
把Nuitka生成的DLL(比如my_script.pyd,Windows下pyd就是DLL)复制到你的项目输出目录,然后在CMake中链接这个DLL:add_executable(your_main_app main.cxx) target_link_libraries(your_main_app PRIVATE ${CMAKE_BINARY_DIR}/bin/my_script.pyd # 其他依赖库 )
注意事项
- 无论用哪个方案,都要确保Python版本一致:编译DLL用的Python版本必须和主程序使用的Python版本完全匹配,否则会出现兼容性错误。
- 分发时要包含所有依赖的DLL:Qt、VTK、ITK以及Python的运行时DLL(比如
python310.dll)都需要和你的主程序、生成的DLL一起打包。 - 测试时要在干净的Windows环境下验证:避免依赖开发环境中的库,确保分发包可以独立运行。
内容的提问来源于stack exchange,提问作者Dwij Mistry

