From 7aa56f374527af4442bad108cc432bf855582092 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Wed, 27 Mar 2024 22:43:14 +0800 Subject: [PATCH 1/8] Compiler the exe host into ScriptCode assembly Have not add Win32 Icon since AssemblyBuilder dose not have DefineIconResource method --- Src/IronPython/Runtime/ClrModule.cs | 196 ++++++++++++++++++++- Src/IronPythonCompiler/AssemblyResolver.cs | 3 - Src/IronPythonCompiler/Config.cs | 40 +++-- Src/IronPythonCompiler/ConsoleOps.cs | 3 - Src/IronPythonCompiler/Program.cs | 178 +++++++------------ 5 files changed, 280 insertions(+), 140 deletions(-) diff --git a/Src/IronPython/Runtime/ClrModule.cs b/Src/IronPython/Runtime/ClrModule.cs index 7e7b404bc..a3eec900b 100644 --- a/Src/IronPython/Runtime/ClrModule.cs +++ b/Src/IronPython/Runtime/ClrModule.cs @@ -15,6 +15,10 @@ using ComTypeLibDesc = Microsoft.Scripting.ComInterop.ComTypeLibDesc; #endif +#if FEATURE_FILESYSTEM && FEATURE_REFEMIT +using System.Reflection.Emit; +#endif + using System; using System.Collections; using System.Collections.Generic; @@ -870,7 +874,7 @@ public static object Convert(CodeContext/*!*/ context, object o, Type toType) { /// Provides a helper for compiling a group of modules into a single assembly. The assembly can later be /// reloaded using the clr.AddReference API. /// - public static void CompileModules(CodeContext/*!*/ context, string/*!*/ assemblyName, [ParamDictionary]IDictionary kwArgs, params string/*!*/[]/*!*/ filenames) { + public static void CompileModules(CodeContext/*!*/ context, string/*!*/ assemblyName, [ParamDictionary] IDictionary kwArgs, params string/*!*/[]/*!*/ filenames) { ContractUtils.RequiresNotNull(assemblyName, nameof(assemblyName)); ContractUtils.RequiresNotNullItems(filenames, nameof(filenames)); @@ -942,6 +946,196 @@ public static void CompileModules(CodeContext/*!*/ context, string/*!*/ assembly SavableScriptCode.SaveToAssembly(assemblyName, kwArgs, code.ToArray()); } + + public static AssemblyGen CreateAssemblyGen(CodeContext/*!*/ context, string/*!*/ assemblyName, [ParamDictionary]IDictionary kwArgs, params string/*!*/[]/*!*/ filenames) { + ContractUtils.RequiresNotNull(assemblyName, nameof(assemblyName)); + ContractUtils.RequiresNotNullItems(filenames, nameof(filenames)); + + PythonContext pc = context.LanguageContext; + + for (int i = 0; i < filenames.Length; i++) { + filenames[i] = Path.GetFullPath(filenames[i]); + } + + Dictionary packageMap = BuildPackageMap(filenames); + + List code = new List(); + foreach (string filename in filenames) { + if (!pc.DomainManager.Platform.FileExists(filename)) { + throw PythonOps.IOError($"Couldn't find file for compilation: {filename}"); + } + + ScriptCode sc; + + string modName; + string dname = Path.GetDirectoryName(filename); + string outFilename = ""; + if (Path.GetFileName(filename) == "__init__.py") { + // remove __init__.py to get package name + dname = Path.GetDirectoryName(dname); + if (String.IsNullOrEmpty(dname)) { + modName = Path.GetDirectoryName(filename); + } else { + modName = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(filename)); + } + outFilename = Path.DirectorySeparatorChar + "__init__.py"; + } else { + modName = Path.GetFileNameWithoutExtension(filename); + } + + // see if we have a parent package, if so incorporate it into + // our name + if (packageMap.TryGetValue(dname, out string parentPackage)) { + modName = parentPackage + "." + modName; + } + + outFilename = modName.Replace('.', Path.DirectorySeparatorChar) + outFilename; + + SourceUnit su = pc.CreateSourceUnit( + new FileStreamContentProvider( + context.LanguageContext.DomainManager.Platform, + filename + ), + outFilename, + pc.DefaultEncoding, + SourceCodeKind.File + ); + + sc = context.LanguageContext.GetScriptCode(su, modName, ModuleOptions.Initialize, Compiler.CompilationMode.ToDisk); + + code.Add((SavableScriptCode)sc); + } + + if (kwArgs != null && kwArgs.TryGetValue("mainModule", out object mainModule)) { + if (mainModule is string strModule) { + if (!pc.DomainManager.Platform.FileExists(strModule)) { + throw PythonOps.IOError("Couldn't find main file for compilation: {0}", strModule); + } + + SourceUnit su = pc.CreateFileUnit(strModule, pc.DefaultEncoding, SourceCodeKind.File); + code.Add((SavableScriptCode)context.LanguageContext.GetScriptCode(su, "__main__", ModuleOptions.Initialize, Compiler.CompilationMode.ToDisk)); + } + } + + return CreateAssemblyGen(assemblyName, kwArgs, code.ToArray()); + } + + private class CodeInfo { + public readonly MethodBuilder Builder; + public readonly ScriptCode Code; + public readonly Type DelegateType; + + public CodeInfo(MethodBuilder builder, ScriptCode code, Type delegateType) { + Builder = builder; + Code = code; + DelegateType = delegateType; + } + } + + private static AssemblyGen CreateAssemblyGen(string assemblyName, IDictionary assemblyAttributes, params SavableScriptCode[] codes) { + ContractUtils.RequiresNotNull(assemblyName, nameof(assemblyName)); + ContractUtils.RequiresNotNullItems(codes, nameof(codes)); + + // break the assemblyName into it's dir/name/extension + string dir = Path.GetDirectoryName(assemblyName); + if (string.IsNullOrEmpty(dir)) { + dir = Environment.CurrentDirectory; + } + + string name = Path.GetFileNameWithoutExtension(assemblyName); + string ext = Path.GetExtension(assemblyName); + + // build the assembly & type gen that all the script codes will live in... + AssemblyGen ag = new AssemblyGen(new AssemblyName(name), dir, ext, /*emitSymbols*/false, assemblyAttributes); + TypeBuilder tb = ag.DefinePublicType("DLRCachedCode", typeof(object), true); + TypeGen tg = new TypeGen(ag, tb); + // then compile all of the code + + MethodInfo compileForSave = + typeof(SavableScriptCode).GetMethod( + "CompileForSave", + BindingFlags.Instance | BindingFlags.NonPublic, + Type.DefaultBinder, + new[] { typeof(TypeGen) }, + null); + + Dictionary> langCtxBuilders = new Dictionary>(); + foreach (SavableScriptCode sc in codes) { + if (!langCtxBuilders.TryGetValue(sc.LanguageContext.GetType(), out List builders)) { + langCtxBuilders[sc.LanguageContext.GetType()] = builders = new List(); + } + KeyValuePair compInfo = (KeyValuePair)compileForSave.Invoke(sc, new[] { tg }); + + builders.Add(new CodeInfo(compInfo.Key, sc, compInfo.Value)); + } + + MethodBuilder mb = tb.DefineMethod( + "GetScriptCodeInfo", + MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.Static, + typeof(MutableTuple), + ReflectionUtils.EmptyTypes); + + ILGen ilgen = new ILGen(mb.GetILGenerator()); + + var langsWithBuilders = langCtxBuilders.ToArray(); + + // lang ctx array + ilgen.EmitArray(typeof(Type), langsWithBuilders.Length, (index) => { + ilgen.Emit(OpCodes.Ldtoken, langsWithBuilders[index].Key); + ilgen.EmitCall(typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), new[] { typeof(RuntimeTypeHandle) })); + }); + + // builders array of array + ilgen.EmitArray(typeof(Delegate[]), langsWithBuilders.Length, (index) => { + List builders = langsWithBuilders[index].Value; + + ilgen.EmitArray(typeof(Delegate), builders.Count, (innerIndex) => { + ilgen.EmitNull(); + ilgen.Emit(OpCodes.Ldftn, builders[innerIndex].Builder); + ilgen.EmitNew( + builders[innerIndex].DelegateType, + new[] { typeof(object), typeof(IntPtr) } + ); + }); + }); + + // paths array of array + ilgen.EmitArray(typeof(string[]), langsWithBuilders.Length, (index) => { + List builders = langsWithBuilders[index].Value; + + ilgen.EmitArray(typeof(string), builders.Count, (innerIndex) => { + ilgen.EmitString(builders[innerIndex].Code.SourceUnit.Path); + }); + }); + + // 4th element in tuple - custom per-language data + ilgen.EmitArray(typeof(string[]), langsWithBuilders.Length, (index) => { + List builders = langsWithBuilders[index].Value; + + ilgen.EmitArray(typeof(string), builders.Count, (innerIndex) => { + ICustomScriptCodeData data = builders[innerIndex].Code as ICustomScriptCodeData; + if (data != null) { + ilgen.EmitString(data.GetCustomScriptCodeData()); + } else { + ilgen.Emit(OpCodes.Ldnull); + } + }); + }); + + ilgen.EmitNew( + typeof(MutableTuple), + new[] { typeof(Type[]), typeof(Delegate[][]), typeof(string[][]), typeof(string[][]) } + ); + ilgen.Emit(OpCodes.Ret); + + mb.SetCustomAttribute(new CustomAttributeBuilder( + typeof(DlrCachedCodeAttribute).GetConstructor(ReflectionUtils.EmptyTypes), + ArrayUtils.EmptyObjects + )); + + tg.FinishType(); + return ag; + } #endif #if FEATURE_REFEMIT diff --git a/Src/IronPythonCompiler/AssemblyResolver.cs b/Src/IronPythonCompiler/AssemblyResolver.cs index 57f388c8f..d8039a90e 100644 --- a/Src/IronPythonCompiler/AssemblyResolver.cs +++ b/Src/IronPythonCompiler/AssemblyResolver.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using IKVM.Reflection; diff --git a/Src/IronPythonCompiler/Config.cs b/Src/IronPythonCompiler/Config.cs index 72e0d6747..f4964e2b2 100644 --- a/Src/IronPythonCompiler/Config.cs +++ b/Src/IronPythonCompiler/Config.cs @@ -1,13 +1,10 @@ using System; -using System.IO; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using IKVM.Reflection; -using IKVM.Reflection.Emit; -using System.Resources; +using System.IO; using System.Reflection; +using System.Reflection.Emit; +using System.Text; + using Microsoft.Scripting.Runtime; namespace IronPythonCompiler { @@ -16,8 +13,8 @@ public class Config { public Config() { Embed = false; Files = new List(); - Platform = IKVM.Reflection.PortableExecutableKinds.ILOnly; - Machine = IKVM.Reflection.ImageFileMachine.AMD64; + Platform = PortableExecutableKinds.ILOnly; + Machine = ImageFileMachine.AMD64; Standalone = false; Target = PEFileKinds.Dll; UseMta = false; @@ -125,12 +122,12 @@ public bool Embed { internal set; } - public IKVM.Reflection.ImageFileMachine Machine { + public ImageFileMachine Machine { get; private set; } - public IKVM.Reflection.PortableExecutableKinds Platform { + public PortableExecutableKinds Platform { get; private set; } @@ -169,16 +166,25 @@ public void ParseArgs(IEnumerable args, List respFiles = null) { string plat = arg.Substring(10).Trim('"'); switch (plat) { case "x86": - Platform = IKVM.Reflection.PortableExecutableKinds.ILOnly | IKVM.Reflection.PortableExecutableKinds.Required32Bit; - Machine = IKVM.Reflection.ImageFileMachine.I386; + Platform = PortableExecutableKinds.ILOnly | PortableExecutableKinds.Required32Bit; + Machine = ImageFileMachine.I386; break; case "x64": - Platform = IKVM.Reflection.PortableExecutableKinds.ILOnly | IKVM.Reflection.PortableExecutableKinds.PE32Plus; - Machine = IKVM.Reflection.ImageFileMachine.AMD64; + Platform = PortableExecutableKinds.ILOnly | PortableExecutableKinds.PE32Plus; + Machine = ImageFileMachine.AMD64; + break; + case "arm": + case "arm32": + Platform = PortableExecutableKinds.ILOnly | PortableExecutableKinds.Required32Bit; + Machine = ImageFileMachine.ARM; + break; + case "arm64": + Platform = PortableExecutableKinds.ILOnly | PortableExecutableKinds.PE32Plus; + Machine = ImageFileMachine.ARM; break; default: - Platform = IKVM.Reflection.PortableExecutableKinds.ILOnly; - Machine = IKVM.Reflection.ImageFileMachine.AMD64; + Platform = PortableExecutableKinds.ILOnly; + Machine = ImageFileMachine.I386; break; } } else if (arg.StartsWith("/win32icon:", StringComparison.Ordinal)) { diff --git a/Src/IronPythonCompiler/ConsoleOps.cs b/Src/IronPythonCompiler/ConsoleOps.cs index 93f1b7cf9..9cb352273 100644 --- a/Src/IronPythonCompiler/ConsoleOps.cs +++ b/Src/IronPythonCompiler/ConsoleOps.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace IronPythonCompiler { public static class ConsoleOps { diff --git a/Src/IronPythonCompiler/Program.cs b/Src/IronPythonCompiler/Program.cs index 115b8f502..1f3428f4a 100644 --- a/Src/IronPythonCompiler/Program.cs +++ b/Src/IronPythonCompiler/Program.cs @@ -5,20 +5,17 @@ using System; using System.Collections.Generic; using System.IO; -using System.Resources; - -using IKVM.Reflection; -using IKVM.Reflection.Emit; +using System.Reflection; +using System.Reflection.Emit; using IronPython.Hosting; using IronPython.Runtime; using IronPython.Runtime.Operations; +using Microsoft.Scripting.Generation; using Microsoft.Scripting.Hosting; using Microsoft.Scripting.Runtime; -using Type = IKVM.Reflection.Type; - namespace IronPythonCompiler { public class Program { @@ -27,33 +24,15 @@ public class Program { /// Generates the stub .exe file for starting the app /// /// - private static void GenerateExe(Config config) { - var u = new Universe(); - var aName = new AssemblyName(Path.GetFileNameWithoutExtension(new FileInfo(config.Output).Name)); - var ab = u.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save, config.OutputPath); - var mb = ab.DefineDynamicModule(config.Output, aName.Name + (aName.Name.EndsWith(".exe", StringComparison.Ordinal) ? string.Empty : ".exe")); - var tb = mb.DefineType("PythonMain", IKVM.Reflection.TypeAttributes.Public); + private static void GenerateExe(AssemblyGen assemblyGen, Config config) { + var ab = assemblyGen.AssemblyBuilder; + var mb = assemblyGen.ModuleBuilder; + var tb = mb.DefineType("PythonMain", TypeAttributes.Public); if (!string.IsNullOrEmpty(config.Win32Icon)) { - ab.__DefineIconResource(File.ReadAllBytes(config.Win32Icon)); + //ab.DefineIconResource(File.ReadAllBytes(config.Win32Icon)); } - var attributes = new List> { - Tuple.Create(config.FileVersion, u.Import(typeof(System.Reflection.AssemblyFileVersionAttribute))), - Tuple.Create(config.ProductName, u.Import(typeof(System.Reflection.AssemblyProductAttribute))), - Tuple.Create(config.ProductVersion, u.Import(typeof(System.Reflection.AssemblyInformationalVersionAttribute))), - Tuple.Create(config.Copyright, u.Import(typeof(System.Reflection.AssemblyCopyrightAttribute))) - }; - - foreach (var attr in attributes) { - if (!string.IsNullOrWhiteSpace(config.FileVersion)) { - CustomAttributeBuilder builder = new CustomAttributeBuilder(attr.Item2.GetConstructor(new[] { u.Import(typeof(string)) }), new object[] { attr.Item1 }); - ab.SetCustomAttribute(builder); - } - } - - ab.DefineVersionInfoResource(); - MethodBuilder assemblyResolveMethod = null; ILGenerator gen = null; @@ -72,12 +51,12 @@ private static void GenerateExe(Config config) { "System.Runtime.CompilerServices.Unsafe", }; - foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies()) { + foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { var n = new AssemblyName(a.FullName); if (!a.IsDynamic && a.EntryPoint == null && (n.Name.StartsWith("IronPython", StringComparison.Ordinal) || embedAssemblies.Contains(n.Name))) { ConsoleOps.Info($"\tEmbedded {n.Name} {n.Version}"); var f = new FileStream(a.Location, FileMode.Open, FileAccess.Read); - mb.DefineManifestResource("Dll." + n.Name, f, IKVM.Reflection.ResourceAttributes.Public); + mb.DefineManifestResource("Dll." + n.Name, f, ResourceAttributes.Public); } } @@ -85,74 +64,74 @@ private static void GenerateExe(Config config) { var name = Path.GetFileNameWithoutExtension(dll); ConsoleOps.Info($"\tEmbedded {name}"); var f = new FileStream(dll, FileMode.Open, FileAccess.Read); - mb.DefineManifestResource("Dll." + name, f, IKVM.Reflection.ResourceAttributes.Public); + mb.DefineManifestResource("Dll." + 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, u.Import(typeof(System.Reflection.Assembly)), new IKVM.Reflection.Type[] { u.Import(typeof(System.Object)), u.Import(typeof(System.ResolveEventArgs)) }); + assemblyResolveMethod = tb.DefineMethod("AssemblyResolve", MethodAttributes.Public | MethodAttributes.Static, typeof(System.Reflection.Assembly), new Type[] { typeof(System.Object), typeof(System.ResolveEventArgs) }); gen = assemblyResolveMethod.GetILGenerator(); - var s = gen.DeclareLocal(u.Import(typeof(System.IO.Stream))); // resource stream + var s = gen.DeclareLocal(typeof(Stream)); // resource stream gen.Emit(OpCodes.Ldnull); gen.Emit(OpCodes.Stloc, s); - var d = gen.DeclareLocal(u.Import(typeof(byte[]))); // data buffer; - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Reflection.Assembly)).GetMethod("GetEntryAssembly"), Type.EmptyTypes); + var d = gen.DeclareLocal(typeof(byte[])); // data buffer; + gen.EmitCall(OpCodes.Call, typeof(Assembly).GetMethod("GetEntryAssembly"), Type.EmptyTypes); gen.Emit(OpCodes.Ldstr, "Dll."); gen.Emit(OpCodes.Ldarg_1); // The event args - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.ResolveEventArgs)).GetMethod("get_Name"), Type.EmptyTypes); - gen.Emit(OpCodes.Newobj, u.Import(typeof(System.Reflection.AssemblyName)).GetConstructor(new IKVM.Reflection.Type[] { u.Import(typeof(string)) })); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Reflection.AssemblyName)).GetMethod("get_Name"), Type.EmptyTypes); - gen.EmitCall(OpCodes.Call, u.Import(typeof(string)).GetMethod("Concat", new IKVM.Reflection.Type[] { u.Import(typeof(string)), u.Import(typeof(string)) }), Type.EmptyTypes); - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.Reflection.Assembly)).GetMethod("GetManifestResourceStream", new IKVM.Reflection.Type[] { u.Import(typeof(string)) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Callvirt, typeof(ResolveEventArgs).GetMethod("get_Name"), Type.EmptyTypes); + gen.Emit(OpCodes.Newobj, typeof(AssemblyName).GetConstructor(new Type[] { typeof(string) })); + gen.EmitCall(OpCodes.Call, typeof(AssemblyName).GetMethod("get_Name"), Type.EmptyTypes); + gen.EmitCall(OpCodes.Call, typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Callvirt, typeof(Assembly).GetMethod("GetManifestResourceStream", new Type[] { typeof(string) }), Type.EmptyTypes); gen.Emit(OpCodes.Stloc, s); gen.Emit(OpCodes.Ldloc, s); - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.IO.Stream)).GetMethod("get_Length"), Type.EmptyTypes); - gen.Emit(OpCodes.Newarr, u.Import(typeof(System.Byte))); + gen.EmitCall(OpCodes.Callvirt, typeof(Stream).GetMethod("get_Length"), Type.EmptyTypes); + gen.Emit(OpCodes.Newarr, typeof(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, u.Import(typeof(System.IO.Stream)).GetMethod("get_Length"), Type.EmptyTypes); + gen.EmitCall(OpCodes.Callvirt, typeof(Stream).GetMethod("get_Length"), Type.EmptyTypes); gen.Emit(OpCodes.Conv_I4); - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.IO.Stream)).GetMethod("Read", new IKVM.Reflection.Type[] { u.Import(typeof(byte[])), u.Import(typeof(int)), u.Import(typeof(int)) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Callvirt, typeof(Stream).GetMethod("Read", new Type[] { typeof(byte[]), typeof(int), typeof(int) }), Type.EmptyTypes); gen.Emit(OpCodes.Pop); gen.Emit(OpCodes.Ldloc, d); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Reflection.Assembly)).GetMethod("Load", new IKVM.Reflection.Type[] { u.Import(typeof(byte[])) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Call, typeof(Assembly).GetMethod("Load", new Type[] { typeof(byte[]) }), Type.EmptyTypes); 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. var staticConstructor = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes); gen = staticConstructor.GetILGenerator(); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.AppDomain)).GetMethod("get_CurrentDomain"), Type.EmptyTypes); + gen.EmitCall(OpCodes.Call, typeof(AppDomain).GetMethod("get_CurrentDomain"), Type.EmptyTypes); gen.Emit(OpCodes.Ldnull); gen.Emit(OpCodes.Ldftn, assemblyResolveMethod); - gen.Emit(OpCodes.Newobj, u.Import(typeof(System.ResolveEventHandler)).GetConstructor(new IKVM.Reflection.Type[] { u.Import(typeof(object)), u.Import(typeof(System.IntPtr)) })); - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.AppDomain)).GetMethod("add_AssemblyResolve"), Type.EmptyTypes); + gen.Emit(OpCodes.Newobj, typeof(ResolveEventHandler).GetConstructor(new Type[] { typeof(object), typeof(IntPtr) })); + gen.EmitCall(OpCodes.Callvirt, typeof(AppDomain).GetMethod("add_AssemblyResolve"), Type.EmptyTypes); gen.Emit(OpCodes.Ret); } - var mainMethod = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, u.Import(typeof(int)), Type.EmptyTypes); + var mainMethod = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); if (config.Target == PEFileKinds.WindowApplication && config.UseMta) { - mainMethod.SetCustomAttribute(u.Import(typeof(System.MTAThreadAttribute)).GetConstructor(Type.EmptyTypes), Array.Empty()); + mainMethod.SetCustomAttribute(typeof(MTAThreadAttribute).GetConstructor(Type.EmptyTypes), Array.Empty()); } else if (config.Target == PEFileKinds.WindowApplication || config.Target == PEFileKinds.ConsoleApplication && config.UseSta) { - mainMethod.SetCustomAttribute(u.Import(typeof(System.STAThreadAttribute)).GetConstructor(Type.EmptyTypes), Array.Empty()); + mainMethod.SetCustomAttribute(typeof(STAThreadAttribute).GetConstructor(Type.EmptyTypes), Array.Empty()); } gen = mainMethod.GetILGenerator(); // variables for saving original working directory and return code of script - var strVar = gen.DeclareLocal(u.Import(typeof(string))); - var intVar = gen.DeclareLocal(u.Import(typeof(int))); + var strVar = gen.DeclareLocal(typeof(string)); + var intVar = gen.DeclareLocal(typeof(int)); LocalBuilder dictVar = null; if (config.PythonOptions.Count > 0) { - var True = u.Import(typeof(ScriptingRuntimeHelpers)).GetField("True"); - var False = u.Import(typeof(ScriptingRuntimeHelpers)).GetField("False"); + var True = typeof(ScriptingRuntimeHelpers).GetField("True"); + var False = typeof(ScriptingRuntimeHelpers).GetField("False"); - dictVar = gen.DeclareLocal(u.Import(typeof(Dictionary))); - gen.Emit(OpCodes.Newobj, u.Import(typeof(Dictionary)).GetConstructor(Type.EmptyTypes)); + dictVar = gen.DeclareLocal(typeof(Dictionary)); + gen.Emit(OpCodes.Newobj, typeof(Dictionary).GetConstructor(Type.EmptyTypes)); gen.Emit(OpCodes.Stloc, dictVar); foreach (var option in config.PythonOptions) { @@ -163,59 +142,20 @@ private static void GenerateExe(Config config) { gen.Emit(OpCodes.Ldc_I4_S, val); // this is more optimized else gen.Emit(OpCodes.Ldc_I4, val); - gen.Emit(OpCodes.Box, u.Import(typeof(System.Int32))); + gen.Emit(OpCodes.Box, typeof(int)); } else if (option.Value.Equals(ScriptingRuntimeHelpers.True)) { gen.Emit(OpCodes.Ldsfld, True); } else if (option.Value.Equals(ScriptingRuntimeHelpers.False)) { gen.Emit(OpCodes.Ldsfld, False); } - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(Dictionary)).GetMethod("Add", new IKVM.Reflection.Type[] { u.Import(typeof(string)), u.Import(typeof(object)) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Callvirt, typeof(Dictionary).GetMethod("Add", new Type[] { typeof(string), typeof(object) }), Type.EmptyTypes); } } Label tryStart = gen.BeginExceptionBlock(); // get the ScriptCode assembly... - if (config.Embed) { - // put the generated DLL into the resources for the stub exe - var mem = new MemoryStream(); - var rw = new ResourceWriter(mem); - rw.AddResource("IPDll." + Path.GetFileNameWithoutExtension(config.Output) + ".dll", File.ReadAllBytes(Path.Combine(config.OutputPath, config.Output) + ".dll")); - rw.Generate(); - mem.Position = 0; - mb.DefineManifestResource("IPDll.resources", mem, ResourceAttributes.Public); - File.Delete(Path.Combine(config.OutputPath, config.Output) + ".dll"); - - // generate code to load the resource - gen.Emit(OpCodes.Ldstr, "IPDll"); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Reflection.Assembly)).GetMethod("GetEntryAssembly"), Type.EmptyTypes); - gen.Emit(OpCodes.Newobj, u.Import(typeof(System.Resources.ResourceManager)).GetConstructor(new IKVM.Reflection.Type[] { u.Import(typeof(string)), u.Import(typeof(System.Reflection.Assembly)) })); - gen.Emit(OpCodes.Ldstr, "IPDll." + Path.GetFileNameWithoutExtension(config.Output) + ".dll"); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Resources.ResourceManager)).GetMethod("GetObject", new IKVM.Reflection.Type[] { u.Import(typeof(string)) }), Type.EmptyTypes); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Reflection.Assembly)).GetMethod("Load", new IKVM.Reflection.Type[] { u.Import(typeof(byte[])) }), Type.EmptyTypes); - } else { - // save current working directory - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Environment)).GetMethod("get_CurrentDirectory"), Type.EmptyTypes); - gen.Emit(OpCodes.Stloc, strVar); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Reflection.Assembly)).GetMethod("GetEntryAssembly"), Type.EmptyTypes); - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.Reflection.Assembly)).GetMethod("get_Location"), Type.EmptyTypes); - gen.Emit(OpCodes.Newobj, u.Import(typeof(System.IO.FileInfo)).GetConstructor(new IKVM.Reflection.Type[] { u.Import(typeof(string)) })); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.IO.FileInfo)).GetMethod("get_Directory"), Type.EmptyTypes); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.IO.DirectoryInfo)).GetMethod("get_FullName"), Type.EmptyTypes); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Environment)).GetMethod("set_CurrentDirectory"), Type.EmptyTypes); - gen.Emit(OpCodes.Ldstr, config.Output + ".dll"); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.IO.Path)).GetMethod("GetFullPath", new IKVM.Reflection.Type[] { u.Import(typeof(string)) }), Type.EmptyTypes); - // result of GetFullPath stays on the stack during the restore of the - // original working directory - - // restore original working directory - gen.Emit(OpCodes.Ldloc, strVar); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Environment)).GetMethod("set_CurrentDirectory"), Type.EmptyTypes); - - // 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, u.Import(typeof(System.Reflection.Assembly)).GetMethod("LoadFile", new IKVM.Reflection.Type[] { u.Import(typeof(string)) }), Type.EmptyTypes); - } + gen.EmitCall(OpCodes.Call, typeof(Assembly).GetMethod(nameof(Assembly.GetExecutingAssembly), Type.EmptyTypes), Type.EmptyTypes); // emit module name gen.Emit(OpCodes.Ldstr, "__main__"); // main module name @@ -230,28 +170,28 @@ private static void GenerateExe(Config config) { // call InitializeModuleEx // (this will also run the script) // and put the return code on the stack - gen.EmitCall(OpCodes.Call, u.Import(typeof(PythonOps)).GetMethod(nameof(PythonOps.InitializeModuleEx), - new IKVM.Reflection.Type[] { u.Import(typeof(System.Reflection.Assembly)), u.Import(typeof(string)), u.Import(typeof(string[])), u.Import(typeof(bool)), u.Import(typeof(Dictionary)) }), + gen.EmitCall(OpCodes.Call, typeof(PythonOps).GetMethod(nameof(PythonOps.InitializeModuleEx), + new Type[] { typeof(Assembly), typeof(string), typeof(string[]), typeof(bool), typeof(Dictionary) }), Type.EmptyTypes); gen.Emit(OpCodes.Stloc, intVar); - gen.BeginCatchBlock(u.Import(typeof(Exception))); + gen.BeginCatchBlock(typeof(Exception)); if (config.Target == PEFileKinds.ConsoleApplication) { - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.Exception)).GetMethod("get_Message", Type.EmptyTypes), Type.EmptyTypes); + gen.EmitCall(OpCodes.Callvirt, typeof(Exception).GetMethod("get_Message", Type.EmptyTypes), Type.EmptyTypes); gen.Emit(OpCodes.Stloc, strVar); gen.Emit(OpCodes.Ldstr, config.ErrorMessageFormat); gen.Emit(OpCodes.Ldloc, strVar); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Console)).GetMethod("WriteLine", new IKVM.Reflection.Type[] { u.Import(typeof(string)), u.Import(typeof(string)) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string), typeof(string) }), Type.EmptyTypes); } else { - gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.Exception)).GetMethod("get_Message", Type.EmptyTypes), Type.EmptyTypes); + gen.EmitCall(OpCodes.Callvirt, typeof(Exception).GetMethod("get_Message", Type.EmptyTypes), Type.EmptyTypes); gen.Emit(OpCodes.Stloc, strVar); gen.Emit(OpCodes.Ldstr, config.ErrorMessageFormat); gen.Emit(OpCodes.Ldloc, strVar); - gen.EmitCall(OpCodes.Call, u.Import(typeof(string)).GetMethod("Format", new IKVM.Reflection.Type[] { u.Import(typeof(string)), u.Import(typeof(string)) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Call, typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(string) }), Type.EmptyTypes); gen.Emit(OpCodes.Ldstr, "Error"); gen.Emit(OpCodes.Ldc_I4, (int)System.Windows.Forms.MessageBoxButtons.OK); gen.Emit(OpCodes.Ldc_I4, (int)System.Windows.Forms.MessageBoxIcon.Error); - gen.EmitCall(OpCodes.Call, u.Import(typeof(System.Windows.Forms.MessageBox)).GetMethod("Show", new IKVM.Reflection.Type[] { u.Import(typeof(string)), u.Import(typeof(string)), u.Import(typeof(System.Windows.Forms.MessageBoxButtons)), u.Import(typeof(System.Windows.Forms.MessageBoxIcon)) }), Type.EmptyTypes); + gen.EmitCall(OpCodes.Call, typeof(System.Windows.Forms.MessageBox).GetMethod("Show", new Type[] { typeof(string), typeof(string), typeof(System.Windows.Forms.MessageBoxButtons), typeof(System.Windows.Forms.MessageBoxIcon) }), Type.EmptyTypes); gen.Emit(OpCodes.Pop); } @@ -265,12 +205,9 @@ private static void GenerateExe(Config config) { tb.CreateType(); ab.SetEntryPoint(mainMethod, config.Target); - string fileName = aName.Name.EndsWith(".exe", StringComparison.Ordinal) ? aName.Name : aName.Name + ".exe"; - ab.Save(fileName, config.Platform, config.Machine); } public static int Main(string[] args) { - var files = new List(); var config = new Config(); config.ParseArgs(args); if (!config.Validate()) { @@ -293,16 +230,25 @@ public static int Main(string[] args) { }; try { - ClrModule.CompileModules(DefaultContext.DefaultCLS, - Path.Combine(config.OutputPath, Path.ChangeExtension(config.Output, ".dll")), + string outputfilename = Path.Combine(config.OutputPath, config.Output); + + if (!Path.HasExtension(outputfilename)) { + outputfilename = config.Target == PEFileKinds.Dll + ? Path.ChangeExtension(outputfilename, ".dll") + : Path.ChangeExtension(outputfilename, ".exe"); + } + + var ag = ClrModule.CreateAssemblyGen(DefaultContext.DefaultCLS, + outputfilename, compileOptions, config.Files.ToArray()); - var outputfilename = Path.Combine(config.OutputPath, Path.ChangeExtension(config.Output, ".dll")); if (config.Target != PEFileKinds.Dll) { - outputfilename = Path.Combine(config.OutputPath, Path.ChangeExtension(config.Output, ".exe")); - GenerateExe(config); + GenerateExe(ag, config); } + + ag.AssemblyBuilder.Save(Path.GetFileName(outputfilename), config.Platform, config.Machine); + ConsoleOps.Info($"Saved to {outputfilename}"); } catch (Exception e) { Console.WriteLine(); From 30b5c77c30cd534a060c96e62d1580479422a813 Mon Sep 17 00:00:00 2001 From: wherewhere Date: Thu, 28 Mar 2024 16:05:41 +0800 Subject: [PATCH 2/8] Compiler add nologo option --- Src/IronPythonCompiler/Config.cs | 9 +++++++++ Src/IronPythonCompiler/ConsoleOps.cs | 3 +++ Src/IronPythonCompiler/Program.cs | 1 + Src/IronPythonCompiler/README.md | 2 ++ 4 files changed, 15 insertions(+) diff --git a/Src/IronPythonCompiler/Config.cs b/Src/IronPythonCompiler/Config.cs index f4964e2b2..1e7a652bd 100644 --- a/Src/IronPythonCompiler/Config.cs +++ b/Src/IronPythonCompiler/Config.cs @@ -102,6 +102,11 @@ public bool Standalone { private set; } + public bool NoLogo { + get; + private set; + } + public List Files { get; private set; @@ -158,6 +163,8 @@ public void ParseArgs(IEnumerable args, List respFiles = null) { case "winexe": Target = PEFileKinds.WindowApplication; break; + case "library": + case "dll": default: Target = PEFileKinds.Dll; break; @@ -215,6 +222,8 @@ public void ParseArgs(IEnumerable args, List respFiles = null) { foreach (var f in Directory.EnumerateFiles(Environment.CurrentDirectory, pattern)) { Files.Add(Path.GetFullPath(f)); } + } else if (arg.Equals("/nologo", StringComparison.Ordinal)) { + NoLogo = true; } else if (Array.IndexOf(helpStrings, arg) >= 0) { ConsoleOps.Usage(true); } else if (arg.StartsWith("/py:", StringComparison.Ordinal)) { diff --git a/Src/IronPythonCompiler/ConsoleOps.cs b/Src/IronPythonCompiler/ConsoleOps.cs index 9cb352273..b83c90e1c 100644 --- a/Src/IronPythonCompiler/ConsoleOps.cs +++ b/Src/IronPythonCompiler/ConsoleOps.cs @@ -2,6 +2,7 @@ namespace IronPythonCompiler { public static class ConsoleOps { + public static bool NoLogo { get; set; } public static void Error(string format, params object[] args) { Error(false, format, args); @@ -30,6 +31,7 @@ public static void Warning(string format, params object[] args) { } public static void Info(string format, params object[] args) { + if (NoLogo) { return; } Console.WriteLine(format, args); } @@ -48,6 +50,7 @@ public static void Usage(bool doExit = false) { /productname: Sets the product name attribute for the generated assembly /productversion: Sets the product version attribute for the generated assembly /py: