[XamlC] XamlResourceIdAttribute (#1167)
This commit is contained in:
Родитель
f6b42cc271
Коммит
de255a8b9e
|
@ -33,7 +33,7 @@
|
|||
</GetTasksAbi >
|
||||
|
||||
<PropertyGroup>
|
||||
<_XFTasksExpectedAbi>2</_XFTasksExpectedAbi>
|
||||
<_XFTasksExpectedAbi>3</_XFTasksExpectedAbi>
|
||||
</PropertyGroup>
|
||||
|
||||
<Error
|
||||
|
@ -92,4 +92,4 @@
|
|||
DebugType = "$(DebugType)"
|
||||
KeepXamlResources = "$(XFKeepXamlResources)" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
public class GetTasksAbi : Task
|
||||
{
|
||||
[Output]
|
||||
public string AbiVersion { get; } = "2";
|
||||
public string AbiVersion { get; } = "3";
|
||||
|
||||
public override bool Execute()
|
||||
=> true;
|
||||
|
|
|
@ -47,12 +47,13 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
{
|
||||
Log.LogMessage("Source: {0}", xamlFile.ItemSpec);
|
||||
Log.LogMessage("Language: {0}", Language);
|
||||
Log.LogMessage("ResourceID: {0}", xamlFile.GetMetadata("ManifestResourceName"));
|
||||
Log.LogMessage("AssemblyName: {0}", AssemblyName);
|
||||
Log.LogMessage("OutputFile {0}", targetPath);
|
||||
|
||||
try
|
||||
{
|
||||
GenerateFile(xamlFile.ItemSpec, targetPath);
|
||||
GenerateFile(xamlFile.ItemSpec, xamlFile.GetMetadata("ManifestResourceName"), targetPath);
|
||||
_generatedCodeFiles.Add(new TaskItem(Microsoft.Build.Evaluation.ProjectCollection.Escape(targetPath)));
|
||||
return true;
|
||||
}
|
||||
|
@ -125,7 +126,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
new CodeAttributeArgument(new CodePrimitiveExpression("0.0.0.0")));
|
||||
|
||||
internal static void GenerateCode(string rootType, string rootNs, CodeTypeReference baseType,
|
||||
IEnumerable<CodeMemberField> namedFields, string xamlFile, string outFile)
|
||||
IEnumerable<CodeMemberField> namedFields, string xamlFile, string resourceId, string outFile)
|
||||
{
|
||||
//Create the target directory if required
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outFile));
|
||||
|
@ -144,7 +145,10 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
IsPartial = true,
|
||||
CustomAttributes = {
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlFilePathAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(xamlFile)))
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(xamlFile))),
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlResourceIdAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(rootNs)),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(resourceId))),
|
||||
}
|
||||
};
|
||||
declType.BaseTypes.Add(baseType);
|
||||
|
@ -182,7 +186,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
Provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions());
|
||||
}
|
||||
|
||||
internal static void GenerateFile(string xamlFile, string outFile)
|
||||
internal static void GenerateFile(string xamlFile, string resourceId, string outFile)
|
||||
{
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
|
@ -191,7 +195,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
using (StreamReader reader = File.OpenText(xamlFile))
|
||||
ParseXaml(reader, out rootType, out rootNs, out baseType, out namedFields);
|
||||
|
||||
GenerateCode(rootType, rootNs, baseType, namedFields, Path.GetFullPath(xamlFile), outFile);
|
||||
GenerateCode(rootType, rootNs, baseType, namedFields, Path.GetFullPath(xamlFile), resourceId, outFile);
|
||||
}
|
||||
|
||||
static IEnumerable<CodeMemberField> GetCodeMemberFields(XmlNode root, XmlNamespaceManager nsmgr)
|
||||
|
@ -297,4 +301,4 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -473,6 +473,7 @@
|
|||
<Compile Include="CompressedLayout.cs" />
|
||||
<Compile Include="PlatformConfiguration\macOSSpecific\NavigationPage.cs" />
|
||||
<Compile Include="PlatformConfiguration\macOSSpecific\NavigationTransitionStyle.cs" />
|
||||
<Compile Include="Xaml\XamlResourceIdAttribute.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
@ -489,4 +490,4 @@
|
|||
<ItemGroup />
|
||||
<ItemGroup />
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class XamlResourceIdAttribute : Attribute
|
||||
{
|
||||
public string RootNamespace { get; set; }
|
||||
public string ResourceId { get; set; }
|
||||
|
||||
public XamlResourceIdAttribute(string rootNamespace, string resourceId)
|
||||
{
|
||||
RootNamespace = rootNamespace;
|
||||
ResourceId = resourceId;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,4 +39,4 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
Assert.DoesNotThrow (() => File.Delete (xamlInputFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace Xamarin.Forms.Xaml
|
|||
else
|
||||
n = string.Concat(Path.GetFileName(f), ".g.", XamlGTask.Provider.FileExtension);
|
||||
|
||||
XamlGTask.GenerateFile(f, n);
|
||||
XamlGTask.GenerateFile(f, f, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,8 +58,6 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
static class XamlLoader
|
||||
{
|
||||
static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
|
||||
|
||||
public static void Load(object view, Type callingType)
|
||||
{
|
||||
var xaml = GetXamlForType(callingType);
|
||||
|
@ -146,125 +144,30 @@ namespace Xamarin.Forms.Xaml
|
|||
//the Previewer might want to provide it's own xaml for this... let them do that
|
||||
//the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
|
||||
|
||||
string xaml;
|
||||
#pragma warning disable 0618
|
||||
var xaml = Internals.XamlLoader.XamlFileProvider?.Invoke(type);
|
||||
if (ResourceLoader.ResourceProvider == null && (xaml = Internals.XamlLoader.XamlFileProvider?.Invoke(type)) != null)
|
||||
return xaml;
|
||||
#pragma warning restore 0618
|
||||
|
||||
if (xaml != null && ResourceLoader.ResourceProvider == null)
|
||||
return xaml;
|
||||
|
||||
var assembly = type.GetTypeInfo().Assembly;
|
||||
|
||||
string resourceId;
|
||||
if (XamlResources.TryGetValue(type, out resourceId))
|
||||
{
|
||||
var result = ReadResourceAsXaml(type, assembly, resourceId);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
var likelyResourceName = type.Name + ".xaml";
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
string resourceName = null;
|
||||
|
||||
// first pass, pray to find it because the user named it correctly
|
||||
|
||||
foreach (var resource in resourceNames)
|
||||
{
|
||||
if (ResourceMatchesFilename(assembly, resource, likelyResourceName))
|
||||
{
|
||||
resourceName = resource;
|
||||
xaml = ReadResourceAsXaml(type, assembly, resource);
|
||||
if (xaml != null)
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
// okay maybe they at least named it .xaml
|
||||
|
||||
foreach (var resource in resourceNames)
|
||||
{
|
||||
if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
resourceName = resource;
|
||||
xaml = ReadResourceAsXaml(type, assembly, resource);
|
||||
if (xaml != null)
|
||||
goto end;
|
||||
}
|
||||
|
||||
foreach (var resource in resourceNames)
|
||||
{
|
||||
if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
resourceName = resource;
|
||||
xaml = ReadResourceAsXaml(type, assembly, resource, true);
|
||||
if (xaml != null)
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
if (xaml == null)
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
var assembly = typeInfo.Assembly;
|
||||
var resourceId = typeInfo.GetCustomAttribute<XamlResourceIdAttribute>()?.ResourceId;
|
||||
if (resourceId == null)
|
||||
return null;
|
||||
|
||||
XamlResources[type] = resourceName;
|
||||
var alternateXaml = ResourceLoader.ResourceProvider?.Invoke(resourceName);
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceId)) {
|
||||
if (stream != null)
|
||||
using (var reader = new StreamReader(stream))
|
||||
xaml = reader.ReadToEnd();
|
||||
else
|
||||
xaml = null;
|
||||
}
|
||||
|
||||
var alternateXaml = ResourceLoader.ResourceProvider?.Invoke(resourceId);
|
||||
return alternateXaml ?? xaml;
|
||||
}
|
||||
|
||||
static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = assembly.GetManifestResourceInfo(resource);
|
||||
|
||||
if (!string.IsNullOrEmpty(info.FileName) &&
|
||||
string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
return true;
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
// Because Win10 + .NET Native
|
||||
}
|
||||
|
||||
if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
|
||||
{
|
||||
using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
if (validate)
|
||||
{
|
||||
// terrible validation of XML. Unfortunately it will probably work most of the time since comments
|
||||
// also start with a <. We can't bring in any real deps.
|
||||
|
||||
var firstNonWhitespace = (char)reader.Read();
|
||||
while (char.IsWhiteSpace(firstNonWhitespace))
|
||||
firstNonWhitespace = (char)reader.Read();
|
||||
|
||||
if (firstNonWhitespace != '<')
|
||||
return null;
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
var xaml = reader.ReadToEnd();
|
||||
|
||||
var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
|
||||
var regex = new Regex(pattern, RegexOptions.ECMAScript);
|
||||
if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
|
||||
return xaml;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public class RuntimeRootNode : RootNode
|
||||
{
|
||||
public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver)
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<Type Name="XamlResourceIdAttribute" FullName="Xamarin.Forms.Xaml.XamlResourceIdAttribute">
|
||||
<TypeSignature Language="C#" Value="public sealed class XamlResourceIdAttribute : Attribute" />
|
||||
<TypeSignature Language="ILAsm" Value=".class public auto ansi sealed beforefieldinit XamlResourceIdAttribute extends System.Attribute" />
|
||||
<AssemblyInfo>
|
||||
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Base>
|
||||
<BaseTypeName>System.Attribute</BaseTypeName>
|
||||
</Base>
|
||||
<Interfaces />
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=false, Inherited=false)</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
<Members>
|
||||
<Member MemberName=".ctor">
|
||||
<MemberSignature Language="C#" Value="public XamlResourceIdAttribute (string rootNamespace, string resourceId);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor(string rootNamespace, string resourceId) cil managed" />
|
||||
<MemberType>Constructor</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Parameters>
|
||||
<Parameter Name="rootNamespace" Type="System.String" />
|
||||
<Parameter Name="resourceId" Type="System.String" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="rootNamespace">To be added.</param>
|
||||
<param name="resourceId">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="ResourceId">
|
||||
<MemberSignature Language="C#" Value="public string ResourceId { get; set; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property instance string ResourceId" />
|
||||
<MemberType>Property</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.String</ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<value>To be added.</value>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="RootNamespace">
|
||||
<MemberSignature Language="C#" Value="public string RootNamespace { get; set; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property instance string RootNamespace" />
|
||||
<MemberType>Property</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.String</ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<value>To be added.</value>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
</Members>
|
||||
</Type>
|
|
@ -552,6 +552,7 @@
|
|||
<Type Name="IXamlTypeResolver" Kind="Interface" />
|
||||
<Type Name="IXmlLineInfoProvider" Kind="Interface" />
|
||||
<Type Name="XamlParseException" Kind="Class" />
|
||||
<Type Name="XamlResourceIdAttribute" Kind="Class" />
|
||||
<Type Name="XmlLineInfo" Kind="Class" />
|
||||
</Namespace>
|
||||
<Namespace Name="Xamarin.Forms.Xaml.Internals">
|
||||
|
|
Загрузка…
Ссылка в новой задаче