Skip to content

Commit 0227cda

Browse files
authored
[generator] Gracefully handle BindingGeneratorException. (#845)
@moljac pointed out that if you have invalid XPath in your metadata, we simply error with the unhelpful: "generator.exe" exited with code -XXXXXX ![before](https://user-images.githubusercontent.com/179295/119032575-f4891680-b971-11eb-9580-96cc2e96e4aa.png) If you turn on `Diagnostic` logs, you can see the uncaught exception hiding in a lot of ceremony: message BG0000: Unhandled Exception: Java.Interop.Tools.Generator.BindingGeneratorException: C:\code\temp\xamarin.exoplayer\Xamarin.ExoPlayer.Core\Transforms\Metadata.xml(11, 4): error BG4304: Invalid XPath specification: /api/package[@name='com.google.android.exoplayer2']interface[@name='ExoPlayer']/method[@name='addMediaItems' and count(parameter)=2 and parameter[1][@type='int'] and parameter[2][@type='java.util.List<com.google.android.exoplayer2.source.MediaSource>']]/parameter[2]. ---> System.Xml.XPath.XPathException: '/api/package[@name='com.google.android.exoplayer2']interface[@name='ExoPlayer']/method[@name='addMediaItems' and count(parameter)=2 and parameter[1][@type='int'] and parameter[2][@type='java.util.List<com.google.android.exoplayer2.source.MediaSource>']]/parameter[2]' has an invalid token. message BG0000: at MS.Internal.Xml.XPath.XPathParser.ParseXPathExpresion(String xpathExpresion) message BG0000: at System.Xml.XPath.XPathExpression.Compile(String xpath, IXmlNamespaceResolver nsResolver) message BG0000: at System.Xml.XPath.XPathNavigator.Evaluate(String xpath, IXmlNamespaceResolver resolver) message BG0000: at System.Xml.XPath.XPathEvaluator.Evaluate[T](XNode node, String expression, IXmlNamespaceResolver resolver) message BG0000: at System.Xml.XPath.Extensions.XPathSelectElements(XNode node, String expression, IXmlNamespaceResolver resolver) message BG0000: at Java.Interop.Tools.Generator.FixupXmlDocument.Apply(ApiXmlDocument apiDocument, String apiLevelString, Int32 productVersion) message BG0000: --- End of inner exception stack trace --- (TaskId:46) message BG0000: at Java.Interop.Tools.Generator.Report.LogCodedError(LocalizedMessage message, Exception innerException, String sourceFile, Int32 line, Int32 column, String[] args) message BG0000: at Java.Interop.Tools.Generator.Report.LogCodedError(LocalizedMessage message, Exception innerException, XNode node, String[] args) message BG0000: at Java.Interop.Tools.Generator.FixupXmlDocument.Apply(ApiXmlDocument apiDocument, String apiLevelString, Int32 productVersion) message BG0000: at Java.Interop.Tools.Generator.ApiXmlDocument.ApplyFixupFile(FixupXmlDocument fixup) message BG0000: at Xamarin.Android.Binder.CodeGenerator.Run(CodeGeneratorOptions options, DirectoryAssemblyResolver resolver) message BG0000: at Xamarin.Android.Binder.CodeGenerator.Run(CodeGeneratorOptions options) message BG0000: at Xamarin.Android.Binder.CodeGenerator.Main(String[] args) error MSB6006: "generator.exe" exited with code -532462766. It turns out this happens anywhere we throw a `BindingGeneratorException`, as there is no `catch` handler that attempts to gracefully handles this exception. This led to making several improvements: - Gracefully handle `BindingGeneratorException` such that we print out the error as a standard MSBuild error that can be processed by Visual Studio. - Exit `generator` with `-1` if `BindingGeneratorException` is thrown. - Add a separate `Report.LogCodedError(…)` method called `Report.LogCodeErrorAndExit(…)`, which is used for terminal errors ("don't do any further processing and exit"). - Audit existing calls to `Report.LogCodedError()` to allow errors such as `Invalid XPath` to be non-terminal. (Today `generator` will exit after finding the first one.) - Remove spaces in the error file location: `metadata.xml(8, 4): ` becomes `metadata.xml(8,4):`. These were not being processed correctly and could not be double clicked in VS previously. The result is a more useful error message: Metadata.xml(11,4): error BG4304: Invalid XPath specification … We also now handle any unhandled exception a little better. Additionally, `ApiFixup.cs` was deleted as it was no longer used after the refactor in commit f4e68b5.
1 parent ce1750f commit 0227cda

File tree

9 files changed

+66
-236
lines changed

9 files changed

+66
-236
lines changed

src/Java.Interop.Tools.Generator/Metadata/FixupXmlDocument.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
5858
else
5959
// BG8A00
6060
Report.LogCodedWarning (0, Report.WarningRemoveNodeMatchedNoNodes, null, metaitem, $"<remove-node path=\"{path}\" />");
61-
} catch (XPathException e) {
61+
} catch (XPathException) {
6262
// BG4301
63-
Report.LogCodedError (Report.ErrorRemoveNodeInvalidXPath, e, metaitem, path);
63+
Report.LogCodedError (Report.ErrorRemoveNodeInvalidXPath, metaitem, path);
6464
}
6565
break;
6666
case "add-node":
@@ -74,9 +74,9 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
7474
foreach (var node in nodes)
7575
node.Add (metaitem.Nodes ());
7676
}
77-
} catch (XPathException e) {
77+
} catch (XPathException) {
7878
// BG4302
79-
Report.LogCodedError (Report.ErrorAddNodeInvalidXPath, e, metaitem, path);
79+
Report.LogCodedError (Report.ErrorAddNodeInvalidXPath, metaitem, path);
8080
}
8181
break;
8282
case "change-node":
@@ -95,9 +95,9 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
9595
if (!matched)
9696
// BG8A03
9797
Report.LogCodedWarning (0, Report.WarningChangeNodeTypeMatchedNoNodes, null, metaitem, $"<change-node-type path=\"{path}\" />");
98-
} catch (XPathException e) {
98+
} catch (XPathException) {
9999
// BG4303
100-
Report.LogCodedError (Report.ErrorChangeNodeInvalidXPath, e, metaitem, path);
100+
Report.LogCodedError (Report.ErrorChangeNodeInvalidXPath, metaitem, path);
101101
}
102102
break;
103103
case "attr":
@@ -106,7 +106,7 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
106106

