如何在 Windows 上使用cython_freeze来构建单个可执行文件



我在Windows 10上运行Python 3.5,我想将我的Python代码编译成一个可执行文件,以便与一些最终用户共享。 我正在使用 Cython 0.25.2 来尝试完成此操作。

我有一个HelloWorld程序,通过使用Cython --embed flag。 在Windows命令提示符下,Cython创建一个.c文件:

# (myVirtualEnv) > cd (pathToSourceCode)
# (myVirtualEnv) > py (pathToVirtualEnv)Scriptscython.exe helloWorld.pyx --embed

这给了我一个HelloWorld.c文件。 然后我打开MSVC++ 2015 x86本机命令提示符,并将HelloWorld.c文件编译为Windows exe:

# > cd (pathToSourceCode)
# > cl.exe  /nologo /Ox /MD /W3 /GS- /DNDEBUG -I(pathToVirtualEnv)Include -I(pathToVirtualEnv)PC /TchelloWorld.c /link /OUT:"helloTest.exe" /SUBSYSTEM:CONSOLE /MACHINE:X86 /LIBPATH:(pathToVirtualEnv)Lib /LIBPATH:C:Python35libs

这很好用。 当从 windows 命令提示符运行时,我的新 helloTest.exe程序打印到命令行。 我需要弄清楚如何将第一组命令(来自常规命令提示符)放入 distutils 安装脚本中,但我还没有这样做。 也许我可以将 windows 编译器命令制作成 make 文件或批处理文件或其他东西。

这里的缺点是我希望能够一起构建许多模块和包,我也不想确保最终用户拥有所有依赖项(Python 3.5 和所有需要的模块)。

我一直无法弄清楚如何构建多个Cython文件,然后将它们链接到最终的Windows.exe中。 但是,看到我还想包含 Python 本身和任何包含的 python 模块,除了一些预编译处理之外,我真的需要一个冻结工具。

Cython有一个用于cython_freeze的演示文件夹。 这看起来很完美。 它说它将所有包含的模块(包括Python本身)拉到一个c文件中进行编译。 示例文件包括 gcc 的自述文件和生成文件。 我不完全了解这里发生了什么,并且由于我使用的是 MSCV cl.exe,我认为正确的做法是查找/修改它以适用于 cl.exe。 我缺乏这样做的技能。 事实上,因为我在 Windows 上,所以我从预编译的轮子中得到了 cython,我什至不确定我的 virtualenv 中是否存在cython_freeze(我做了搜索,什么也没想出来)。 同样,"cython freeze"网络搜索也是空的。 到目前为止,我已经有了cython--embed HelloWorld程序,我已经从GitHub下载了cython源代码以查看cython_freeze自述文件并制作文件。

所以,我的问题:

  • 如何使用 cython_freeze 和 cl.exe 构建一个包含 python、我的主程序和包含的包/模块(标准或自定义)的单个 Windows 可执行文件?
  • 因为我从为 Windows 预编译的轮子上安装了 Cython 0.25.2,所以cython_freeze甚至已经在我的 virtualenv 中了吗? 如果是这样,我该如何使用它? 如果没有,我该如何"安装"它?

我绝对愿意接受其他非cython_freeze方式。 但是,我应该指出,我已经查看了其他几个看起来不太有希望的选项:

  • py2exe 和 cx-freeze 以及其他类似的 freeze 工具压缩了自定义代码,但仍需要安装 Python 和所有模块。 这给我的用户带来了太多。 我真的希望他们能够点击.exe并且(只要他们在 Windows 上)让它运行。
  • 我考虑过使用 gcc 作为编译器,但由于 MSVC 2015 用于编译 Python for Windows 和所有扩展,这听起来会为与 python 本身的许多兼容性问题打开大门。
  • 我也倾向于cython,因为我在numpy中有一些相当慢的"大数学"例程,需要很长时间,所以我有动力学习更多关于cython的知识,以便尽快加快这些速度。

除了 windows10 和 Python 3.5 之外,我还使用 pip 和 virtualenv 来管理我的软件包并创建我的 virtualenv。 我没有使用蚂蚁或康达。 Cython和我的大多数其他软件包都是从预编译的轮子安装的,如上所示。


更新:

我的问题的精神实际上是关于为没有安装Python的用户打包Python。 Matt 给出了一个很好的方法来做到这一点(以及编译 Python 的良好教育)。 Biswa_9937和S.Moncayo正确地指出,Py2Exe(Python 3.4及更早版本)和PyInstaller(适用于Python 3.5)也可以实现这一目标。 这些都是很好的答案,非常有帮助。

但是,我的问题的细节是关于让Cython_freeze在Windows上工作。 没有人尝试过,所以我没有回答这个问题,希望Cython大师可以参与进来。

