Browse Source

Support group transformations in the XAML Build Task

pull/9537/head
Max Katz 4 years ago
parent
commit
a9e904b0d2
  1. 1
      src/Avalonia.Base/Utilities/StringBuilderCache.cs
  2. 3
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  3. 2
      src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
  4. 12
      src/Avalonia.Build.Tasks/Extensions.cs
  5. 75
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

1
src/Avalonia.Base/Utilities/StringBuilderCache.cs

@ -9,6 +9,7 @@ using System;
using System.Text; using System.Text;
namespace Avalonia.Utilities; namespace Avalonia.Utilities;
#nullable enable
// <summary>Provide a cached reusable instance of stringbuilder per thread.</summary> // <summary>Provide a cached reusable instance of stringbuilder per thread.</summary>
internal static class StringBuilderCache internal static class StringBuilderCache

3
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -55,6 +55,9 @@
<Compile Include="../Avalonia.Base/Utilities/MathUtilities.cs"> <Compile Include="../Avalonia.Base/Utilities/MathUtilities.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="..\Avalonia.Base\Platform\AssetLoader.cs">
<Link>Markup\AssetLoader.cs</Link>
</Compile>
<Compile Include="..\Avalonia.Base\Utilities\StringBuilderCache.cs" Link="Utilities\StringBuilderCache.cs" /> <Compile Include="..\Avalonia.Base\Utilities\StringBuilderCache.cs" Link="Utilities\StringBuilderCache.cs" />
<Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\ArgumentListParser.cs"> <Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\ArgumentListParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>

2
src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs

@ -5,6 +5,8 @@ namespace Avalonia.Build.Tasks
InvalidXAML = 1, InvalidXAML = 1,
DuplicateXClass = 2, DuplicateXClass = 2,
LegacyResmScheme = 3, LegacyResmScheme = 3,
TransformError = 4,
EmitError = 4,
Unknown = 9999 Unknown = 9999
} }

12
src/Avalonia.Build.Tasks/Extensions.cs

@ -7,15 +7,19 @@ namespace Avalonia.Build.Tasks
{ {
static string FormatErrorCode(BuildEngineErrorCode code) => $"AVLN:{(int)code:0000}"; static string FormatErrorCode(BuildEngineErrorCode code) => $"AVLN:{(int)code:0000}";
public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message) public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message,
int lineNumber = 0, int linePosition = 0)
{ {
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message, engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "",
lineNumber, linePosition, lineNumber, linePosition, message,
"", "Avalonia")); "", "Avalonia"));
} }
public static void LogWarning(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message) public static void LogWarning(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message,
int lineNumber = 0, int linePosition = 0)
{ {
engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message, engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", FormatErrorCode(code), file ?? "",
lineNumber, linePosition, lineNumber, linePosition, message,
"", "Avalonia")); "", "Avalonia"));
} }

