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

时间:2017-07-10 05:49:15

标签: python windows windows-10 cython python-3.5

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

我通过使用Cython --embed标志获得了一个HelloWorld程序。从Windows命令提示符Cython生成一个.c文件:

# (myVirtualEnv) > cd (pathToSourceCode)
# (myVirtualEnv) > py (pathToVirtualEnv)\Scripts\cython.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:\Python35\libs

这很有效。从Windows命令提示符运行时,我的新helloTest.exe程序将打印到命令行。我需要弄清楚如何将第一组命令(从常规命令提示符)转换为distutils安装脚本,我还没有完成。也许我可以将windows编译器命令变成make文件或批处理文件等。

这里的缺点是我希望能够将许多模块和软件包组合在一起,而且我也不想确保最终用户拥有所有依赖项(Python 3.5和所有必需的模块) )到位。

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

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

所以,我的问题:

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

我绝对愿意接受其他非cython_freeze方式。但是,我应该注意到,我已经看过其他一些看起来不太有希望的选择:

  • py2exe和cx-freeze以及其他类似的冻结工具压缩自定义代码,但仍需要安装Python和所有模块。这对我的用户太过分了。我真的希望他们能够点击它和(只要它们在Windows上)让它运行。
  • 我考虑过使用gcc作为编译器,但由于MSVC 2015用于compile Python for windows并且所有扩展听起来像这样会打开大门与python本身的许多兼容性问题的大门。 / LI>
  • 我也倾向于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大师可以权衡。

2 个答案:

答案 0 :(得分:1)

我不会(直接)回答你的问题,但会得到我认为你想要的结果。相反,我将为您提供安装了NumPy SciPy Pandas的预编译Python 3.5 x64 Windows构建嵌入式副本的链接,您只需下载并将从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_modules\numpy\
    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 argument\n");
                    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: %ld\n", PyLong_AsLong(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr, "Call failed\n");
                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:\Python3\include;</IncludePath>## Heading ##
    <LibraryPath>$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64;</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:\Python3\libs;</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-packages \ numpy \ core \ lib \ npymath.lib。任何你自己编译的PYD Cython程序都可以直接放在\ extension_modules \中。对于我在Windows上的构建,我还必须在带有EXE的目录中包含msvcp140_app.dll(奇怪的是\ Program Files(x86)\ Microsoft Visual Studio 14.0 \重命名VC \ redist \ x64 \ Microsoft.VC140.CRT \ msvcp140.dll,或者32位的\ x86 \目录。)

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

享受。

答案 1 :(得分:0)

你应该试试py2exe,我认为这是从python代码制作.exe的最简单方法。我理解你想要的东西,但很容易用py2exe创建一个.exe,用C复制文件夹,然后创建一个快捷方式。