Permalink
Browse files

Fully functional compiled to EXE IronPython support is now working!

  • Loading branch information...
1 parent c5e4117 commit 14dabd57e0bef750a9435692b1c079963e64da63 Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) committed Mar 7, 2012
View
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{acaefc7d-2e27-4e17-8184-89468d9edfb1}</ProjectGuid>
+ <ProjectHome>.</ProjectHome>
+ <StartupFile>tests\TestParseBErepoWithHTTP.py</StartupFile>
+ <SearchPath>
+ </SearchPath>
+ <WorkingDirectory>.</WorkingDirectory>
+ <InterpreterId>fcc291aa-427c-498c-a4d7-4502d6449b8c</InterpreterId>
+ <LaunchProvider>IronPython (.NET) launcher</LaunchProvider>
+ <InterpreterVersion>2.7</InterpreterVersion>
+ <OutputPath>.</OutputPath>
+ <Name>BEXML</Name>
+ <RootNamespace>BEXML</RootNamespace>
+ <CommandLineArguments />
+ <InterpreterPath />
+ <InterpreterArguments />
+ <DebugStdLib>False</DebugStdLib>
+ <IsWindowsApplication>False</IsWindowsApplication>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <DebugSymbols>true</DebugSymbols>
+ <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <DebugSymbols>true</DebugSymbols>
+ <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
+ </PropertyGroup>
+ <ItemGroup>
+ <Folder Include="libBEXML\" />
+ <Folder Include="libBEXML\parsers\" />
+ <Folder Include="tests\" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="bexmlsrv.py" />
+ <Compile Include="libBEXML\coerce_datetime.py" />
+ <Compile Include="libBEXML\comment.py" />
+ <Compile Include="libBEXML\issue.py" />
+ <Compile Include="libBEXML\parserbase.py" />
+ <Compile Include="libBEXML\parsers\be_dir.py" />
+ <Compile Include="libBEXML\parsers\__init__.py" />
+ <Compile Include="libBEXML\propertieddictionary.py" />
+ <Compile Include="libBEXML\__init__.py" />
+ <Compile Include="setup.py" />
+ <Compile Include="tests\TestParseBErepoWithHTTP.py" />
+ <Compile Include="tests\TestParseBErepoWithLib.py" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="tests\bugs.bugseverywhere.org.xml" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
+</Project>
@@ -0,0 +1,75 @@
+#!/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)
View
@@ -0,0 +1,260 @@
+#####################################################################################
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+# This source code is subject to terms and conditions of the Apache License, Version 2.0. A
+# copy of the license can be found in the License.html file at the root of this distribution. If
+# you cannot locate the Apache License, Version 2.0, please send an email to
+# ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
+# by the terms of the Apache License, Version 2.0.
+#
+# You must not remove this notice, or any other, from this software.
+#
+#
+#####################################################################################
+
+"""
+pyc: The Command-Line Python Compiler
+
+Usage: ipy.exe pyc.py [options] file [file ...]
+
+Options:
+ /out:output_file Output file name (default is main_file.<extenstion>)
+ /target:dll Compile only into dll. Default
+ /target:exe Generate console executable stub for startup in addition to dll.
+ /target:winexe Generate windows executable stub for startup in addition to dll.
+ /files:input_file Read to read files from, one per line.
+ /? /h This message
+
+EXE/WinEXE specific options:
+ /main:main_file.py Main file of the project (module to be executed first)
+ /platform:x86 Compile for x86 only
+ /platform:x64 Compile for x64 only
+ /embed Embeds the generated DLL as a resource into the executable which is loaded at runtime
+ /standalone Embeds the IronPython assemblies into the stub executable.
+
+Example:
+ ipy.exe pyc.py /main:Program.py Form.py /target:winexe
+"""
+
+import sys
+import clr
+clr.AddReferenceByPartialName("IronPython")
+
+from System.Collections.Generic import List
+import IronPython.Hosting as Hosting
+from IronPython.Runtime.Operations import PythonOps
+import System
+from System.Reflection import Emit, Assembly
+from System.Reflection.Emit import OpCodes, AssemblyBuilderAccess
+from System.Reflection import AssemblyName, TypeAttributes, MethodAttributes, ResourceAttributes, CallingConventions
+
+def GenerateExe(name, targetKind, platform, machine, main_module, embed, standalone):
+ """generates the stub .EXE file for starting the app"""
+ aName = AssemblyName(System.IO.FileInfo(name).Name)
+ ab = PythonOps.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave)
+ mb = ab.DefineDynamicModule(name, aName.Name + ".exe")
+ tb = mb.DefineType("PythonMain", TypeAttributes.Public)
+ assemblyResolveMethod = None
+
+ if standalone:
+ print "Generating stand alone executable"
+ embed = True
+
+ for a in System.AppDomain.CurrentDomain.GetAssemblies():
+ n = AssemblyName(a.FullName)
+ if not a.IsDynamic and not a.EntryPoint and (n.Name.StartsWith("IronPython") or n.Name in ['Microsoft.Dynamic', 'Microsoft.Scripting']):
+ print "\tEmbedding %s %s" % (n.Name, str(n.Version))
+ f = System.IO.FileStream(a.Location, System.IO.FileMode.Open, System.IO.FileAccess.Read)
+ mb.DefineManifestResource("Dll." + n.Name, f, ResourceAttributes.Public)
+
+ # we currently do no error checking on what is passed in to the assemblyresolve event handler
+ assemblyResolveMethod = tb.DefineMethod("AssemblyResolve", MethodAttributes.Public | MethodAttributes.Static, clr.GetClrType(Assembly), (clr.GetClrType(System.Object), clr.GetClrType(System.ResolveEventArgs)))
+ gen = assemblyResolveMethod.GetILGenerator()
+ s = gen.DeclareLocal(clr.GetClrType(System.IO.Stream)) # resource stream
+ gen.Emit(OpCodes.Ldnull)
+ gen.Emit(OpCodes.Stloc, s)
+ d = gen.DeclareLocal(clr.GetClrType(System.Array[System.Byte])) # data buffer
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("GetEntryAssembly"), ())
+ gen.Emit(OpCodes.Ldstr, "Dll.")
+ gen.Emit(OpCodes.Ldarg_1) # The event args
+ gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.ResolveEventArgs).GetMethod("get_Name"), ())
+ gen.Emit(OpCodes.Newobj, clr.GetClrType(AssemblyName).GetConstructor((str, )))
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(AssemblyName).GetMethod("get_Name"), ())
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(str).GetMethod("Concat", (str, str)), ())
+ gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(Assembly).GetMethod("GetManifestResourceStream", (str, )), ())
+ gen.Emit(OpCodes.Stloc, s)
+ gen.Emit(OpCodes.Ldloc, s)
+ gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.IO.Stream).GetMethod("get_Length"), ())
+ gen.Emit(OpCodes.Newarr, clr.GetClrType(System.Byte))
+ gen.Emit(OpCodes.Stloc, d)
+ gen.Emit(OpCodes.Ldloc, s)
+ gen.Emit(OpCodes.Ldloc, d)
+ gen.Emit(OpCodes.Ldc_I4_0)
+ gen.Emit(OpCodes.Ldloc, s)
+ gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.IO.Stream).GetMethod("get_Length"), ())
+ gen.Emit(OpCodes.Conv_I4)
+ gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.IO.Stream).GetMethod("Read", (clr.GetClrType(System.Array[System.Byte]), int, int)), ())
+ gen.Emit(OpCodes.Pop)
+ gen.Emit(OpCodes.Ldloc, d)
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("Load", (clr.GetClrType(System.Array[System.Byte]), )), ())
+ gen.Emit(OpCodes.Ret)
+
+ # generate a static constructor to assign the AssemblyResolve handler (otherwise it tries to use IronPython before it adds the handler)
+ # the other way of handling this would be to move the call to InitializeModule into a separate method.
+ staticConstructor = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, System.Type.EmptyTypes)
+ gen = staticConstructor.GetILGenerator()
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.AppDomain).GetMethod("get_CurrentDomain"), ())
+ gen.Emit(OpCodes.Ldnull)
+ gen.Emit(OpCodes.Ldftn, assemblyResolveMethod)
+ gen.Emit(OpCodes.Newobj, clr.GetClrType(System.ResolveEventHandler).GetConstructor((clr.GetClrType(System.Object), clr.GetClrType(System.IntPtr))))
+ gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.AppDomain).GetMethod("add_AssemblyResolve"), ())
+ gen.Emit(OpCodes.Ret)
+
+ mainMethod = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, int, ())
+ if targetKind == System.Reflection.Emit.PEFileKinds.WindowApplication:
+ mainMethod.SetCustomAttribute(clr.GetClrType(System.STAThreadAttribute).GetConstructor(()), System.Array[System.Byte](()))
+ gen = mainMethod.GetILGenerator()
+
+ # get the ScriptCode assembly...
+ if embed:
+ # put the generated DLL into the resources for the stub exe
+ w = mb.DefineResource("IPDll.resources", "Embedded IronPython Generated DLL")
+ w.AddResource("IPDll." + name, System.IO.File.ReadAllBytes(name + ".dll"))
+ System.IO.File.Delete(name + ".dll")
+
+ # generate code to load the resource
+ gen.Emit(OpCodes.Ldstr, "IPDll")
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("GetEntryAssembly"), ())
+ gen.Emit(OpCodes.Newobj, clr.GetClrType(System.Resources.ResourceManager).GetConstructor((str, clr.GetClrType(Assembly))))
+ gen.Emit(OpCodes.Ldstr, "IPDll." + name)
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Resources.ResourceManager).GetMethod("GetObject", (str, )), ())
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Reflection.Assembly).GetMethod("Load", (clr.GetClrType(System.Array[System.Byte]), )), ())
+ else:
+ # variables for saving original working directory und return code of script
+ wdSave = gen.DeclareLocal(str)
+
+ # save current working directory
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Environment).GetMethod("get_CurrentDirectory"), ())
+ gen.Emit(OpCodes.Stloc, wdSave)
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("GetEntryAssembly"), ())
+ gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(Assembly).GetMethod("get_Location"), ())
+ gen.Emit(OpCodes.Newobj, clr.GetClrType(System.IO.FileInfo).GetConstructor( (str, ) ))
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.IO.FileInfo).GetMethod("get_Directory"), ())
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.IO.DirectoryInfo).GetMethod("get_FullName"), ())
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Environment).GetMethod("set_CurrentDirectory"), ())
+ gen.Emit(OpCodes.Ldstr, name + ".dll")
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.IO.Path).GetMethod("GetFullPath", (clr.GetClrType(str), )), ())
+ # result of GetFullPath stays on the stack during the restore of the
+ # original working directory
+
+ # restore original working directory
+ gen.Emit(OpCodes.Ldloc, wdSave)
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Environment).GetMethod("set_CurrentDirectory"), ())
+
+ # for the LoadFile() call, the full path of the assembly is still is on the stack
+ # as the result from the call to GetFullPath()
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Reflection.Assembly).GetMethod("LoadFile", (clr.GetClrType(str), )), ())
+
+ # emit module name
+ gen.Emit(OpCodes.Ldstr, "__main__") # main module name
+ gen.Emit(OpCodes.Ldnull) # no references
+ gen.Emit(OpCodes.Ldc_I4_0) # don't ignore environment variables for engine startup
+
+ # call InitializeModule
+ # (this will also run the script)
+ gen.EmitCall(OpCodes.Call, clr.GetClrType(PythonOps).GetMethod("InitializeModuleEx"), ())
+ gen.Emit(OpCodes.Ret)
+ tb.CreateType()
+ ab.SetEntryPoint(mainMethod, targetKind)
+ ab.Save(aName.Name + ".exe", platform, machine)
+
+def Main(args):
+ files = []
+ main = None # The main file to start the execution (passed to the PythonCompiler)
+ main_name = None # File which will drive the name of the assembly if "output" not provided
+ output = None # Output assembly name
+ target = System.Reflection.Emit.PEFileKinds.Dll
+ platform = System.Reflection.PortableExecutableKinds.ILOnly
+ machine = System.Reflection.ImageFileMachine.I386
+ embed = False # True to embed the generated DLL into the executable
+ standalone = False # True to embed all the IronPython and Microsoft.Scripting DLL's into the generated exe
+
+ for arg in args:
+ if arg.startswith("/main:"):
+ main_name = main = arg[6:]
+ # only override the target kind if its current a DLL
+ if target == System.Reflection.Emit.PEFileKinds.Dll:
+ target = System.Reflection.Emit.PEFileKinds.ConsoleApplication
+
+ elif arg.startswith("/out:"):
+ output = arg[5:]
+
+ elif arg.startswith("/target:"):
+ tgt = arg[8:]
+ if tgt == "exe": target = System.Reflection.Emit.PEFileKinds.ConsoleApplication
+ elif tgt == "winexe": target = System.Reflection.Emit.PEFileKinds.WindowApplication
+ else: target = System.Reflection.Emit.PEFileKinds.Dll
+
+ elif arg.startswith("/platform:"):
+ pform = arg[10:]
+ if pform == "x86":
+ platform = System.Reflection.PortableExecutableKinds.ILOnly | System.Reflection.PortableExecutableKinds.Required32Bit
+ machine = System.Reflection.ImageFileMachine.I386
+ elif pform == "x64":
+ platform = System.Reflection.PortableExecutableKinds.ILOnly | System.Reflection.PortableExecutableKinds.PE32Plus
+ machine = System.Reflection.ImageFileMachine.AMD64
+ else:
+ platform = System.Reflection.PortableExecutableKinds.ILOnly
+ machine = System.Reflection.ImageFileMachine.I386
+
+ elif arg.startswith("/embed"):
+ embed = True
+
+ elif arg.startswith("/standalone"):
+ standalone = True
+
+ elif arg.startswith("/files:"):
+ with open(arg[7:], 'r') as ih:
+ files+=[x[:-1] for x in ih.readlines()]
+
+ elif arg in ["/?", "-?", "/h", "-h"]:
+ print __doc__
+ sys.exit(0)
+
+ else:
+ files.append(arg)
+
+ if not files and not main_name:
+ print __doc__
+ sys.exit(0)
+
+ if target != System.Reflection.Emit.PEFileKinds.Dll and main_name == None:
+ print __doc__
+ sys.exit(0)
+ print "EXEs require /main:<filename> to be specified"
+
+ if not output and main_name:
+ output = System.IO.Path.GetFileNameWithoutExtension(main_name)
+ elif not output and files:
+ output = System.IO.Path.GetFileNameWithoutExtension(files[0])
+
+ print "Input Files:"
+ for file in files:
+ print "\t%s" % file
+
+ print "Output:\n\t%s" % output
+ print "Target:\n\t%s" % target
+ print "Platform:\n\t%s" % platform
+ print "Machine:\n\t%s" % machine
+
+ print "Compiling..."
+ clr.CompileModules(output + ".dll", mainModule = main_name, *files)
+
+ if target != System.Reflection.Emit.PEFileKinds.Dll:
+ GenerateExe(output, target, platform, machine, main_name, embed, standalone)
+
+ print "Saved to %s" % (output, )
+
+if __name__ == "__main__":
+ Main(sys.argv[1:])
Oops, something went wrong.

0 comments on commit 14dabd5

Please sign in to comment.