Permalink
Browse files

Fix 32288 and 26706

- Add option to pyc /standalone to embed the IronPython assemblies into the stub exe
  * Added catch of ArgumentException in sys.GetPrefix to handle when there is no .Location
- Added check for IRONPYTHONPATH in the InitializeModule method
  • Loading branch information...
slide committed Feb 24, 2012
1 parent 88d48b9 commit 08322dfc55d83c4f58b6a199f80e18dffca7ecba
@@ -56,6 +56,8 @@ public static class SysModule {
prefix = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
} catch (SecurityException) {
prefix = String.Empty;
+ } catch (ArgumentException) {
+ prefix = String.Empty;
}
#else
prefix = String.Empty;
@@ -3697,6 +3697,15 @@ public static partial class PythonOps {
/// the exit code that the program reported via SystemExit or 0.
/// </summary>
public static int InitializeModule(Assembly/*!*/ precompiled, string/*!*/ main, string[] references) {
+ return InitializeModuleEx(precompiled, main, references, false);
+ }
+
+ /// <summary>
+ /// Provides the entry point for a compiled module. The stub exe calls into InitializeModule which
+ /// does the actual work of adding references and importing the main module. Upon completion it returns
+ /// the exit code that the program reported via SystemExit or 0.
+ /// </summary>
+ public static int InitializeModuleEx(Assembly/*!*/ precompiled, string/*!*/ main, string[] references, bool ignoreEnvVars) {
ContractUtils.RequiresNotNull(precompiled, "precompiled");
ContractUtils.RequiresNotNull(main, "main");
@@ -3707,6 +3716,17 @@ public static partial class PythonOps {
var pythonContext = (PythonContext)HostingHelpers.GetLanguageContext(pythonEngine);
+ if (!ignoreEnvVars) {
+ int pathIndex = pythonContext.PythonOptions.SearchPaths.Count;
+ string path = Environment.GetEnvironmentVariable("IRONPYTHONPATH");
+ if (path != null && path.Length > 0) {
+ string[] paths = path.Split(Path.PathSeparator);
+ foreach (string p in paths) {
+ pythonContext.InsertIntoPath(pathIndex++, p);
+ }
+ }
+ // TODO: add more environment variable setup here...
+ }
foreach (var scriptCode in SavableScriptCode.LoadFromAssembly(pythonContext.DomainManager, precompiled)) {
pythonContext.GetCompiledLoader().AddScriptCode(scriptCode);
@@ -30,7 +30,7 @@
/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
@@ -46,22 +46,78 @@
import System
from System.Reflection import Emit, Assembly
from System.Reflection.Emit import OpCodes, AssemblyBuilderAccess
-from System.Reflection import AssemblyName, TypeAttributes, MethodAttributes
+from System.Reflection import AssemblyName, TypeAttributes, MethodAttributes, ResourceAttributes, CallingConventions
-def GenerateExe(name, targetKind, platform, machine, main_module, embed=False):
+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
+ # 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")
@@ -100,15 +156,14 @@ def GenerateExe(name, targetKind, platform, machine, main_module, embed=False):
gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Reflection.Assembly).GetMethod("LoadFile", (clr.GetClrType(str), )), ())
# emit module name
- gen.Emit(OpCodes.Ldstr, "__main__")
- gen.Emit(OpCodes.Ldnull)
+ 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("InitializeModule"), ())
-
+ 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)
@@ -122,6 +177,7 @@ def Main(args):
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:"):
@@ -154,6 +210,9 @@ def Main(args):
elif arg.startswith("/embed"):
embed = True
+ elif arg.startswith("/standalone"):
+ standalone = True
+
elif arg in ["/?", "-?", "/h", "-h"]:
print __doc__
sys.exit(0)
@@ -188,7 +247,7 @@ def Main(args):
clr.CompileModules(output + ".dll", mainModule = main_name, *files)
if target != System.Reflection.Emit.PEFileKinds.Dll:
- GenerateExe(output, target, platform, machine, main_name, embed)
+ GenerateExe(output, target, platform, machine, main_name, embed, standalone)
print "Saved to %s" % (output, )

0 comments on commit 08322df

Please sign in to comment.