107107
if (string.IsNullOrEmpty (attr_name))
108108
// BG4307
109-
Report.LogCodedError (Report.ErrorMissingAttrName, null, metaitem, path);
109+
Report.LogCodedError (Report.ErrorMissingAttrName, metaitem, path);
110110
var nodes = attr_last_cache != null ? new XElement [] { attr_last_cache } : apiDocument.ApiDocument.XPathSelectElements (path);
111111
var attr_matched = 0;
112112

@@ -119,9 +119,9 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
119119
Report.LogCodedWarning (0, Report.WarningAttrMatchedNoNodes, null, metaitem, $"<attr path=\"{path}\" />");
120120
if (attr_matched != 1)
121121
attr_last_cache = null;
122-
} catch (XPathException e) {
122+
} catch (XPathException) {
123123
// BG4304
124-
Report.LogCodedError (Report.ErrorAttrInvalidXPath, e, metaitem, path);
124+
Report.LogCodedError (Report.ErrorAttrInvalidXPath, metaitem, path);
125125
}
126126
break;
127127
case "move-node":
@@ -140,9 +140,9 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
140140
if (!matched)
141141
// BG8A05
142142
Report.LogCodedWarning (0, Report.WarningMoveNodeMatchedNoNodes, null, metaitem, $"<move-node path=\"{path}\" />");
143-
} catch (XPathException e) {
143+
} catch (XPathException) {
144144
// BG4305
145-
Report.LogCodedError (Report.ErrorMoveNodeInvalidXPath, e, metaitem, path);
145+
Report.LogCodedError (Report.ErrorMoveNodeInvalidXPath, metaitem, path);
146146
}
147147
break;
148148
case "remove-attr":
@@ -159,9 +159,9 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
159159
if (!matched)
160160
// BG8A06
161161
Report.LogCodedWarning (0, Report.WarningRemoveAttrMatchedNoNodes, null, metaitem, $"<remove-attr path=\"{path}\" />");
162-
} catch (XPathException e) {
162+
} catch (XPathException) {
163163
// BG4306
164-
Report.LogCodedError (Report.ErrorRemoveAttrInvalidXPath, e, metaitem, path);
164+
Report.LogCodedError (Report.ErrorRemoveAttrInvalidXPath, metaitem, path);
165165
}
166166
break;
167167
}

