Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[X] l10n of build errors (#10662)
Browse files Browse the repository at this point in the history
* [X] l10n of build errors

provide error code, error links, and allow l10n of XamlC exceptions.

- fixes AB#826363
- fixes AB#1030395
- closes #7353
- closes #8505

* run xliff on CI

* - local nuget file

* Update DevopsNuget.config

Co-authored-by: shane <[email protected]>
  • Loading branch information
StephaneDelcroix and PureWeen authored Jun 5, 2020
1 parent b804d00 commit 2745dc5
Show file tree
Hide file tree
Showing 71 changed files with 3,382 additions and 162 deletions.
12 changes: 12 additions & 0 deletions DevopsNuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear/>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" protocolVersion="3" />
<add key="nightly" value="https://aka.ms/xf-ci/index.json" />
</packageSources>
<activePackageSource>
<add key="All" value="(Aggregate source)" />
</activePackageSource>
</configuration>
7 changes: 7 additions & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ static class BindablePropertyReferenceExtensions
public static TypeReference GetBindablePropertyType(this FieldReference bpRef, IXmlLineInfo iXmlLineInfo, ModuleDefinition module)
{
if (!bpRef.Name.EndsWith("Property", StringComparison.InvariantCulture))
throw new XamlParseException($"The name of the bindable property {bpRef.Name} does not ends with \"Property\". This is the kind of convention the world is build upon, a bit like Planck's constant.", iXmlLineInfo);
throw new BuildException(BuildExceptionCode.BPName, iXmlLineInfo, null, bpRef.Name);
var bpName = bpRef.Name.Substring(0, bpRef.Name.Length - 8);
var owner = bpRef.DeclaringType;
TypeReference declaringTypeRef = null;
Expand All @@ -28,7 +28,8 @@ public static TypeReference GetBindablePropertyType(this FieldReference bpRef, I
md.Parameters.Count == 1 &&
md.Parameters[0].ParameterType.InheritsFromOrImplements(module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject"))), module).SingleOrDefault()?.Item1;
if (getter == null)
throw new XamlParseException($"Missing a public static Get{bpName} or a public instance property getter for the attached property \"{bpRef.DeclaringType}.{bpRef.Name}\"", iXmlLineInfo);
throw new BuildException(BuildExceptionCode.BPName, iXmlLineInfo, null, bpName, bpRef.DeclaringType);

return getter.ResolveGenericReturnType(declaringTypeRef, module);
}

Expand Down
107 changes: 107 additions & 0 deletions Xamarin.Forms.Build.Tasks/BuildException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Xml;
using System.Linq;

namespace Xamarin.Forms.Build.Tasks
{
[Serializable]
class BuildException : Exception
{
public BuildExceptionCode Code { get; private set; }

public IXmlLineInfo XmlInfo { get; private set; }

public string[] MessageArgs { get; private set; }

public override string HelpLink { get => Code.HelpLink; set => base.HelpLink = value; }

public BuildException(BuildExceptionCode code, IXmlLineInfo xmlInfo, Exception innerException, params object[] args)
: base(FormatMessage(code, xmlInfo, args), innerException)
{
Code = code;
XmlInfo = xmlInfo;
MessageArgs = args?.Select(a=>a?.ToString()).ToArray();
}

protected BuildException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{
}

static string FormatMessage(BuildExceptionCode code, IXmlLineInfo xmlinfo, object[] args)
{
var message = string.Format(ErrorMessages.ResourceManager.GetString(code.ErrorMessageKey), args);
var ecode = code.Code;
var position = xmlinfo == null || !xmlinfo.HasLineInfo() ? "" : $"({xmlinfo.LineNumber},{xmlinfo.LinePosition})";

return $"{position} : XamlC error {ecode} : {message}";
}
}

class BuildExceptionCode
{
//Assemblies, Types, Members
public static BuildExceptionCode TypeResolution = new BuildExceptionCode("XFC0000", nameof(TypeResolution), "");
public static BuildExceptionCode PropertyResolution = new BuildExceptionCode("XFC0001", nameof(PropertyResolution), "");
public static BuildExceptionCode MissingEventHandler = new BuildExceptionCode("XFC0002", nameof(MissingEventHandler), "");
public static BuildExceptionCode PropertyMissing = new BuildExceptionCode("XFC0003", nameof(PropertyMissing), "");
public static BuildExceptionCode ConstructorDefaultMissing = new BuildExceptionCode("XFC0004", nameof(ConstructorDefaultMissing), "");
public static BuildExceptionCode ConstructorXArgsMissing = new BuildExceptionCode("XFC0005", nameof(ConstructorXArgsMissing), "");
public static BuildExceptionCode MethodStaticMissing = new BuildExceptionCode("XFC0006", nameof(MethodStaticMissing), "");
public static BuildExceptionCode EnumValueMissing = new BuildExceptionCode("XFC0007", nameof(EnumValueMissing), "");
public static BuildExceptionCode AdderMissing = new BuildExceptionCode("XFC0008", nameof(AdderMissing), "");
public static BuildExceptionCode MemberResolution = new BuildExceptionCode("XFC0009", nameof(MemberResolution), "");


//BP,BO
public static BuildExceptionCode BPName = new BuildExceptionCode("XFC0020", nameof(BPName), "");
public static BuildExceptionCode BPMissingGetter = new BuildExceptionCode("XFC0021", nameof(BPMissingGetter), "");

//Bindings, conversions
public static BuildExceptionCode Conversion = new BuildExceptionCode("XFC0040", nameof(Conversion), "");
public static BuildExceptionCode BindingIndexerNotClosed = new BuildExceptionCode("XFC0041", nameof(BindingIndexerNotClosed), "");
public static BuildExceptionCode BindingIndexerEmpty = new BuildExceptionCode("XFC0042", nameof(BindingIndexerEmpty), "");
public static BuildExceptionCode BindingIndexerTypeUnsupported = new BuildExceptionCode("XFC0043", nameof(BindingIndexerTypeUnsupported), "");
public static BuildExceptionCode BindingIndexerParse = new BuildExceptionCode("XFC0044", nameof(BindingIndexerParse), "");
public static BuildExceptionCode BindingPropertyNotFound = new BuildExceptionCode("XFC0045", nameof(BindingPropertyNotFound), "");

//XAML issues
public static BuildExceptionCode MarkupNotClosed = new BuildExceptionCode("XFC0060", nameof(MarkupNotClosed), "");
public static BuildExceptionCode MarkupParsingFailed = new BuildExceptionCode("XFC0061", nameof(MarkupParsingFailed), "");
public static BuildExceptionCode XmlnsUndeclared = new BuildExceptionCode("XFC0062", nameof(XmlnsUndeclared), "");
public static BuildExceptionCode SByteEnums = new BuildExceptionCode("XFC0063", nameof(SByteEnums), "");
public static BuildExceptionCode NamescopeDuplicate = new BuildExceptionCode("XFC0064", nameof(NamescopeDuplicate), "");
public static BuildExceptionCode ContentPropertyAttributeMissing = new BuildExceptionCode("XFC0065", nameof(ContentPropertyAttributeMissing), "");
public static BuildExceptionCode InvalidXaml = new BuildExceptionCode("XFC0066", nameof(InvalidXaml), "");


//Extensions
public static BuildExceptionCode XStaticSyntax = new BuildExceptionCode("XFC0100", nameof(XStaticSyntax), "");
public static BuildExceptionCode XStaticResolution = new BuildExceptionCode("XFC0101", nameof(XStaticResolution), "");
public static BuildExceptionCode XDataTypeSyntax = new BuildExceptionCode("XFC0102", nameof(XDataTypeSyntax), "");

//Style, StyleSheets, Resources
public static BuildExceptionCode StyleSheetSourceOrContent = new BuildExceptionCode("XFC0120", nameof(StyleSheetSourceOrContent), "");
public static BuildExceptionCode StyleSheetNoSourceOrContent = new BuildExceptionCode("XFC0121", nameof(StyleSheetNoSourceOrContent), "");
public static BuildExceptionCode StyleSheetStyleNotALiteral = new BuildExceptionCode("XFC0122", nameof(StyleSheetStyleNotALiteral), "");
public static BuildExceptionCode StyleSheetSourceNotALiteral = new BuildExceptionCode("XFC0123", nameof(StyleSheetSourceNotALiteral), "");
public static BuildExceptionCode ResourceMissing = new BuildExceptionCode("XFC0124", nameof(ResourceMissing), "");
public static BuildExceptionCode ResourceDictDuplicateKey = new BuildExceptionCode("XFC0125", nameof(ResourceDictDuplicateKey), "");
public static BuildExceptionCode ResourceDictMissingKey = new BuildExceptionCode("XFC0126", nameof(ResourceDictMissingKey), "");

public string Code { get; private set; }
public string ErrorMessageKey { get; private set; }
public string HelpLink { get; private set; }

BuildExceptionCode()
{
}

BuildExceptionCode(string code, string errorMessage, string helpLink)
{
Code = code;
ErrorMessageKey = errorMessage;
HelpLink = helpLink;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Xamarin.Forms.Xaml;

using static System.String;
using static Xamarin.Forms.Build.Tasks.BuildExceptionCode;

namespace Xamarin.Forms.Core.XamlC
{
Expand All @@ -31,42 +32,52 @@ public FieldReference GetBindablePropertyFieldReference(string value, ModuleDefi
string typeName = null, propertyName = null;

var parts = value.Split('.');
if (parts.Length == 1) {
if (parts.Length == 1)
{
var parent = node.Parent?.Parent as IElementNode ?? (node.Parent?.Parent as IListNode)?.Parent as IElementNode;
if ( (node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.XFUri
&& ( (node.Parent as ElementNode)?.XmlType.Name == nameof(Setter)
|| (node.Parent as ElementNode)?.XmlType.Name == nameof(PropertyCondition))) {
if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.XFUri
&& ((node.Parent as ElementNode)?.XmlType.Name == nameof(Setter)
|| (node.Parent as ElementNode)?.XmlType.Name == nameof(PropertyCondition)))
{
if (parent.XmlType.NamespaceUri == XamlParser.XFUri &&
( parent.XmlType.Name == nameof(Trigger)
|| parent.XmlType.Name == nameof(DataTrigger)
|| parent.XmlType.Name == nameof(MultiTrigger)
|| parent.XmlType.Name == nameof(Style))) {
var ttnode = (parent as ElementNode).Properties [new XmlName("", "TargetType")];
(parent.XmlType.Name == nameof(Trigger)
|| parent.XmlType.Name == nameof(DataTrigger)
|| parent.XmlType.Name == nameof(MultiTrigger)
|| parent.XmlType.Name == nameof(Style)))
{
var ttnode = (parent as ElementNode).Properties[new XmlName("", "TargetType")];
if (ttnode is ValueNode)
typeName = (ttnode as ValueNode).Value as string;
else if (ttnode is IElementNode)
typeName = ((ttnode as IElementNode).CollectionItems.FirstOrDefault() as ValueNode)?.Value as string ?? ((ttnode as IElementNode).Properties [new XmlName("", "TypeName")] as ValueNode)?.Value as string;
} else if (parent.XmlType.NamespaceUri == XamlParser.XFUri && parent.XmlType.Name == nameof(VisualState)) {
typeName = ((ttnode as IElementNode).CollectionItems.FirstOrDefault() as ValueNode)?.Value as string ?? ((ttnode as IElementNode).Properties[new XmlName("", "TypeName")] as ValueNode)?.Value as string;
}
else if (parent.XmlType.NamespaceUri == XamlParser.XFUri && parent.XmlType.Name == nameof(VisualState))
{
typeName = FindTypeNameForVisualState(parent, node);
}
} else if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.XFUri && (node.Parent as ElementNode)?.XmlType.Name == nameof(Trigger))
typeName = ((node.Parent as ElementNode).Properties [new XmlName("", "TargetType")] as ValueNode).Value as string;
propertyName = parts [0];
} else if (parts.Length == 2) {
typeName = parts [0];
propertyName = parts [1];
} else
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(BindableProperty)}", node);
}
else if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.XFUri && (node.Parent as ElementNode)?.XmlType.Name == nameof(Trigger))
typeName = ((node.Parent as ElementNode).Properties[new XmlName("", "TargetType")] as ValueNode).Value as string;
propertyName = parts[0];
}
else if (parts.Length == 2)
{
typeName = parts[0];
propertyName = parts[1];
}
else
throw new BuildException(Conversion, node, null, value, typeof(BindableProperty));

if (typeName == null || propertyName == null)
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(BindableProperty)}", node);
throw new BuildException(Conversion, node, null, value, typeof(BindableProperty));

var typeRef = XmlTypeExtensions.GetTypeReference(typeName, module, node);
if (typeRef == null)
throw new XamlParseException($"Can't resolve {typeName}", node);
throw new BuildException(TypeResolution, node, null, typeName);

bpRef = GetBindablePropertyFieldReference(typeRef, propertyName, module);
if (bpRef == null)
throw new XamlParseException($"Can't resolve {propertyName} on {typeRef.Name}", node);
throw new BuildException(PropertyResolution, node, null, propertyName, typeRef.Name);
return bpRef;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
var module = context.Body.Method.Module;

if (IsNullOrEmpty(value))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Binding)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Binding));

yield return Instruction.Create(OpCodes.Ldstr, value);
yield return Instruction.Create(OpCodes.Ldc_I4, (int)BindingMode.Default);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
var module = context.Body.Method.Module;

if (string.IsNullOrEmpty(value))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Rectangle));

