diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index f98b1fe4f4..51cb04a3a1 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -37,14 +37,14 @@ EmbeddedResources="@(EmbeddedResources)"/> + Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:BuildProjectReferences=false"/> @@ -61,10 +61,11 @@ AssemblyFile="@(IntermediateAssembly)" ReferencesFilePath="$(AvaloniaXamlReferencesTemporaryFilePath)" OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)" + ProjectDirectory="$(MSBuildProjectDirectory)" /> + Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileAvaloniaXaml /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:BuildProjectReferences=false"/> diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index fea55bcb07..8534c022f8 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -1,6 +1,6 @@ +> diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs index 028643f7c0..54db830f2f 100644 --- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs +++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs @@ -21,12 +21,15 @@ namespace Avalonia.Build.Tasks File.Delete(AssemblyFile); } - var data = XamlCompilerTaskExecutor.Compile(BuildEngine, input, - File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray()); - if(data == null) + var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input, + File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(), + ProjectDirectory); + if (!res.Success) + return false; + if (res.Data == null) File.Copy(input, OutputPath); else - File.WriteAllBytes(OutputPath, data); + File.WriteAllBytes(OutputPath, res.Data); return true; } @@ -39,6 +42,8 @@ namespace Avalonia.Build.Tasks public string ReferencesFilePath { get; set; } [Required] public string OriginalCopyPath { get; set; } + [Required] + public string ProjectDirectory { get; set; } public string OutputPath { get; set; } diff --git a/src/Avalonia.Build.Tasks/Program.cs b/src/Avalonia.Build.Tasks/Program.cs index 2f206acf2a..c2d0950264 100644 --- a/src/Avalonia.Build.Tasks/Program.cs +++ b/src/Avalonia.Build.Tasks/Program.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.IO; using Microsoft.Build.Framework; namespace Avalonia.Build.Tasks @@ -19,7 +20,8 @@ namespace Avalonia.Build.Tasks AssemblyFile = args[0], ReferencesFilePath = args[1], OutputPath = args[2], - BuildEngine = new ConsoleBuildEngine() + BuildEngine = new ConsoleBuildEngine(), + ProjectDirectory = Directory.GetCurrentDirectory() }.Execute() ? 0 : 2; diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index acaa80b608..5addf939eb 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -10,6 +10,8 @@ using Mono.Cecil; using XamlIl.TypeSystem; using Avalonia.Utilities; using Mono.Cecil.Rocks; +using XamlIl; +using XamlIl.Ast; using XamlIl.Parsers; using XamlIl.Transform; using TypeAttributes = Mono.Cecil.TypeAttributes; @@ -47,7 +49,19 @@ namespace Avalonia.Build.Tasks return rv; } - public static byte[] Compile(IBuildEngine engine, string input, string[] references) + public class CompileResult + { + public bool Success { get; set; } + public byte[] Data { get; set; } + + public CompileResult(bool success, byte[] data = null) + { + Success = success; + Data = data; + } + } + + public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory) { var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input); var asm = typeSystem.TargetAssemblyDefinition; @@ -55,7 +69,7 @@ namespace Avalonia.Build.Tasks var avares = ReadAvaloniaXamlResources(asm); if (avares.Count == 0 && emres.Count == 0) // Nothing to do - return null; + return new CompileResult(true); var xamlLanguage = AvaloniaXamlIlLanguage.Configure(typeSystem); var compilerConfig = new XamlIlTransformerConfiguration(typeSystem, typeSystem.TargetAssembly, @@ -80,7 +94,8 @@ namespace Avalonia.Build.Tasks asm.MainModule.ImportReference(editorBrowsableAttribute.GetConstructors() .First(c => c.Parameters.Count == 1)); - void CompileGroup(Dictionary resources, string name, Func uriTransform) + bool CompileGroup(Dictionary resources, string name, Func uriTransform, + Func pathTransform) { var typeDef = new TypeDefinition("CompiledAvaloniaXaml", name, TypeAttributes.Class, asm.MainModule.TypeSystem.Object); @@ -94,23 +109,63 @@ namespace Avalonia.Build.Tasks var builder = typeSystem.CreateTypeBuilder(typeDef); foreach (var res in resources) { - var xaml = Encoding.UTF8.GetString(res.Value); - var parsed = XDocumentXamlIlParser.Parse(xaml); - compiler.Transform(parsed); - compiler.Compile(parsed, builder, contextClass, - "Populate:" + res.Key, "Build:" + res.Key, - "NamespaceInfo:" + res.Key, uriTransform(res.Key)); + try + { + // StreamReader is needed here to handle BOM + var xaml = new StreamReader(new MemoryStream(res.Value)).ReadToEnd(); + var parsed = XDocumentXamlIlParser.Parse(xaml); + + var initialRoot = (XamlIlAstObjectNode)parsed.Root; + var classDirective = initialRoot.Children.OfType() + .FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class"); + + if (classDirective != null) + { + if (classDirective.Values.Count != 1 || !(classDirective.Values[0] is XamlIlAstTextNode tn)) + throw new XamlIlParseException("x:Class should have a string value", classDirective); + var classType = typeSystem.TargetAssembly.FindType(tn.Text); + if (classType == null) + throw new XamlIlParseException($"Unable to find type `{tn.Text}`", classDirective); + initialRoot.Type = new XamlIlAstClrTypeReference(classDirective, classType); + } + + + compiler.Transform(parsed); + compiler.Compile(parsed, builder, contextClass, + "Populate:" + res.Key, "Build:" + res.Key, + "NamespaceInfo:" + res.Key, uriTransform(res.Key)); + } + catch (Exception e) + { + int lineNumber = 0, linePosition = 0; + if (e is XamlIlParseException xe) + { + lineNumber = xe.Line; + linePosition = xe.Position; + } + engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", pathTransform(res.Key), + lineNumber, linePosition, lineNumber, linePosition, + e.Message, "", "Avalonia")); + return false; + } } + + return true; } if (emres.Count != 0) - CompileGroup(emres, "EmbeddedResource", name => $"resm:{name}?assembly={asm.Name}"); + if (!CompileGroup(emres, "EmbeddedResource", + name => $"resm:{name}?assembly={asm.Name}", name => name)) + return new CompileResult(false); if (avares.Count != 0) - CompileGroup(avares, "AvaloniaResource", name => $"avares://{asm.Name}/{name}"); + if (!CompileGroup(avares, "AvaloniaResource", + name => $"avares://{asm.Name}/{name}", + name => Path.Combine(projectDirectory, name.TrimStart('/')))) + return new CompileResult(false); var ms = new MemoryStream(); asm.Write(ms); - return ms.ToArray(); + return new CompileResult(true, ms.ToArray()); } } diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index 045cc49142..ea53f308ab 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -1,7 +1,6 @@  netstandard2.0 - true diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index ddc2490b8f..570fa4d67e 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit ddc2490b8f0437c42c9bc4662b25c2f693255278 +Subproject commit 570fa4d67ed0da7fc5b6e36c3100f0ccbba5aa5a