src/Java.Interop.Tools.Generator/Utilities/ApiXmlDocument.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void ApplyFixupFile (FixupXmlDocument fixup)
4040
fixup.Apply (this, ApiLevel, ProductVersion);
4141
} catch (XmlException ex) {
4242
// BG4200
43-
Report.LogCodedError (Report.ErrorFailedToProcessMetadata, ex.Message);
43+
Report.LogCodedErrorAndExit (Report.ErrorFailedToProcessMetadata, null, fixup.FixupDocument, ex.Message);
4444
}
4545
}
4646
}

src/Java.Interop.Tools.Generator/Utilities/Report.cs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,30 @@ public LocalizedMessage (int code, string value)
6969
public static LocalizedMessage WarningBaseInterfaceNotFound => new LocalizedMessage (0x8C00, Localization.Resources.Generator_BG8C00);
7070
public static LocalizedMessage WarningBaseInterfaceInvalid => new LocalizedMessage (0x8C01, Localization.Resources.Generator_BG8C01);
7171

72-
public static void LogCodedError (LocalizedMessage message, params string [] args)
73-
=> LogCodedError (message, null, null, -1, -1, args);
72+
public static void LogCodedErrorAndExit (LocalizedMessage message, params string [] args)
73+
=> LogCodedErrorAndExit (message, null, null, args);
7474

75-
public static void LogCodedError (LocalizedMessage message, Exception? innerException, params string [] args)
76-
=> LogCodedError (message, innerException, null, -1, -1, args);
75+
public static void LogCodedErrorAndExit (LocalizedMessage message, Exception? innerException, params string [] args)
76+
=> LogCodedErrorAndExit (message, innerException, null, args);
7777

78-
public static void LogCodedError (LocalizedMessage message, Exception? innerException, XNode node, params string? [] args)
78+
public static void LogCodedErrorAndExit (LocalizedMessage message, Exception? innerException, XNode? node, params string? [] args)
7979
{
80-
var file = Uri.TryCreate (node.BaseUri, UriKind.Absolute, out var uri) ? uri.LocalPath : null;
81-
var line_info = (node as IXmlLineInfo)?.HasLineInfo () == true ? node as IXmlLineInfo : null;
80+
LogCodedError (message, node, args);
8281

83-
LogCodedError (message, innerException, file, line_info?.LineNumber ?? -1, line_info?.LinePosition ?? -1, args);
82+
// Throwing a BindingGeneratorException will cause generator to terminate
83+
throw new BindingGeneratorException (message.Code, string.Format (message.Value, args), innerException);
8484
}
8585

86-
public static void LogCodedError (LocalizedMessage message, Exception? innerException, string? sourceFile, int line, int column, params string? [] args)
86+
public static void LogCodedError (LocalizedMessage message, XNode? node, params string? [] args)
8787
{
88-
throw new BindingGeneratorException (message.Code, sourceFile, line, column, string.Format (message.Value, args), innerException);
88+
var (file, line, col) = GetLineInfo (node);
89+
90+
LogCodedError (message, file, line, col, args);
91+
}
92+
93+
public static void LogCodedError (LocalizedMessage message, string? sourceFile, int line, int column, params string? [] args)
94+
{
95+
Console.Error.WriteLine (Format (true, message.Code, sourceFile, line, column, message.Value, args));
8996
}
9097