我不会(直接)回答你的问题,但会给你我认为你想要的结果。相反,我将给你一个预编译的Python 3.5 x64 Windows构建嵌入式副本的链接,其中安装了NumPy SciPy Pandas,你可以下载并从Python/libs/site-packages/导入的每个库添加到extension_modules/目录中,并且仅使用.py脚本即插即用。 https://stackoverflow.com/a/44610044/6037118 您可能需要滚动到我的答案。 除了冻结在我的理解中不会将所有内容放入exe之外,您仍然需要带有导入库的子文件夹。

以下是编译的嵌入式Python 3.5 x64 VS 2015链接,带有NumPy,SciPy,Pandas,Intel MKL:

https://www.dropbox.com/sh/2smbgen2i9ilf2e/AADI8A3pCAFU-EqNLTbOiUwJa?dl=0

如何编译自己

我不会将其重新编译为 x32,而是教你如何钓鱼本身。 下面是Visual Studio项目call_function.c文件:

#include <Python.h>
int
main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict, *pFunc;
PyObject *pArgs, *pValue;
int i;
if (argc < 3) {
fprintf(stderr, "Usage: call pythonfile funcname [args]n");
return 1;
}

Py_SetPath(L"python35.zip;extension_modules//;); //all your libraries from site-packages must be installed under extension_modules directory like extension_modulesnumpy
Py_Initialize();
pName = PyUnicode_DecodeFSDefault(argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */
if (pFunc && PyCallable_Check(pFunc)) {
pArgs = PyTuple_New(argc - 3);
for (i = 0; i < argc - 3; ++i) {
pValue = PyLong_FromLong(atoi(argv[i + 3]));
if (!pValue) {
Py_DECREF(pArgs);
Py_DECREF(pModule);
fprintf(stderr, "Cannot convert argumentn");
return 1;
}
/* pValue reference stolen here: */
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != NULL) {
printf("Result of call: %ldn", PyLong_AsLong(pValue));
Py_DECREF(pValue);
}
else {
Py_DECREF(pFunc);
Py_DECREF(pModule);
PyErr_Print();
fprintf(stderr, "Call failedn");
return 1;
}
}
else {
if (PyErr_Occurred())
PyErr_Print();
fprintf(stderr, "Cannot find function "%s"n", argv[2]);
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
}
else {
PyErr_Print();
fprintf(stderr, "Failed to load "%s"n", argv[1]);
return 1;
}
Py_Finalize();
return 0;
}

以下是您可以保存为embedpython.sln的Visual Studio解决方案文件:

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "embedpython", "embedpython.vcxproj", "{51062161-3FE4-42F2-9B89-BEB15E8F7590}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Debug|x64.ActiveCfg = Debug|x64
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Debug|x64.Build.0 = Debug|x64
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Debug|x86.ActiveCfg = Debug|Win32
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Debug|x86.Build.0 = Debug|Win32
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Release|x64.ActiveCfg = Release|x64
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Release|x64.Build.0 = Release|x64
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Release|x86.ActiveCfg = Release|Win32
{51062161-3FE4-42F2-9B89-BEB15E8F7590}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

现在你必须下载可嵌入的zip文件(32位文件在这里):https://www.python.org/ftp/python/3.5.1/python-3.5.1-embed-win32.zip 并解压缩并将其放在与构建的EXE文件的目录中。 此外,你不需要python.exe或wpython.exe它附带,并将所有PYD文件复制到\extension_modules\目录中。 请注意,您还需要在链接器中添加任何链接的库 .lib 文件:

这是embedpython.vsxproj文件:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{51062161-3FE4-42F2-9B89-BEB15E8F7590}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);C:Python3;C:Python3include;</IncludePath>## Heading ##
<LibraryPath>$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Libumx64;</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Link>
<AdditionalDependencies>python35.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Link>
<AdditionalDependencies>python35.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:Python3libs;</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="call_function.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

确保上述所有目录都指向您的实际 Python3 安装(此处假设 \Python3\ 是安装目录)。 然后只需打开解决方案文件并生成 x32 或 x64 或任何您想要的内容。 您必须尝试一下才能找到软件包所需的一切。例如,Numpy 需要链接到 \Python3\Lib\site-packagesumpy\core\libpymath.lib。 任何你自己编译的 PYD Cython 程序都可以直接位于 \extension_modules\ 对于我在 Windows 上的构建,我还必须将msvcp140_app.dll包含在带有 EXE 的目录中(奇怪的是 \Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.CRT\msvcp140.dll重命名,或 \x86\ 目录为 32 位)。

出于所有这些奇怪的原因,我注意到,这就是为什么我建议下载预构建版本以开始使用 - 至少它会告诉你必须如何打包才能工作。

享受。

你应该尝试py2exe,我认为这是从python代码进行.exe的最简单方法。我明白你想要什么,但很容易用py2exe创建一个.exe,用C复制文件夹,然后创建一个快捷方式。

最新更新