Browse Source

Improved error reporting with the PythonCompilerTask. STAThread attribute added to main method when compiling python code to executable.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3721 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Matt Ward 17 years ago
parent
commit
652a2288d8
  1. 3
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Project/Src/PythonCompiler.cs
  2. 47
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Project/Src/PythonCompilerTask.cs
  3. 9
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/CompileSingleSourceFileTestFixture.cs
  4. 67
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/DummyPythonCompilerTask.cs
  5. 67
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/IOErrorTestFixture.cs
  6. 14
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/MockPythonCompiler.cs
  7. 8
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/Python.Build.Tasks.Tests.csproj
  8. 108
      src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/SyntaxErrorTestFixture.cs

3
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Project/Src/PythonCompiler.cs

@ -129,6 +129,9 @@ namespace ICSharpCode.Python.Build.Tasks @@ -129,6 +129,9 @@ namespace ICSharpCode.Python.Build.Tasks
TypeBuilder typeBuilder = moduleBuilder.DefineType("PythonMain", TypeAttributes.Public);
MethodBuilder mainMethod = typeBuilder.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[0]);
// Mark main method as STA.
mainMethod.SetCustomAttribute(typeof(STAThreadAttribute).GetConstructor(Type.EmptyTypes), new byte[0]);
ILGenerator generator = mainMethod.GetILGenerator();
generator.Emit(OpCodes.Ldstr, Path.GetFileName(outputAssemblyDll));
generator.EmitCall(OpCodes.Call, typeof(Path).GetMethod("GetFullPath", new Type[] {typeof(String)}, new ParameterModifier[0]), null);

47
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Project/Src/PythonCompilerTask.cs

@ -13,6 +13,7 @@ using System.Reflection.Emit; @@ -13,6 +13,7 @@ using System.Reflection.Emit;
using IronPython.Hosting;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Scripting;
namespace ICSharpCode.Python.Build.Tasks
{
@ -127,9 +128,16 @@ namespace ICSharpCode.Python.Build.Tasks @@ -127,9 +128,16 @@ namespace ICSharpCode.Python.Build.Tasks
compiler.IncludeDebugInformation = emitDebugInformation;
// Compile the code.
compiler.Compile();
try {
compiler.Compile();
return true;
} catch (SyntaxErrorException ex) {
LogSyntaxError(ex);
} catch (IOException ex) {
LogError(ex.Message);
}
}
return true;
return false;
}
/// <summary>
@ -140,6 +148,41 @@ namespace ICSharpCode.Python.Build.Tasks @@ -140,6 +148,41 @@ namespace ICSharpCode.Python.Build.Tasks
return Directory.GetCurrentDirectory();
}
/// <summary>
/// Logs any error message that occurs during compilation. Default implementation
/// is to use the MSBuild task's base.Log.LogError(...)
/// </summary>
protected virtual void LogError(string message, string errorCode, string file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber)
{
Log.LogError(null, errorCode, null, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message);
}
void LogError(string message)
{
LogError(message, null, null, 0, 0, 0, 0);
}
void LogSyntaxError(SyntaxErrorException ex)
{
string fileName = GetFileName(ex, sources);
LogError(ex.Message, ex.ErrorCode.ToString(), fileName, ex.Line, ex.Column, ex.RawSpan.End.Line, ex.RawSpan.End.Column);
}
/// <summary>
/// Matches the syntax exception SourcePath against filenames being compiled. The
/// syntax exception only contains the file name without its path and without its file extension.
/// </summary>
static string GetFileName(SyntaxErrorException ex, ITaskItem[] sources)
{
foreach (ITaskItem item in sources) {
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item.ItemSpec);
if (fileNameWithoutExtension == ex.SourcePath) {
return item.ItemSpec;
}
}
return null;
}
/// <summary>
/// Maps from the target type string to the PEFileKind
/// needed by the compiler.

9
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/CompileSingleSourceFileTestFixture.cs

@ -25,6 +25,7 @@ namespace Python.Build.Tasks.Tests @@ -25,6 +25,7 @@ namespace Python.Build.Tasks.Tests
TaskItem systemXmlReferenceTaskItem;
TaskItem systemDataReferenceTaskItem;
PythonCompilerTask compiler;
bool success;
[SetUp]
public void Init()
@ -40,7 +41,13 @@ namespace Python.Build.Tasks.Tests @@ -40,7 +41,13 @@ namespace Python.Build.Tasks.Tests
systemXmlReferenceTaskItem = new TaskItem(@"C:\Windows\Microsoft.NET\Framework\2.0\System.Xml.dll");
compiler.References = new ITaskItem[] {systemDataReferenceTaskItem, systemXmlReferenceTaskItem};
compiler.Execute();
success = compiler.Execute();
}
[Test]
public void CompilationSucceeded()
{
Assert.IsTrue(success);
}
[Test]

