Skip to content
This repository has been archived by the owner on Sep 14, 2018. It is now read-only.

Commit

Permalink
Fixes #469 (#1528)
Browse files Browse the repository at this point in the history
* Start addition of /py options

* The engine doesn't need the options...

* Fixes #469

* Updates based on feedback
  • Loading branch information
slide committed Nov 4, 2016
1 parent 76151d3 commit e6d5bd9
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 13 deletions.
11 changes: 9 additions & 2 deletions Languages/IronPython/IronPython/Runtime/Operations/PythonOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3778,7 +3778,7 @@ public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyB
/// 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);
return InitializeModuleEx(precompiled, main, references, false, null);
}

/// <summary>
Expand All @@ -3787,10 +3787,17 @@ public static int InitializeModule(Assembly/*!*/ precompiled, string/*!*/ main,
/// the exit code that the program reported via SystemExit or 0.
/// </summary>
public static int InitializeModuleEx(Assembly/*!*/ precompiled, string/*!*/ main, string[] references, bool ignoreEnvVars) {
return InitializeModuleEx(precompiled, main, references, ignoreEnvVars, null);
}


public static int InitializeModuleEx(Assembly/*!*/ precompiled, string/*!*/ main, string[] references, bool ignoreEnvVars, Dictionary<string, object> options) {
ContractUtils.RequiresNotNull(precompiled, "precompiled");
ContractUtils.RequiresNotNull(main, "main");

Dictionary<string, object> options = new Dictionary<string, object>();
if(options == null) {
options = new Dictionary<string, object>();
}
options["Arguments"] = Environment.GetCommandLineArgs();

var pythonEngine = Python.CreateEngine(options);
Expand Down
66 changes: 63 additions & 3 deletions Languages/IronPython/IronPythonCompiler/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
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.Reflection;

using Microsoft.Scripting.Runtime;

namespace IronPythonCompiler {
public class Config {
Expand All @@ -25,6 +26,7 @@ public Config() {
Win32Icon = string.Empty;
Version = string.Empty;
ErrorMessageFormat = "Error occured: {0}";
PythonOptions = new Dictionary<string, object>();
}

public string ErrorMessageFormat {
Expand Down Expand Up @@ -77,6 +79,11 @@ public List<string> Files {
private set;
}

public IDictionary<string, object> PythonOptions {
get;
private set;
}

public bool Embed {
get;
internal set;
Expand Down Expand Up @@ -137,10 +144,10 @@ public void ParseArgs(IEnumerable<string> args, List<string> respFiles = null) {
break;
}
} else if (arg.StartsWith("/win32icon:")) {
Win32Icon = arg.Substring(11).Trim('"');
Win32Icon = arg.Substring(11).Trim('"');
} else if (arg.StartsWith("/version:")) {
Version = arg.Substring(9).Trim('"');
} else if(arg.StartsWith("/errfmt:")) {
} else if (arg.StartsWith("/errfmt:")) {
ErrorMessageFormat = arg.Substring(8);
} else if (arg.StartsWith("/embed")) {
Embed = true;
Expand All @@ -150,6 +157,52 @@ public void ParseArgs(IEnumerable<string> args, List<string> respFiles = null) {
UseMta = true;
} else if (Array.IndexOf(new string[] { "/?", "-?", "/h", "-h" }, args) >= 0) {
ConsoleOps.Usage(true);
} else if(arg.StartsWith("/py:")) {
// if you add a parameter that takes a different type then
// ScriptingRuntimeHelpers.True/False or int
// you need ot also modify Program.cs for standalone generation.
string[] pyargs = arg.Substring(4).Trim('"').Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
switch(pyargs[0]) {
case "-X:Frames":
PythonOptions["Frames"] = ScriptingRuntimeHelpers.True;
break;
case "-X:FullFrames":
PythonOptions["Frames"] = PythonOptions["FullFrames"] = ScriptingRuntimeHelpers.True;
break;
case "-X:Tracing":
PythonOptions["Tracing"] = ScriptingRuntimeHelpers.True;
break;
case "-X:GCStress":
int gcStress;
if (!int.TryParse(pyargs[1], out gcStress) || (gcStress < 0 || gcStress > GC.MaxGeneration)) {
ConsoleOps.Error(true, "The argument for the {0} option must be between 0 and {1}.", pyargs[1], GC.MaxGeneration);
}

PythonOptions["GCStress"] = gcStress;
break;

case "-X:MaxRecursion":
// we need about 6 frames for starting up, so 10 is a nice round number.
int limit;
if (!int.TryParse(pyargs[1], out limit) || limit < 10) {
ConsoleOps.Error(true, "The argument for the {0} option must be an integer >= 10.", pyargs[1]);
}

PythonOptions["RecursionLimit"] = limit;
break;

case "-X:EnableProfiler":
PythonOptions["EnableProfiler"] = ScriptingRuntimeHelpers.True;
break;

case "-X:LightweightScopes":
PythonOptions["LightweightScopes"] = ScriptingRuntimeHelpers.True;
break;

case "-X:Debug":
PythonOptions["Debug"] = ScriptingRuntimeHelpers.True;
break;
}
} else {
if (arg.StartsWith("@")) {
var respFile = Path.GetFullPath(arg.Substring(1));
Expand Down Expand Up @@ -220,6 +273,13 @@ public override string ToString() {
}
}

if (PythonOptions.Count > 0) {
res.AppendFormat("\nIronPython Context Options:\n");
foreach(var option in PythonOptions) {
res.AppendFormat("\t{0} = {1}\n", option.Key, option.Value);
}
}

return res.ToString();
}
}
Expand Down
1 change: 1 addition & 0 deletions Languages/IronPython/IronPythonCompiler/ConsoleOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static void Usage(bool doExit = false) {
/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.
/py:<option> Allows specifying options that modify the behavior of IronPython (e.g., -X:FullFrames)
@<file> Specifies a response file to be parsed for input files and command line options (one per line)
/? /h This message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Runtime\Microsoft.Dynamic\Microsoft.Dynamic.csproj">
<Project>{EB66B766-6354-4208-A3D4-AACBDCB5C3B3}</Project>
<Name>Microsoft.Dynamic</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Runtime\Microsoft.Scripting\Microsoft.Scripting.csproj">
<Project>{02ff0909-f5ad-48cf-a86a-345e721b7e40}</Project>
<Name>Microsoft.Scripting</Name>
Expand Down
56 changes: 48 additions & 8 deletions Languages/IronPython/IronPythonCompiler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,34 @@ static void GenerateExe(Config config) {
// 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)));
LocalBuilder dictVar = null;

if (config.PythonOptions.Count > 0) {
var True = u.Import(typeof(ScriptingRuntimeHelpers)).GetField("True");
var False = u.Import(typeof(ScriptingRuntimeHelpers)).GetField("False");

dictVar = gen.DeclareLocal(u.Import(typeof(Dictionary<string, object>)));
gen.Emit(OpCodes.Newobj, u.Import(typeof(Dictionary<string, object>)).GetConstructor(Type.EmptyTypes));
gen.Emit(OpCodes.Stloc, dictVar);

foreach (var option in config.PythonOptions) {
gen.Emit(OpCodes.Ldloc, dictVar);
gen.Emit(OpCodes.Ldstr, option.Key);
if (option.Value is int) {
int val = (int)option.Value;
if (val >= -128 && val <= 127)
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)));
} 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<string, object>)).GetMethod("Add", new IKVM.Reflection.Type[] { u.Import(typeof(string)), u.Import(typeof(object)) }), Type.EmptyTypes);
}
}

Label tryStart = gen.BeginExceptionBlock();

Expand Down Expand Up @@ -178,13 +206,19 @@ static void GenerateExe(Config config) {
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
if(config.PythonOptions.Count > 0) {
gen.Emit(OpCodes.Ldloc, dictVar);
} else {
gen.Emit(OpCodes.Ldnull);
}

// call InitializeModule
// 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("InitializeModuleEx"), Type.EmptyTypes);
gen.EmitCall(OpCodes.Call, u.Import(typeof(PythonOps)).GetMethod("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<string, object>)) }),
Type.EmptyTypes);
gen.Emit(OpCodes.Stloc, intVar);

gen.BeginCatchBlock(u.Import(typeof(Exception)));

if (config.Target == PEFileKinds.ConsoleApplication) {
Expand All @@ -194,7 +228,6 @@ static void GenerateExe(Config config) {
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);
} else {
// what do we want to do in the case of a Windows app, show a MessageBox?
gen.EmitCall(OpCodes.Callvirt, u.Import(typeof(System.Exception)).GetMethod("get_Message", Type.EmptyTypes), Type.EmptyTypes);
gen.Emit(OpCodes.Stloc, strVar);
gen.Emit(OpCodes.Ldstr, config.ErrorMessageFormat);
Expand Down Expand Up @@ -230,13 +263,20 @@ static int Main(string[] args) {
}

// we don't use the engine, but we create it so we can have a default context.
ScriptEngine engine = Python.CreateEngine();
ScriptEngine engine = Python.CreateEngine(config.PythonOptions);

ConsoleOps.Info("IronPython Compiler for {0} ({1})", engine.Setup.DisplayName, engine.LanguageVersion);
ConsoleOps.Info("{0}", config);
ConsoleOps.Info("compiling...");

ClrModule.CompileModules(DefaultContext.DefaultCLS, config.Output + ".dll", new Dictionary<string, object> { { "mainModule", config.MainName } }, config.Files.ToArray());
ConsoleOps.Info("compiling...");

var compileOptions = new Dictionary<string, object>() {
{ "mainModule", config.MainName }
};

ClrModule.CompileModules(DefaultContext.DefaultCLS,
Path.ChangeExtension(config.Output, ".dll"),
compileOptions,
config.Files.ToArray());

if (config.Target != PEFileKinds.Dll) {
GenerateExe(config);
Expand Down

0 comments on commit e6d5bd9

Please sign in to comment.