|
|
|
@ -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<string, byte[]> resources, string name, Func<string, string> uriTransform) |
|
|
|
bool CompileGroup(Dictionary<string, byte[]> resources, string name, Func<string, string> uriTransform, |
|
|
|
Func<string, string> 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<XamlIlAstXmlDirective>() |
|
|
|
.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()); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|