double x = -1, y = -1, w = -1, h = -1;
bool hasX, hasY, hasW, hasH;
var xywh = value.Split(',');

if (xywh.Length != 2 && xywh.Length != 4)
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Rectangle));

hasX = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh [0], NumberStyles.Number, CultureInfo.InvariantCulture, out x);
hasY = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh [1], NumberStyles.Number, CultureInfo.InvariantCulture, out y);
Expand All @@ -50,7 +50,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
}

if (!hasX || !hasY || !hasW || !hasH)
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Rectangle));

return GenerateIL(x, y, w, h, module);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
}
}
} while (false);

throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Color)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Color));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
double size;

if (string.IsNullOrEmpty(value) || !double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out size))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Constraint)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Constraint));

yield return Create(Ldc_R8, size);
yield return Create(Call, module.ImportMethodReference(("Xamarin.Forms.Core", "Xamarin.Forms", "Constraint"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
}
} while (false);

throw new XamlParseException(String.Format("Cannot convert \"{0}\" into {1}", value, typeof(LayoutOptions)), node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(LayoutOptions));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
//fail early
var resourceId = XamlCTask.GetResourceIdForPath(module, resourcePath);
if (resourceId == null)
throw new XamlParseException($"Resource '{value}' not found.", node);
throw new BuildException(BuildExceptionCode.ResourceMissing, node, null, value);

var resourceDictionaryType = ("Xamarin.Forms.Core", "Xamarin.Forms", "ResourceDictionary");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
var module = context.Body.Method.Module;

if (string.IsNullOrEmpty(value))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Rectangle));
double x, y, w, h;
var xywh = value.Split(',');
if (xywh.Length != 4 ||
!double.TryParse(xywh [0], NumberStyles.Number, CultureInfo.InvariantCulture, out x) ||
!double.TryParse(xywh [1], NumberStyles.Number, CultureInfo.InvariantCulture, out y) ||
!double.TryParse(xywh [2], NumberStyles.Number, CultureInfo.InvariantCulture, out w) ||
!double.TryParse(xywh [3], NumberStyles.Number, CultureInfo.InvariantCulture, out h))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Rectangle));