75
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -233,13 +233,14 @@ namespace Avalonia.Build.Tasks
var builder = typeSystem.CreateTypeBuilder(typeDef); var builder = typeSystem.CreateTypeBuilder(typeDef);
var populateMethodsToTransform = new List<(MethodDefinition populateMethod, string resourceFilePath)>(); var populateMethodsToTransform = new List<(MethodDefinition populateMethod, string resourceFilePath)>();
foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x=>x.FilePath.ToLowerInvariant())) IReadOnlyCollection<XamlDocumentResource> parsedXamlDocuments = new List<XamlDocumentResource>();
foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x => x.FilePath.ToLowerInvariant()))
{ {
engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", logImportance);
try try
{ {
engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", logImportance);
// StreamReader is needed here to handle BOM // StreamReader is needed here to handle BOM
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd(); var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
var parsed = XDocumentXamlParser.Parse(xaml); var parsed = XDocumentXamlParser.Parse(xaml);
@ -276,9 +277,9 @@ namespace Avalonia.Build.Tasks
compiler.Transform(parsed); compiler.Transform(parsed);
var populateName = classType == null ? "Populate:" + res.Name : "!XamlIlPopulate"; var populateName = classType == null ? "Populate:" + res.Name : "!XamlIlPopulate";
var buildName = classType == null ? "Build:" + res.Name : null; var buildName = classType == null ? "Build:" + res.Name : null;
var classTypeDefinition = var classTypeDefinition =
classType == null ? null : typeSystem.GetTypeReference(classType).Resolve(); classType == null ? null : typeSystem.GetTypeReference(classType).Resolve();
@ -286,11 +287,57 @@ namespace Avalonia.Build.Tasks
var populateBuilder = classTypeDefinition == null ? var populateBuilder = classTypeDefinition == null ?
builder : builder :
typeSystem.CreateTypeBuilder(classTypeDefinition); typeSystem.CreateTypeBuilder(classTypeDefinition);
compiler.Compile(parsed,
contextClass, ((List<XamlDocumentResource>)parsedXamlDocuments).Add(new XamlDocumentResource(
parsed, res.Uri, res, classType,
populateBuilder,
compiler.DefinePopulateMethod(populateBuilder, parsed, populateName, compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
classTypeDefinition == null), classTypeDefinition == null),
buildName == null ? null : compiler.DefineBuildMethod(builder, parsed, buildName, true), buildName == null ? null : compiler.DefineBuildMethod(builder, parsed, buildName, true)));
}
catch (Exception e)
{
int lineNumber = 0, linePosition = 0;
if (e is XamlParseException xe)
{
lineNumber = xe.LineNumber;
linePosition = xe.LinePosition;
}
engine.LogError(BuildEngineErrorCode.TransformError, res.FilePath, e.Message, lineNumber, linePosition);
return false;
}
}
try
{
compiler.TransformGroup(parsedXamlDocuments);
}
catch (XamlDocumentParseException e)
{
engine.LogError(BuildEngineErrorCode.TransformError, e.FilePath, e.Message, e.LineNumber, e.LinePosition);
}
catch (XamlParseException e)
{
engine.LogError(BuildEngineErrorCode.TransformError, "", e.Message, e.LineNumber, e.LinePosition);
}
foreach (var document in parsedXamlDocuments)
{
var parsed = document.XamlDocument;
var res = (IResource)document.FileSource;
var classType = document.ClassType;
var populateBuilder = document.TypeBuilder;
try
{
var classTypeDefinition =
classType == null ? null : typeSystem.GetTypeReference(classType).Resolve();
compiler.Compile(parsed,
contextClass,
document.PopulateMethod,
document.BuildMethod,
builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name, builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name,
true), true),
(closureName, closureBaseType) => (closureName, closureBaseType) =>
@ -417,12 +464,12 @@ namespace Avalonia.Build.Tasks
} }
if (buildName != null || classTypeDefinition != null) if (document.BuildMethod != null || classTypeDefinition != null)
{ {
var compiledBuildMethod = buildName == null ? var compiledBuildMethod = document.BuildMethod == null ?
null : null :
typeSystem.GetTypeReference(builder).Resolve() typeSystem.GetTypeReference(builder).Resolve()
.Methods.First(m => m.Name == buildName); .Methods.First(m => m.Name == document.BuildMethod.Name);
var parameterlessConstructor = compiledBuildMethod != null ? var parameterlessConstructor = compiledBuildMethod != null ?
null : null :
classTypeDefinition.GetConstructors().FirstOrDefault(c => classTypeDefinition.GetConstructors().FirstOrDefault(c =>
@ -459,9 +506,7 @@ namespace Avalonia.Build.Tasks
lineNumber = xe.LineNumber; lineNumber = xe.LineNumber;
linePosition = xe.LinePosition; linePosition = xe.LinePosition;
} }
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", res.FilePath, engine.LogError(BuildEngineErrorCode.EmitError, res.FilePath, e.Message, lineNumber, linePosition);
lineNumber, linePosition, lineNumber, linePosition,
e.Message, "", "Avalonia"));
return false; return false;
} }
res.Remove(); res.Remove();

Loading…
Cancel
Save