From a9e904b0d2112be95e53cb27f5c6b016dd6b85c2 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 25 Nov 2022 01:01:16 -0500 Subject: [PATCH] Support group transformations in the XAML Build Task --- .../Utilities/StringBuilderCache.cs | 1 + .../Avalonia.Build.Tasks.csproj | 3 + .../BuildEngineErrorCode.cs | 2 + src/Avalonia.Build.Tasks/Extensions.cs | 12 ++- .../XamlCompilerTaskExecutor.cs | 75 +++++++++++++++---- 5 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Base/Utilities/StringBuilderCache.cs b/src/Avalonia.Base/Utilities/StringBuilderCache.cs index be8b24c848..cbbcf2b398 100644 --- a/src/Avalonia.Base/Utilities/StringBuilderCache.cs +++ b/src/Avalonia.Base/Utilities/StringBuilderCache.cs @@ -9,6 +9,7 @@ using System; using System.Text; namespace Avalonia.Utilities; +#nullable enable // Provide a cached reusable instance of stringbuilder per thread. internal static class StringBuilderCache diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 74debed828..ce84f23dd4 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -55,6 +55,9 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup\AssetLoader.cs + Markup/%(RecursiveDir)%(FileName)%(Extension) diff --git a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs index 43365346b3..a149a758f4 100644 --- a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs +++ b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs @@ -5,6 +5,8 @@ namespace Avalonia.Build.Tasks InvalidXAML = 1, DuplicateXClass = 2, LegacyResmScheme = 3, + TransformError = 4, + EmitError = 4, Unknown = 9999 } diff --git a/src/Avalonia.Build.Tasks/Extensions.cs b/src/Avalonia.Build.Tasks/Extensions.cs index 46c12eaf3d..7295f56b3a 100644 --- a/src/Avalonia.Build.Tasks/Extensions.cs +++ b/src/Avalonia.Build.Tasks/Extensions.cs @@ -7,15 +7,19 @@ namespace Avalonia.Build.Tasks { 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")); } - 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")); } diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 974f8485c0..589472785d 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -233,13 +233,14 @@ namespace Avalonia.Build.Tasks var builder = typeSystem.CreateTypeBuilder(typeDef); var populateMethodsToTransform = new List<(MethodDefinition populateMethod, string resourceFilePath)>(); - - foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x=>x.FilePath.ToLowerInvariant())) + + IReadOnlyCollection parsedXamlDocuments = new List(); + foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x => x.FilePath.ToLowerInvariant())) { + engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", logImportance); + try { - engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", logImportance); - // StreamReader is needed here to handle BOM var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd(); var parsed = XDocumentXamlParser.Parse(xaml); @@ -276,9 +277,9 @@ namespace Avalonia.Build.Tasks compiler.Transform(parsed); + 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 = classType == null ? null : typeSystem.GetTypeReference(classType).Resolve(); @@ -286,11 +287,57 @@ namespace Avalonia.Build.Tasks var populateBuilder = classTypeDefinition == null ? builder : typeSystem.CreateTypeBuilder(classTypeDefinition); - compiler.Compile(parsed, - contextClass, + + ((List)parsedXamlDocuments).Add(new XamlDocumentResource( + parsed, res.Uri, res, classType, + populateBuilder, compiler.DefinePopulateMethod(populateBuilder, parsed, populateName, 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, true), (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 : typeSystem.GetTypeReference(builder).Resolve() - .Methods.First(m => m.Name == buildName); + .Methods.First(m => m.Name == document.BuildMethod.Name); var parameterlessConstructor = compiledBuildMethod != null ? null : classTypeDefinition.GetConstructors().FirstOrDefault(c => @@ -459,9 +506,7 @@ namespace Avalonia.Build.Tasks lineNumber = xe.LineNumber; linePosition = xe.LinePosition; } - engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", res.FilePath, - lineNumber, linePosition, lineNumber, linePosition, - e.Message, "", "Avalonia")); + engine.LogError(BuildEngineErrorCode.EmitError, res.FilePath, e.Message, lineNumber, linePosition); return false; } res.Remove();