return GenerateIL(x, y, w, h, module);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
break;
}
}
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Thickness)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Thickness));
}

IEnumerable<Instruction> GenerateIL(ModuleDefinition module, params double[] args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public IEnumerable<Instruction> ConvertFromString(string value, ILContext contex
yield break;

error:
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Type)}", node);
throw new BuildException(BuildExceptionCode.Conversion, node, null, value, typeof(Type));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ public IEnumerable<Instruction> ProvideValue(IElementNode node, ModuleDefinition
typeNameNode = node.CollectionItems[0];

if (!(typeNameNode is ValueNode valueNode))
throw new XamlParseException("TypeName isn't set.", node as XmlLineInfo);
throw new BuildException(BuildExceptionCode.PropertyMissing, node as IXmlLineInfo, null, "TypeName", typeof(Xamarin.Forms.Xaml.DataTemplateExtension));

var contentTypeRef = module.ImportReference(XmlTypeExtensions.GetTypeReference(valueNode.Value as string, module, node as BaseNode))
?? throw new XamlParseException($"Can't resolve type `{valueNode.Value}'.", node as IXmlLineInfo);
?? throw new BuildException(BuildExceptionCode.TypeResolution, node as IXmlLineInfo, null, valueNode.Value);

var dataTemplateCtor = module.ImportCtorReference(typeRef, new[] { module.ImportReference(("mscorlib", "System", "Type")) });
return new List<Instruction> {
Expand Down
Loading

0 comments on commit 2745dc5

Please sign in to comment.