9198
public static void LogCodedWarning (int verbosity, LocalizedMessage message, params string? [] args)
@@ -99,10 +106,9 @@ public static void LogCodedWarning (int verbosity, LocalizedMessage message, Exc
99106

100107
public static void LogCodedWarning (int verbosity, LocalizedMessage message, Exception? innerException, XNode node, params string? [] args)
101108
{
102-
var file = Uri.TryCreate (node.BaseUri, UriKind.Absolute, out var uri) ? uri.LocalPath : null;
103-
var line_info = (node as IXmlLineInfo)?.HasLineInfo () == true ? node as IXmlLineInfo : null;
109+
var (file, line, col) = GetLineInfo (node);
104110

105-
LogCodedWarning (verbosity, message, innerException, file, line_info?.LineNumber ?? -1, line_info?.LinePosition ?? -1, args);
111+
LogCodedWarning (verbosity, message, innerException, file, line, col, args);
106112
}
107113

108114
public static void LogCodedWarning (int verbosity, LocalizedMessage message, Exception? innerException, string? sourceFile, int line, int column, params string? [] args)
@@ -145,12 +151,26 @@ public static string Format (bool error, int errorCode, string? sourceFile, int
145151
return ret + ": ";
146152

147153
if (column > 0)
148-
return ret + $"({line}, {column}): ";
154+
return ret + $"({line},{column}): ";
149155

150156
return ret + $"({line}): ";
151157
}
158+
159+
static (string? file, int line, int col) GetLineInfo (XNode? node)
160+
{
161+
if (node is null)
162+
return (null, -1, -1);
163+
164+
var file = Uri.TryCreate (node.BaseUri, UriKind.Absolute, out var uri) ? uri.LocalPath : null;
165+
var pos = (node as IXmlLineInfo)?.HasLineInfo () == true ? node as IXmlLineInfo : null;
166+
167+
return (file, pos?.LineNumber ?? -1, pos?.LinePosition ?? -1);
168+
}
152169
}
153-
170+
171+
/// <summary>
172+
/// Throwing this exception will cause generator to exit gracefully.
173+
/// </summary>
154174
public class BindingGeneratorException : Exception
155175
{
156176
public BindingGeneratorException (int errorCode, string message)

tests/generator-Tests/Unit-Tests/ReportTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public void FormatTests ()
2424
Assert.AreEqual ("error BG0037: There was a bad error", Report.Format (true, code, null, 0, 0, msg, args));
2525
Assert.AreEqual (@"C:\code\test.cs: error BG0037: There was a bad error", Report.Format (true, code, sourcefile, 0, 0, msg, args));
2626
Assert.AreEqual (@"C:\code\test.cs(32): error BG0037: There was a bad error", Report.Format (true, code, sourcefile, line, 0, msg, args));
27-
Assert.AreEqual (@"C:\code\test.cs(32, 12): error BG0037: There was a bad error", Report.Format (true, code, sourcefile, line, col, msg, args));
28-
Assert.AreEqual (@"C:\code\test.cs(32, 12): warning BG0037: There was a bad error", Report.Format (false, code, sourcefile, line, col, msg, args));
27+
Assert.AreEqual (@"C:\code\test.cs(32,12): error BG0037: There was a bad error", Report.Format (true, code, sourcefile, line, col, msg, args));
28+
Assert.AreEqual (@"C:\code\test.cs(32,12): warning BG0037: There was a bad error", Report.Format (false, code, sourcefile, line, col, msg, args));
2929
}
3030
}
3131
}

tools/generator/CodeGenerationOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ public string GetOutputName (string type)
222222
if (type.StartsWith ("params "))
223223
return "params " + GetOutputName (type.Substring ("params ".Length));
224224
if (type.StartsWith ("global::"))
225-
Report.LogCodedError (Report.ErrorUnexpectedGlobal);
225+
Report.LogCodedErrorAndExit (Report.ErrorUnexpectedGlobal);
226226
if (!UseGlobal)
227227
return type;
228228

tools/generator/CodeGenerator.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,19 @@ public class CodeGenerator
2222
{
2323
public static int Main (string[] args)
2424
{
25-
var options = CodeGeneratorOptions.Parse (args);
26-
if (options == null)
25+
try {
26+
var options = CodeGeneratorOptions.Parse (args);
27+
if (options == null)
28+
return 1;
29+
30+
Run (options);
31+
} catch (BindingGeneratorException) {
2732
return 1;
33+
} catch (Exception ex) {
34+
Console.Error.WriteLine (Report.Format (true, 0, null, -1, -1, ex.ToString ()));
35+
return 1;
36+
}
2837

29-
Run (options);
3038
return 0;
3139
}
3240

0 commit comments

Comments
 (0)