[XamlC] XamlResourceIdAttribute (#1167)

This commit is contained in:
Stephane Delcroix 2017-10-23 10:56:23 +02:00 коммит произвёл GitHub
Родитель f6b42cc271
Коммит de255a8b9e
10 изменённых файлов: 124 добавлений и 125 удалений

Просмотреть файл

@ -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">