67
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/DummyPythonCompilerTask.cs

@ -16,16 +16,83 @@ namespace Python.Build.Tasks.Tests @@ -16,16 +16,83 @@ namespace Python.Build.Tasks.Tests
public class DummyPythonCompilerTask : PythonCompilerTask
{
string currentFolder;
string loggedErrorMessage;
string loggedErrorCode;
string loggedErrorFile;
int loggedStartColumn = -1;
int loggedStartLine = -1;
int loggedEndLine = -1;
int loggedEndColumn = -1;
public DummyPythonCompilerTask(IPythonCompiler compiler, string currentFolder)
: base(compiler)
{
this.currentFolder = currentFolder;
}
/// <summary>
/// Gets the error message passed to the LogError method.
/// </summary>
public string LoggedErrorMessage {
get { return loggedErrorMessage; }
}
/// <summary>
/// Gets the error code passed to the LogError method.
/// </summary>
public string LoggedErrorCode {
get { return loggedErrorCode; }
}
/// <summary>
/// Gets the file passed to the LogError method.
/// </summary>
public string LoggedErrorFile {
get { return loggedErrorFile; }
}
/// <summary>
/// Gets the start line passed to the LogError method.
/// </summary>
public int LoggedStartLine {
get { return loggedStartLine; }
}
/// <summary>
/// Gets the end line passed to the LogError method.
/// </summary>
public int LoggedEndLine {
get { return loggedEndLine; }
}
/// <summary>
/// Gets the start column passed to the LogError method.
/// </summary>
public int LoggedStartColumn {
get { return loggedStartColumn; }
}
/// <summary>
/// Gets the end column passed to the LogError method.
/// </summary>
public int LoggedEndColumn {
get { return loggedEndColumn; }
}
protected override string GetCurrentFolder()
{
return currentFolder;
}
protected override void LogError(string message, string errorCode, string file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber)
{
loggedErrorMessage = message;
loggedErrorCode = errorCode;
loggedErrorFile = file;
loggedStartColumn = columnNumber;
loggedStartLine = lineNumber;
loggedEndColumn = endColumnNumber;
loggedEndLine = endLineNumber;
}
}
}

67
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/IOErrorTestFixture.cs

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Reflection;
using System.Reflection.Emit;
using IronPython.Runtime;
using IronPython.Runtime.Operations;
using ICSharpCode.Python.Build.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Runtime;
using NUnit.Framework;
namespace Python.Build.Tasks.Tests
{
/// <summary>
/// Tests that an IOException is caught and logged.
/// </summary>
[TestFixture]
public class IOErrorTestFixture
{
MockPythonCompiler mockCompiler;
DummyPythonCompilerTask compiler;
bool success;
[SetUp]
public void Init()
{
mockCompiler = new MockPythonCompiler();
compiler = new DummyPythonCompilerTask(mockCompiler, @"C:\Projects\MyProject");
compiler.TargetType = "Exe";
compiler.OutputAssembly = "test.exe";
TaskItem sourceFile = new TaskItem(@"D:\Projects\MyProject\test.py");
compiler.Sources = new ITaskItem[] {sourceFile};
mockCompiler.ThrowExceptionAtCompile = PythonOps.IOError("Could not find main file test.py");;
success = compiler.Execute();
}
[Test]
public void ExecuteFailed()
{
Assert.IsFalse(success);
}
[Test]
public void IsExceptionMessageLogged()
{
Assert.AreEqual("Could not find main file test.py", compiler.LoggedErrorMessage);
}
// [Test]
// public void IsErrorCodeLogged()
// {
// Assert.AreEqual("1000", compiler.LoggedErrorCode);
// }
}
}

14
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/MockPythonCompiler.cs

@ -31,7 +31,7 @@ namespace Python.Build.Tasks.Tests @@ -31,7 +31,7 @@ namespace Python.Build.Tasks.Tests
bool includeDebugInformation;
IList<string> referencedAssemblies;
IList<ResourceFile> resourceFiles;
Exception throwExceptionAtCompile;
public MockPythonCompiler()
{
@ -61,12 +61,24 @@ namespace Python.Build.Tasks.Tests @@ -61,12 +61,24 @@ namespace Python.Build.Tasks.Tests
set { resourceFiles = value; }
}
/// <summary>
/// Gets or sets the exception that will be thrown when the Compile method is called.
/// </summary>
public Exception ThrowExceptionAtCompile {
get { return throwExceptionAtCompile; }
set { throwExceptionAtCompile = value; }
}
/// <summary>
/// Compiles the source code.
/// </summary>
public void Compile()
{
compileCalled = true;
if (throwExceptionAtCompile != null) {
throw throwExceptionAtCompile;
}
}
/// <summary>

