IronPython 2.6 .py 转 .exe

18

我已经尝试使用了py2exe(与ipy不兼容)和PYC(已过时)。 有人能指点我一个好的编译器吗?


你能提供一个最小化的复现吗? - daftspaniel
5个回答

29
你可以使用pyc.py,即Python命令行编译器,自IronPython 2.6版本以来就已经包含在其中,用于将Python脚本编译为可执行文件。你可以在硬盘上的%IRONPYTONINSTALLDIR%\Tools\Scripts\pyc.py找到它。

示例

假设你有一个简单的脚本test.py,它只是在控制台上打印一些内容。你可以使用以下命令行将其转换为可执行文件(假设IronPython目录是当前目录,并且test.py也在其中):

ipy.exe Tools\Scripts\pyc.py /main:test.py /target:exe

注意:如果您正在使用表单并且不希望打开控制台窗口,则应使用/target:winexe而不是/target:exe
结果将是两个文件:test.dlltest.exetest.dll将包含您的实际脚本代码,而test.exe只是test.dll的启动器。如果您包含了这些文件,您可以将此EXE和DLL分发到其他没有安装IronPython的计算机上。
还需参阅博客文章IronPython-如何编译exe

1
我使用pyc.py编译了我的脚本,但在执行过程中,我一直收到以下错误信息:Unhandled Exception: IronPython.Runtime.Exceptions.ImportException: No module named os。然而,一旦我添加了sys.path.append(System.Environment.GetEnvironmentVariable("ProgramFiles(x86)") + "\IronPython 2.7\Lib"),它就可以正常工作了! - Gezim

6
这是一个长期存在的问题,互联网上很少有相关信息。目前我能找到的唯一已知解决方案在http://community.sharpdevelop.net/blogs/mattward/archive/2010/03/16/CompilingPythonPackagesWithIronPython.aspx,需要使用SharpDevelop。然而,这个解决方案并不实用,因为任何稍微复杂一点的python项目都会进行大量模块导入,而SharpDevelop的解决方案需要你为每个导入创建一个项目我尝试了大约30个新项目后放弃了,最好写一个自动化的解决方案!

所以这就是我的解决方案,我现在告诉你它没有被发布为一个正式的项目,原因很简单:

#!/usr/bin/env python
# CompileToStandalone, a Python to .NET ILR compiler which produces standalone binaries
# (C) 2012 Niall Douglas http://www.nedproductions.biz/
# Created: March 2012

import modulefinder, sys, os, subprocess, _winreg

if len(sys.argv)<2:
    print("Usage: CompileEverythingToILR.py <source py> [-outdir=<dest dir>]")
    sys.exit(0)

if sys.platform=="cli":
    print("ERROR: IronPython's ModuleFinder currently doesn't work, so run me under CPython please")
    sys.exit(1)

sourcepath=sys.argv[1]
destpath=sys.argv[2][8:] if len(sys.argv)==3 else os.path.dirname(sys.argv[0])
ironpythonpath=None
try:
    try:
        keyh=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\IronPython\\2.7\\InstallPath")
        ironpythonpath=_winreg.QueryValue(keyh, None)
    except Exception as e:
        try:
            keyh=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\IronPython\\2.7\\InstallPath")
            ironpythonpath=_winreg.QueryValue(keyh, "")
        except Exception as e:
            pass
finally:
    if ironpythonpath is not None:
        _winreg.CloseKey(keyh)
        print("IronPython found at "+ironpythonpath)
    else:
        raise Exception("Cannot find IronPython in the registry")

# What we do now is to load the python source but against the customised IronPython runtime
# library which has been hacked to work with IronPython. This spits out the right set of
# modules mostly, but we include the main python's site-packages in order to resolve any
# third party packages
print("Scanning '"+sourcepath+"' for dependencies and outputting into '"+destpath+"' ...")
searchpaths=[".", ironpythonpath+os.sep+"Lib"]
searchpaths+=[x for x in sys.path if 'site-packages' in x]
finder=modulefinder.ModuleFinder(searchpaths)
finder.run_script(sourcepath)
print(finder.report())
modules=[]
badmodules=finder.badmodules.keys()
for name, mod in finder.modules.iteritems():
    path=mod.__file__
    # Ignore internal modules
    if path is None: continue
    # Ignore DLL internal modules
    #if '\\DLLs\\' in path: continue
    # Watch out for C modules
    if os.path.splitext(path)[1]=='.pyd':
        print("WARNING: I don't support handling C modules at '"+path+"'")
        badmodules.append(name)
        continue
    modules.append((name, os.path.abspath(path)))
modules.sort()
print("Modules not imported due to not found, error or being a C module:")
print("\n".join(badmodules))
raw_input("\nPress Return if you are happy with these missing modules ...")

with open(destpath+os.sep+"files.txt", "w") as oh:
    oh.writelines([x[1]+'\n' for x in modules])
cmd='ipy64 '+destpath+os.sep+'pyc.py /main:"'+os.path.abspath(sourcepath)+'" /out:'+os.path.splitext(os.path.basename(sourcepath))[0]+' /target:exe /standalone /platform:x86 /files:'+destpath+os.sep+'files.txt'
print(cmd)
cwd=os.getcwd()
try:
    os.chdir(destpath)
    retcode=subprocess.call(cmd, shell=True)