8
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/Python.Build.Tasks.Tests.csproj

@ -47,6 +47,12 @@ @@ -47,6 +47,12 @@
<Reference Include="Microsoft.Build.Utilities.v3.5">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="Microsoft.Scripting">
<HintPath>..\..\RequiredLibraries\Microsoft.Scripting.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Scripting.Core">
<HintPath>..\..\RequiredLibraries\Microsoft.Scripting.Core.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\..\..\..\..\Tools\NUnit\nunit.framework.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
@ -60,12 +66,14 @@ @@ -60,12 +66,14 @@
<Compile Include="DifferentTargetTypesTestFixture.cs" />
<Compile Include="DummyPythonCompilerTask.cs" />
<Compile Include="IncludeDebugInfoTestFixture.cs" />
<Compile Include="IOErrorTestFixture.cs" />
<Compile Include="MainEntryPointTestFixture.cs" />
<Compile Include="MockPythonCompiler.cs" />
<Compile Include="CompileSingleSourceFileTestFixture.cs" />
<Compile Include="PlatformTestFixture.cs" />
<Compile Include="RelativeReferenceTestFixture.cs" />
<Compile Include="RelativeResourceFileTestFixture.cs" />
<Compile Include="SyntaxErrorTestFixture.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Project\Python.Build.Tasks.csproj">

108
src/AddIns/BackendBindings/Python/Python.Build.Tasks/Test/SyntaxErrorTestFixture.cs

@ -0,0 +1,108 @@ @@ -0,0 +1,108 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Reflection;
using System.Reflection.Emit;
using IronPython.Runtime;
using ICSharpCode.Python.Build.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Runtime;
using NUnit.Framework;
namespace Python.Build.Tasks.Tests
{
/// <summary>
/// Tests that a syntax error exception is caught and logged.
/// </summary>
[TestFixture]
public class SyntaxErrorTestFixture
{
MockPythonCompiler mockCompiler;
DummyPythonCompilerTask compiler;
bool success;
[TestFixtureSetUp]
public void SetUpFixture()
{
ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
}
[SetUp]
public void Init()
{
mockCompiler = new MockPythonCompiler();
compiler = new DummyPythonCompilerTask(mockCompiler, @"C:\Projects\MyProject");
compiler.TargetType = "Exe";
compiler.OutputAssembly = "test.exe";
TaskItem sourceFile = new TaskItem(@"D:\Projects\MyProject\test.py");
compiler.Sources = new ITaskItem[] {sourceFile};
SourceUnit source = DefaultContext.DefaultPythonContext.CreateSourceUnit(NullTextContentProvider.Null, @"test", SourceCodeKind.InteractiveCode);
SourceLocation start = new SourceLocation(0, 1, 1);
SourceLocation end = new SourceLocation(0, 2, 3);
SourceSpan span = new SourceSpan(start, end);
SyntaxErrorException ex = new SyntaxErrorException("Error", source, span, 1000, Severity.FatalError);
mockCompiler.ThrowExceptionAtCompile = ex;
success = compiler.Execute();
}
[Test]
public void ExecuteFailed()
{
Assert.IsFalse(success);
}
[Test]
public void IsExceptionMessageLogged()
{
Assert.AreEqual("Error", compiler.LoggedErrorMessage);
}
[Test]
public void IsErrorCodeLogged()
{
Assert.AreEqual("1000", compiler.LoggedErrorCode);
}
[Test]
public void SourceFile()
{
Assert.AreEqual(@"D:\Projects\MyProject\test.py", compiler.LoggedErrorFile);
}
[Test]
public void SourceStartLine()
{
Assert.AreEqual(1, compiler.LoggedStartLine);
}
[Test]
public void SourceStartColumn()
{
Assert.AreEqual(1, compiler.LoggedStartColumn);
}
[Test]
public void SourceEndLine()
{
Assert.AreEqual(2, compiler.LoggedEndLine);
}
[Test]
public void SourceEndColumn()
{
Assert.AreEqual(3, compiler.LoggedEndColumn);
}
}
}
Loading…
Cancel
Save