finally:
    os.chdir(cwd)
sys.exit(retcode)

这是针对IronPython v2.7.2 RC1使用其新的独立二进制文件功能编写的,确实可以正常工作。您将获得一个完全自包含的独立.exe文件——它不需要安装任何其他内容。该脚本通过解析提供的脚本的导入并将整个内容发送到pyc.py来工作。这是好消息。
坏消息如下:
1. IronPython v2.7.2 RC1的ModuleFinder似乎不起作用,因此上述脚本需要使用CPython运行。然后它使用CPython的ModuleFinder,但针对IronPython的定制运行时库。是的,我很惊讶它也能工作...
2. 生成的二进制文件从大约8Mb开始。一个简单的单元测试重达16Mb。有很多东西不需要在里面,例如它会扔掉Wpf支持和更多,但它们仍然不小。
3. 加载时间比非独立版本慢得多。想想快速的Intel Core 2上的独立单元测试需要大约40秒,而非独立版本只需要大约3秒。如果只编译为x86,则可将其降至10秒。
4. 运行时性能比非独立版本慢约40%。如果只编译为x86,则性能大约翻倍。这就是为什么我在上面留下/platform:x86的原因。
5. CPython的编码和编解码支持存在一个众所周知的错误,即ModuleFinder根本不包括任何编解码支持,除非您手动指定它。因此,例如,如果您正在使用UTF-8与codecs.open(),则需要“从encodings导入utf_8 as some_unique_identifier”来强制依赖项。
6. 上述假设修改了pyc.py,它可以接受/files参数,因为命令行长度限制很容易超过。如果您无法修改自己的pyc.py,则可以轻松地修改,否则我已提交增强以纳入下一个IronPython。
所以这就是全部。它可以工作,但解决方案仍需要更多成熟。祝好运!

4
请查看 IronPython示例页面
在页面的中间位置: Pyc - Python命令行编译器 此示例演示了开发人员如何直接从IronPython脚本创建.NET可执行文件。下载中的readme.htm将帮助您入门。
IronPython的Hosting API可用于将Python脚本编译为DLL、控制台可执行文件或Windows可执行文件。本教程中包含的pyc.py脚本利用这些Hosting API,并可用于编译其他Python脚本。它提供了各种标志,例如指定.NET程序集的目标平台(例如x64)等。
虽然IronPython Hosting API生成的程序集是真正的.NET程序集,但Python语言的动态性使得难以从其他.NET语言中使用这些程序集。简而言之,这意味着不建议尝试将Python类型导入到其他.NET语言(例如C#)中。 编辑:刚刚注意到您提到PYC已过时。这是什么原因?IronPython团队似乎仍在推广它,所以我想它并没有那么过时。

谢谢,但是当我尝试在PYC编译后运行.exe文件时,出现了“字符串不可调用”的错误。 - user177215
如果您使用ipy调用Python代码,它是否能够运行?这似乎是一个奇怪的错误。我今天会在工作中尝试一些代码,看看能否重现这个问题。 - Toji
是的,如果我在命令行中输入“ipy file.py”,我的Python代码完美运行,但当它是一个可执行文件并且我调用“file.exe”时,就会出现“字符串不可调用”的错误。 - user177215

2
我在实现这个解决方案时遇到了一些麻烦。 我所做的事情如下:
1. 从此处下载pyc。由于pyc似乎很难找到(我认为有点过时),因此我搜索了很长时间。
2. 我从zip文件中提取了pyc文件夹,并将其添加到C:\Program Files中的IronPython文件夹中。
3. 然后我按照pyc下载中的readme中的指示,在Windows控制台上运行了以下命令: ipy.exe pyc.py other_hw.py /main:console_hw.py 但是它给了我这个错误:
Traceback (most recent call last):
  File "pyc\pyc.py", line 35, in pyc\pyc.py
AttributeError: attribute 'CompilerSink' of 'namespace#' object is read-only

我对第35行进行了以下更改:
之前:class PycSink(Hosting.CompilerSink): 之后:class PycSink(): 由于权限问题,保存文件成为了一个问题。所以我将pyc.py的内容复制到一个新的IDLE窗口中(创建副本),删除了现有的 pyc.py 副本,并将副本另存为相同位置下的 pyc.py。这解决了权限问题并允许进行更改。
在进行此更改后,我尝试再次运行以下命令: ipy.exe pyc.py other_hw.py /main:console_hw.py 但是,这一次,我收到了以下错误:
Traceback (most recent call last):
  File "pyc\pyc.py", line 170, in pyc\pyc.py
  File "pyc\pyc.py", line 56, in Main
AttributeError: attribute 'ResourceFile' of 'namespace#' object is read-only

此时,我意识到现在已经是凌晨1点,而且明天还有期中考试,所以我撤销了更改并关闭了它。

如果您有解决方案或对我的方案有任何进展,请告诉我。


1

是的,我也发现编译exe太难了,所以我又回到使用标准Python了。他们应该在IronPython网站上提供一个好的教程。


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