зеркало из https://github.com/DeGsoft/maui-linux.git
[XamlC] Compile RDs without codebehind (#1241)
* [XamlC] generate code behind and compile RD without x:Class * [XamlG] change Namespace and add [EditorBrowsable]
This commit is contained in:
Родитель
256e9219c3
Коммит
550520b59b
|
@ -56,7 +56,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
foreach (var resource in module.Resources.OfType<EmbeddedResource>()) {
|
||||
Logger.LogString(2, " Resource: {0}... ", resource.Name);
|
||||
string classname;
|
||||
if (!resource.IsXaml(out classname)) {
|
||||
if (!resource.IsXaml(module, out classname)) {
|
||||
Logger.LogLine(2, "skipped.");
|
||||
continue;
|
||||
} else
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
<Compile Include="GetTasksAbi.cs" />
|
||||
<Compile Include="CompiledConverters\UriTypeConverter.cs" />
|
||||
<Compile Include="CompiledConverters\RDSourceTypeConverter.cs" />
|
||||
<Compile Include="XamlGenerator.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="AfterBuild">
|
||||
|
|
|
@ -117,7 +117,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
foreach (var resource in module.Resources.OfType<EmbeddedResource>()) {
|
||||
Logger.LogString(2, " Resource: {0}... ", resource.Name);
|
||||
string classname;
|
||||
if (!resource.IsXaml(out classname)) {
|
||||
if (!resource.IsXaml(module, out classname)) {
|
||||
Logger.LogLine(2, "skipped.");
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
using System;
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.CSharp;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Build.Tasks
|
||||
{
|
||||
public class XamlGTask : Task
|
||||
{
|
||||
internal static CodeDomProvider Provider = new CSharpCodeProvider();
|
||||
List<ITaskItem> _generatedCodeFiles = new List<ITaskItem>();
|
||||
|
||||
[Required]
|
||||
|
@ -28,7 +24,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
public override bool Execute()
|
||||
{
|
||||
bool result = true;
|
||||
bool success = true;
|
||||
|
||||
if (XamlFiles == null) {
|
||||
Log.LogMessage("Skipping XamlG");
|
||||
|
@ -36,277 +32,29 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
}
|
||||
|
||||
foreach (var xamlFile in XamlFiles) {
|
||||
var targetPath = Path.Combine(OutputPath, xamlFile.GetMetadata("TargetPath") + ".g.cs");
|
||||
if (Path.DirectorySeparatorChar == '/' && targetPath.Contains(@"\"))
|
||||
targetPath = targetPath.Replace('\\','/');
|
||||
else if (Path.DirectorySeparatorChar == '\\' && targetPath.Contains(@"/"))
|
||||
targetPath = targetPath.Replace('/', '\\');
|
||||
result &= Execute(xamlFile, targetPath);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal bool Execute(ITaskItem xamlFile, string outputFile)
|
||||
{
|
||||
Log.LogMessage("Source: {0}", xamlFile.ItemSpec);
|
||||
Log.LogMessage(" Language: {0}", Language);
|
||||
Log.LogMessage(" ResourceID: {0}", xamlFile.GetMetadata("ManifestResourceName"));
|
||||
Log.LogMessage(" TargetPath: {0}", xamlFile.GetMetadata("TargetPath"));
|
||||
Log.LogMessage(" AssemblyName: {0}", AssemblyName);
|
||||
Log.LogMessage(" OutputFile {0}", outputFile);
|
||||
|
||||
try
|
||||
{
|
||||
GenerateFile(xamlFile.ItemSpec, xamlFile.GetMetadata("ManifestResourceName"), xamlFile.GetMetadata("TargetPath"), outputFile);
|
||||
_generatedCodeFiles.Add(new TaskItem(Microsoft.Build.Evaluation.ProjectCollection.Escape(outputFile)));
|
||||
return true;
|
||||
}
|
||||
catch (XmlException xe)
|
||||
{
|
||||
Log.LogError(null, null, null, xamlFile.ItemSpec, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(null, null, null, xamlFile.ItemSpec, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ParseXaml(TextReader xaml, out string rootType, out string rootNs, out CodeTypeReference baseType,
|
||||
out IEnumerable<CodeMemberField> namedFields)
|
||||
{
|
||||
var xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(xaml);
|
||||
|
||||
var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
|
||||
nsmgr.AddNamespace("__f__", XamlParser.XFUri);
|
||||
|
||||
var root = xmlDoc.SelectSingleNode("/*", nsmgr);
|
||||
|
||||
if (root == null)
|
||||
{
|
||||
Console.Error.WriteLine("{0}: No root node found");
|
||||
rootType = null;
|
||||
rootNs = null;
|
||||
baseType = null;
|
||||
namedFields = null;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (XmlAttribute attr in root.Attributes) {
|
||||
if (attr.Name == "xmlns") {
|
||||
nsmgr.AddNamespace("", attr.Value); //Add default xmlns
|
||||
var outputFile = Path.Combine(OutputPath, $"{xamlFile.GetMetadata("TargetPath")}.g.cs");
|
||||
if (Path.DirectorySeparatorChar == '/' && outputFile.Contains(@"\"))
|
||||
outputFile = outputFile.Replace('\\','/');
|
||||
else if (Path.DirectorySeparatorChar == '\\' && outputFile.Contains(@"/"))
|
||||
outputFile = outputFile.Replace('/', '\\');
|
||||
|
||||
var generator = new XamlGenerator(xamlFile, Language, AssemblyName, outputFile, Log);
|
||||
try {
|
||||
if (generator.Execute())
|
||||
_generatedCodeFiles.Add(new TaskItem(Microsoft.Build.Evaluation.ProjectCollection.Escape(outputFile)));
|
||||
}
|
||||
if (attr.Prefix != "xmlns")
|
||||
continue;
|
||||
nsmgr.AddNamespace(attr.LocalName, attr.Value);
|
||||
}
|
||||
catch (XmlException xe) {
|
||||
Log.LogError(null, null, null, xamlFile.ItemSpec, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
|
||||
|
||||
var rootClass = root.Attributes["Class", XamlParser.X2006Uri]
|
||||
?? root.Attributes["Class", XamlParser.X2009Uri];
|
||||
if (rootClass == null)
|
||||
{
|
||||
rootType = null;
|
||||
rootNs = null;
|
||||
baseType = null;
|
||||
namedFields = null;
|
||||
return;
|
||||
}
|
||||
|
||||
string rootAsm, targetPlatform;
|
||||
XmlnsHelper.ParseXmlns(rootClass.Value, out rootType, out rootNs, out rootAsm, out targetPlatform);
|
||||
namedFields = GetCodeMemberFields(root, nsmgr);
|
||||
|
||||
var typeArguments = GetAttributeValue(root, "TypeArguments", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
var xmlType = new XmlType(root.NamespaceURI, root.LocalName, typeArguments != null ? TypeArgumentsParser.ParseExpression(typeArguments, nsmgr, null): null);
|
||||
baseType = GetType(xmlType, root.GetNamespaceOfPrefix);
|
||||
}
|
||||
|
||||
static CodeAttributeDeclaration GeneratedCodeAttrDecl =>
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(GeneratedCodeAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression("Xamarin.Forms.Build.Tasks.XamlG")),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression("0.0.0.0")));
|
||||
|
||||
internal static void GenerateCode(string rootType, string rootNs, CodeTypeReference baseType,
|
||||
IEnumerable<CodeMemberField> namedFields, string xamlFile, string resourceId, string targetPath, string outFile)
|
||||
{
|
||||
//Create the target directory if required
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outFile));
|
||||
|
||||
if (rootType == null)
|
||||
{
|
||||
File.WriteAllText(outFile, "");
|
||||
return;
|
||||
}
|
||||
|
||||
var ccu = new CodeCompileUnit();
|
||||
ccu.AssemblyCustomAttributes.Add(
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlResourceIdAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(resourceId)),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(targetPath.Replace('\\', '/'))), //use forward slashes, paths are uris-like
|
||||
new CodeAttributeArgument(new CodeTypeOfExpression($"global::{rootNs}.{rootType}"))
|
||||
));
|
||||
var declNs = new CodeNamespace(rootNs);
|
||||
ccu.Namespaces.Add(declNs);
|
||||
|
||||
var declType = new CodeTypeDeclaration(rootType) {
|
||||
IsPartial = true,
|
||||
CustomAttributes = {
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlFilePathAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(xamlFile))),
|
||||
success = false;
|
||||
}
|
||||
};
|
||||
declType.BaseTypes.Add(baseType);
|
||||
|
||||
declNs.Types.Add(declType);
|
||||
|
||||
var initcomp = new CodeMemberMethod {
|
||||
Name = "InitializeComponent",
|
||||
CustomAttributes = { GeneratedCodeAttrDecl }
|
||||
};
|
||||
|
||||
declType.Members.Add(initcomp);
|
||||
|
||||
initcomp.Statements.Add(new CodeMethodInvokeExpression(
|
||||
new CodeTypeReferenceExpression(new CodeTypeReference($"global::{typeof(Extensions).FullName}")),
|
||||
"LoadFromXaml", new CodeThisReferenceExpression(), new CodeTypeOfExpression(declType.Name)));
|
||||
|
||||
foreach (var namedField in namedFields)
|
||||
{
|
||||
declType.Members.Add(namedField);
|
||||
|
||||
var find_invoke = new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(
|
||||
new CodeTypeReferenceExpression(new CodeTypeReference($"global::{typeof(NameScopeExtensions).FullName}")),
|
||||
"FindByName", namedField.Type),
|
||||
new CodeThisReferenceExpression(), new CodePrimitiveExpression(namedField.Name));
|
||||
|
||||
CodeAssignStatement assign = new CodeAssignStatement(
|
||||
new CodeVariableReferenceExpression(namedField.Name), find_invoke);
|
||||
|
||||
initcomp.Statements.Add(assign);
|
||||
}
|
||||
|
||||
using (var writer = new StreamWriter(outFile))
|
||||
Provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions());
|
||||
}
|
||||
|
||||
internal static void GenerateFile(string xamlFile, string resourceId, string targetPath, string outFile)
|
||||
{
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> namedFields;
|
||||
|
||||
using (StreamReader reader = File.OpenText(xamlFile))
|
||||
ParseXaml(reader, out rootType, out rootNs, out baseType, out namedFields);
|
||||
|
||||
GenerateCode(rootType, rootNs, baseType, namedFields, Path.GetFullPath(xamlFile), resourceId, targetPath, outFile);
|
||||
}
|
||||
|
||||
static IEnumerable<CodeMemberField> GetCodeMemberFields(XmlNode root, XmlNamespaceManager nsmgr)
|
||||
{
|
||||
var xPrefix = nsmgr.LookupPrefix(XamlParser.X2006Uri) ?? nsmgr.LookupPrefix(XamlParser.X2009Uri);
|
||||
if (xPrefix == null)
|
||||
yield break;
|
||||
|
||||
XmlNodeList names =
|
||||
root.SelectNodes(
|
||||
"//*[@" + xPrefix + ":Name" +
|
||||
"][not(ancestor:: __f__:DataTemplate) and not(ancestor:: __f__:ControlTemplate) and not(ancestor:: __f__:Style)]", nsmgr);
|
||||
foreach (XmlNode node in names)
|
||||
{
|
||||
// Don't take the root canvas
|
||||
if (node == root)
|
||||
continue;
|
||||
var name = GetAttributeValue(node, "Name", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
var typeArguments = GetAttributeValue(node, "TypeArguments", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
var fieldModifier = GetAttributeValue(node, "FieldModifier", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
|
||||
var xmlType = new XmlType(node.NamespaceURI, node.LocalName,
|
||||
typeArguments != null
|
||||
? TypeArgumentsParser.ParseExpression(typeArguments, nsmgr, null)
|
||||
: null);
|
||||
|
||||
var access = MemberAttributes.Private;
|
||||
if (fieldModifier!=null) {
|
||||
switch (fieldModifier.ToLowerInvariant()){
|
||||
default:
|
||||
case "private":
|
||||
access = MemberAttributes.Private;
|
||||
break;
|
||||
case "public":
|
||||
access = MemberAttributes.Public;
|
||||
break;
|
||||
case "protected":
|
||||
access = MemberAttributes.Family;
|
||||
break;
|
||||
case "internal":
|
||||
case "notpublic": //WPF syntax
|
||||
access = MemberAttributes.Assembly;
|
||||
break;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.LogError(null, null, null, xamlFile.ItemSpec, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
|
||||
success = false;
|
||||
}
|
||||
|
||||
yield return new CodeMemberField {
|
||||
Name = name,
|
||||
Type = GetType(xmlType, node.GetNamespaceOfPrefix),
|
||||
Attributes = access,
|
||||
CustomAttributes = { GeneratedCodeAttrDecl }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static CodeTypeReference GetType(XmlType xmlType,
|
||||
Func<string, string> getNamespaceOfPrefix = null)
|
||||
{
|
||||
var type = xmlType.Name;
|
||||
var ns = GetClrNamespace(xmlType.NamespaceUri);
|
||||
if (ns != null)
|
||||
type = $"{ns}.{type}";
|
||||
|
||||
if (xmlType.TypeArguments != null)
|
||||
type = $"{type}`{xmlType.TypeArguments.Count}";
|
||||
|
||||
var returnType = new CodeTypeReference(type);
|
||||
if (ns != null)
|
||||
returnType.Options |= CodeTypeReferenceOptions.GlobalReference;
|
||||
|
||||
if (xmlType.TypeArguments != null)
|
||||
foreach (var typeArg in xmlType.TypeArguments)
|
||||
returnType.TypeArguments.Add(GetType(typeArg, getNamespaceOfPrefix));
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
static string GetClrNamespace(string namespaceuri)
|
||||
{
|
||||
if (namespaceuri == XamlParser.XFUri)
|
||||
return "Xamarin.Forms";
|
||||
if (namespaceuri == XamlParser.X2009Uri)
|
||||
return "System";
|
||||
if (namespaceuri != XamlParser.X2006Uri && !namespaceuri.Contains("clr-namespace"))
|
||||
throw new Exception($"Can't load types from xmlns {namespaceuri}");
|
||||
return XmlnsHelper.ParseNamespaceFromXmlns(namespaceuri);
|
||||
}
|
||||
|
||||
static string GetAttributeValue(XmlNode node, string localName, params string[] namespaceURIs)
|
||||
{
|
||||
if (node == null)
|
||||
throw new ArgumentNullException(nameof(node));
|
||||
if (localName == null)
|
||||
throw new ArgumentNullException(nameof(localName));
|
||||
if (namespaceURIs == null)
|
||||
throw new ArgumentNullException(nameof(namespaceURIs));
|
||||
foreach (var namespaceURI in namespaceURIs) {
|
||||
var attr = node.Attributes[localName, namespaceURI];
|
||||
if (attr == null)
|
||||
continue;
|
||||
return attr.Value;
|
||||
}
|
||||
return null;
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,355 @@
|
|||
using System;
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.CSharp;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Build.Tasks
|
||||
{
|
||||
class XamlGenerator
|
||||
{
|
||||
internal XamlGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public XamlGenerator(
|
||||
ITaskItem taskItem,
|
||||
string language,
|
||||
string assemblyName,
|
||||
string outputFile,
|
||||
TaskLoggingHelper logger)
|
||||
: this(
|
||||
taskItem.ItemSpec,
|
||||
language,
|
||||
taskItem.GetMetadata("ManifestResourceName"),
|
||||
taskItem.GetMetadata("TargetPath"),
|
||||
assemblyName,
|
||||
outputFile,
|
||||
logger)
|
||||
{
|
||||
}
|
||||
|
||||
static int generatedTypesCount;
|
||||
internal static CodeDomProvider Provider = new CSharpCodeProvider();
|
||||
|
||||
public string XamlFile { get; }
|
||||
public string Language { get; }
|
||||
public string ResourceId { get; }
|
||||
public string TargetPath { get; }
|
||||
public string AssemblyName { get; }
|
||||
public string OutputFile { get; }
|
||||
public TaskLoggingHelper Logger { get; }
|
||||
public string RootClrNamespace { get; private set; }
|
||||
public string RootType { get; private set; }
|
||||
bool GenerateDefaultCtor { get; set; }
|
||||
bool AddXamlCompilationAttribute { get; set; }
|
||||
bool HideFromIntellisense { get; set; }
|
||||
internal IEnumerable<CodeMemberField> NamedFields { get; set; }
|
||||
internal CodeTypeReference BaseType { get; set; }
|
||||
|
||||
public XamlGenerator(
|
||||
string xamlFile,
|
||||
string language,
|
||||
string resourceId,
|
||||
string targetPath,
|
||||
string assemblyName,
|
||||
string outputFile,
|
||||
TaskLoggingHelper logger = null)
|
||||
{
|
||||
XamlFile = xamlFile;
|
||||
Language = language;
|
||||
ResourceId = resourceId;
|
||||
TargetPath = targetPath;
|
||||
AssemblyName = assemblyName;
|
||||
OutputFile = outputFile;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
//returns true if a file is generated
|
||||
public bool Execute()
|
||||
{
|
||||
Logger?.LogMessage("Source: {0}", XamlFile);
|
||||
Logger?.LogMessage(" Language: {0}", Language);
|
||||
Logger?.LogMessage(" ResourceID: {0}", ResourceId);
|
||||
Logger?.LogMessage(" TargetPath: {0}", TargetPath);
|
||||
Logger?.LogMessage(" AssemblyName: {0}", AssemblyName);
|
||||
Logger?.LogMessage(" OutputFile {0}", OutputFile);
|
||||
|
||||
using (StreamReader reader = File.OpenText(XamlFile))
|
||||
if (!ParseXaml(reader))
|
||||
return false;
|
||||
|
||||
GenerateCode();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool ParseXaml(TextReader xaml)
|
||||
{
|
||||
var xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(xaml);
|
||||
|
||||
// if the following xml processing instruction is present
|
||||
//
|
||||
// <?xaml-comp compile="true" ?>
|
||||
//
|
||||
// we will generate a xaml.g.cs file with the default ctor calling InitializeComponent, and a XamlCompilation attribute
|
||||
var hasXamlCompilationProcessingInstruction = GetXamlCompilationProcessingInstruction(xmlDoc);
|
||||
|
||||
var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
|
||||
nsmgr.AddNamespace("__f__", XamlParser.XFUri);
|
||||
|
||||
var root = xmlDoc.SelectSingleNode("/*", nsmgr);
|
||||
if (root == null) {
|
||||
Logger?.LogWarning(" No root node found");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (XmlAttribute attr in root.Attributes) {
|
||||
if (attr.Name == "xmlns")
|
||||
nsmgr.AddNamespace("", attr.Value); //Add default xmlns
|
||||
if (attr.Prefix != "xmlns")
|
||||
continue;
|
||||
nsmgr.AddNamespace(attr.LocalName, attr.Value);
|
||||
}
|
||||
|
||||
var rootClass = root.Attributes["Class", XamlParser.X2006Uri]
|
||||
?? root.Attributes["Class", XamlParser.X2009Uri];
|
||||
|
||||
if (rootClass != null) {
|
||||
string rootType, rootNs, rootAsm, targetPlatform;
|
||||
XmlnsHelper.ParseXmlns(rootClass.Value, out rootType, out rootNs, out rootAsm, out targetPlatform);
|
||||
RootType = rootType;
|
||||
RootClrNamespace = rootNs;
|
||||
}
|
||||
else if (hasXamlCompilationProcessingInstruction) {
|
||||
RootClrNamespace = "__XamlGeneratedCode__";
|
||||
RootType = $"__Type{generatedTypesCount++}";
|
||||
GenerateDefaultCtor = true;
|
||||
AddXamlCompilationAttribute = true;
|
||||
HideFromIntellisense = true;
|
||||
}
|
||||
else { // rootClass == null && !hasXamlCompilationProcessingInstruction) {
|
||||
Logger?.LogMessage(" no x:Class on root element and no xaml-comp processing instruction: Skipping");
|
||||
return false;
|
||||
}
|
||||
|
||||
NamedFields = GetCodeMemberFields(root, nsmgr);
|
||||
var typeArguments = GetAttributeValue(root, "TypeArguments", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
var xmlType = new XmlType(root.NamespaceURI, root.LocalName, typeArguments != null ? TypeArgumentsParser.ParseExpression(typeArguments, nsmgr, null) : null);
|
||||
BaseType = GetType(xmlType, root.GetNamespaceOfPrefix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static CodeAttributeDeclaration GeneratedCodeAttrDecl =>
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(GeneratedCodeAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression("Xamarin.Forms.Build.Tasks.XamlG")),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression("0.0.0.0")));
|
||||
|
||||
void GenerateCode()
|
||||
{
|
||||
//Create the target directory if required
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(OutputFile));
|
||||
|
||||
if (RootType == null)
|
||||
throw new Exception("Something went wrong while executing XamlG");
|
||||
|
||||
var ccu = new CodeCompileUnit();
|
||||
ccu.AssemblyCustomAttributes.Add(
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlResourceIdAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(ResourceId)),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(TargetPath.Replace('\\', '/'))), //use forward slashes, paths are uris-like
|
||||
new CodeAttributeArgument(new CodeTypeOfExpression($"global::{RootClrNamespace}.{RootType}"))
|
||||
));
|
||||
var declNs = new CodeNamespace(RootClrNamespace);
|
||||
ccu.Namespaces.Add(declNs);
|
||||
|
||||
var declType = new CodeTypeDeclaration(RootType) {
|
||||
IsPartial = true,
|
||||
CustomAttributes = {
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlFilePathAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(XamlFile))),
|
||||
}
|
||||
};
|
||||
if (AddXamlCompilationAttribute)
|
||||
declType.CustomAttributes.Add(
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlCompilationAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodeSnippetExpression($"global::{typeof(XamlCompilationOptions).FullName}.Compile"))));
|
||||
if (HideFromIntellisense)
|
||||
declType.CustomAttributes.Add(
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(System.ComponentModel.EditorBrowsableAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodeSnippetExpression($"global::{typeof(System.ComponentModel.EditorBrowsableState).FullName}.{nameof(System.ComponentModel.EditorBrowsableState.Never)}"))));
|
||||
|
||||
declType.BaseTypes.Add(BaseType);
|
||||
|
||||
declNs.Types.Add(declType);
|
||||
|
||||
//Create a default ctor calling InitializeComponent
|
||||
if (GenerateDefaultCtor) {
|
||||
var ctor = new CodeConstructor {
|
||||
Attributes = MemberAttributes.Public,
|
||||
CustomAttributes = { GeneratedCodeAttrDecl },
|
||||
Statements = {
|
||||
new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "InitializeComponent")
|
||||
}
|
||||
};
|
||||
|
||||
declType.Members.Add(ctor);
|
||||
}
|
||||
|
||||
//Create InitializeComponent()
|
||||
var initcomp = new CodeMemberMethod {
|
||||
Name = "InitializeComponent",
|
||||
CustomAttributes = { GeneratedCodeAttrDecl }
|
||||
};
|
||||
|
||||
declType.Members.Add(initcomp);
|
||||
|
||||
//Create and initialize fields
|
||||
initcomp.Statements.Add(new CodeMethodInvokeExpression(
|
||||
new CodeTypeReferenceExpression(new CodeTypeReference($"global::{typeof(Extensions).FullName}")),
|
||||
"LoadFromXaml", new CodeThisReferenceExpression(), new CodeTypeOfExpression(declType.Name)));
|
||||
|
||||
foreach (var namedField in NamedFields) {
|
||||
declType.Members.Add(namedField);
|
||||
|
||||
var find_invoke = new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(
|
||||
new CodeTypeReferenceExpression(new CodeTypeReference($"global::{typeof(NameScopeExtensions).FullName}")),
|
||||
"FindByName", namedField.Type),
|
||||
new CodeThisReferenceExpression(), new CodePrimitiveExpression(namedField.Name));
|
||||
|
||||
CodeAssignStatement assign = new CodeAssignStatement(
|
||||
new CodeVariableReferenceExpression(namedField.Name), find_invoke);
|
||||
|
||||
initcomp.Statements.Add(assign);
|
||||
}
|
||||
|
||||
//write the result
|
||||
using (var writer = new StreamWriter(OutputFile))
|
||||
Provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions());
|
||||
}
|
||||
|
||||
static IEnumerable<CodeMemberField> GetCodeMemberFields(XmlNode root, XmlNamespaceManager nsmgr)
|
||||
{
|
||||
var xPrefix = nsmgr.LookupPrefix(XamlParser.X2006Uri) ?? nsmgr.LookupPrefix(XamlParser.X2009Uri);
|
||||
if (xPrefix == null)
|
||||
yield break;
|
||||
|
||||
XmlNodeList names =
|
||||
root.SelectNodes(
|
||||
"//*[@" + xPrefix + ":Name" +
|
||||
"][not(ancestor:: __f__:DataTemplate) and not(ancestor:: __f__:ControlTemplate) and not(ancestor:: __f__:Style)]", nsmgr);
|
||||
foreach (XmlNode node in names) {
|
||||
// Don't take the root canvas
|
||||
if (node == root)
|
||||
continue;
|
||||
var name = GetAttributeValue(node, "Name", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
var typeArguments = GetAttributeValue(node, "TypeArguments", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
var fieldModifier = GetAttributeValue(node, "FieldModifier", XamlParser.X2006Uri, XamlParser.X2009Uri);
|
||||
|
||||
var xmlType = new XmlType(node.NamespaceURI, node.LocalName,
|
||||
typeArguments != null
|
||||
? TypeArgumentsParser.ParseExpression(typeArguments, nsmgr, null)
|
||||
: null);
|
||||
|
||||
var access = MemberAttributes.Private;
|
||||
if (fieldModifier != null) {
|
||||
switch (fieldModifier.ToLowerInvariant()) {
|
||||
default:
|
||||
case "private":
|
||||
access = MemberAttributes.Private;
|
||||
break;
|
||||
case "public":
|
||||
access = MemberAttributes.Public;
|
||||
break;
|
||||
case "protected":
|
||||
access = MemberAttributes.Family;
|
||||
break;
|
||||
case "internal":
|
||||
case "notpublic": //WPF syntax
|
||||
access = MemberAttributes.Assembly;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yield return new CodeMemberField {
|
||||
Name = name,
|
||||
Type = GetType(xmlType, node.GetNamespaceOfPrefix),
|
||||
Attributes = access,
|
||||
CustomAttributes = { GeneratedCodeAttrDecl }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetXamlCompilationProcessingInstruction(XmlDocument xmlDoc)
|
||||
{
|
||||
var instruction = xmlDoc.SelectSingleNode("processing-instruction('xaml-comp')") as XmlProcessingInstruction;
|
||||
if (instruction == null)
|
||||
return false;
|
||||
|
||||
var parts = instruction.Data.Split(' ', '=');
|
||||
string compileValue = null;
|
||||
var indexOfCompile = Array.IndexOf(parts, "compile");
|
||||
if (indexOfCompile != -1)
|
||||
compileValue = parts[indexOfCompile + 1].Trim('"', '\'');
|
||||
return compileValue.Equals("true", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
static CodeTypeReference GetType(XmlType xmlType,
|
||||
Func<string, string> getNamespaceOfPrefix = null)
|
||||
{
|
||||
var type = xmlType.Name;
|
||||
var ns = GetClrNamespace(xmlType.NamespaceUri);
|
||||
if (ns != null)
|
||||
type = $"{ns}.{type}";
|
||||
|
||||
if (xmlType.TypeArguments != null)
|
||||
type = $"{type}`{xmlType.TypeArguments.Count}";
|
||||
|
||||
var returnType = new CodeTypeReference(type);
|
||||
if (ns != null)
|
||||
returnType.Options |= CodeTypeReferenceOptions.GlobalReference;
|
||||
|
||||
if (xmlType.TypeArguments != null)
|
||||
foreach (var typeArg in xmlType.TypeArguments)
|
||||
returnType.TypeArguments.Add(GetType(typeArg, getNamespaceOfPrefix));
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
static string GetClrNamespace(string namespaceuri)
|
||||
{
|
||||
if (namespaceuri == XamlParser.XFUri)
|
||||
return "Xamarin.Forms";
|
||||
if (namespaceuri == XamlParser.X2009Uri)
|
||||
return "System";
|
||||
if (namespaceuri != XamlParser.X2006Uri && !namespaceuri.Contains("clr-namespace"))
|
||||
throw new Exception($"Can't load types from xmlns {namespaceuri}");
|
||||
return XmlnsHelper.ParseNamespaceFromXmlns(namespaceuri);
|
||||
}
|
||||
|
||||
static string GetAttributeValue(XmlNode node, string localName, params string[] namespaceURIs)
|
||||
{
|
||||
if (node == null)
|
||||
throw new ArgumentNullException(nameof(node));
|
||||
if (localName == null)
|
||||
throw new ArgumentNullException(nameof(localName));
|
||||
if (namespaceURIs == null)
|
||||
throw new ArgumentNullException(nameof(namespaceURIs));
|
||||
foreach (var namespaceURI in namespaceURIs) {
|
||||
var attr = node.Attributes[localName, namespaceURI];
|
||||
if (attr == null)
|
||||
continue;
|
||||
return attr.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,7 +65,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
static class CecilExtensions
|
||||
{
|
||||
public static bool IsXaml(this EmbeddedResource resource, out string classname)
|
||||
public static bool IsXaml(this EmbeddedResource resource, ModuleDefinition module, out string classname)
|
||||
{
|
||||
classname = null;
|
||||
if (!resource.Name.EndsWith(".xaml", StringComparison.InvariantCulture))
|
||||
|
@ -83,11 +83,33 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
var rootClass = root.Attributes["Class", XamlParser.X2006Uri] ??
|
||||
root.Attributes["Class", XamlParser.X2009Uri];
|
||||
if (rootClass == null)
|
||||
return false;
|
||||
classname = rootClass.Value;
|
||||
return true;
|
||||
if (rootClass != null) {
|
||||
classname = rootClass.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
//no x:Class, but it might be a RD without x:Class and with <?xaml-comp compile="true" ?>
|
||||
//in that case, it has a XamlResourceIdAttribute
|
||||
var typeRef = GetTypeForResourceId(module, resource.Name);
|
||||
if (typeRef != null) {
|
||||
classname = typeRef.FullName;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static TypeReference GetTypeForResourceId(ModuleDefinition module, string resourceId)
|
||||
{
|
||||
foreach (var ca in module.GetCustomAttributes()) {
|
||||
if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference(typeof(XamlResourceIdAttribute))))
|
||||
continue;
|
||||
if (ca.ConstructorArguments[0].Value as string != resourceId)
|
||||
continue;
|
||||
return ca.ConstructorArguments[2].Value as TypeReference;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<?xaml-comp compile="true" ?>
|
||||
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
|
||||
<Style x:Key="foo" TargetType="Label">
|
||||
<Setter Property="TextColor" Value="Orange" />
|
||||
<Setter Property="Text" Value="Pumpkin" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Xamarin.Forms.Xaml.UnitTests.SharedResourceDictionary">
|
||||
|
|
|
@ -495,7 +495,7 @@
|
|||
<Compile Include="Issues\Bz59818.xaml.cs">
|
||||
<DependentUpon>Bz59818.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ResourceDictionaryWithSource.xaml.cs" >
|
||||
<Compile Include="ResourceDictionaryWithSource.xaml.cs">
|
||||
<DependentUpon>ResourceDictionaryWithSource.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
@ -924,6 +924,7 @@
|
|||
<EmbeddedResource Include="ResourceDictionaryWithSource.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="RDWithoutCodeBehind.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Build.Tasks;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
@ -27,13 +28,19 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
public void XamlFileShouldNotBeLockedAfterFileIsGenerated ()
|
||||
{
|
||||
string xamlInputFile = CreateXamlInputFile ();
|
||||
string xamlOutputFile = Path.ChangeExtension (xamlInputFile, ".xaml.g.cs");
|
||||
var generator = new XamlGTask ();
|
||||
generator.BuildEngine = new DummyBuildEngine ();
|
||||
generator.AssemblyName = "Test";
|
||||
generator.Language = "C#";
|
||||
var item = new TaskItem(xamlInputFile);
|
||||
item.SetMetadata("TargetPath", xamlInputFile);
|
||||
var generator = new XamlGTask() {
|
||||
BuildEngine= new DummyBuildEngine(),
|
||||
AssemblyName = "test",
|
||||
Language = "C#",
|
||||
XamlFiles = new[] { item},
|
||||
OutputPath = Path.GetDirectoryName(xamlInputFile),
|
||||
};
|
||||
|
||||
generator.Execute(new TaskItem(xamlInputFile), xamlOutputFile);
|
||||
generator.Execute();
|
||||
|
||||
string xamlOutputFile = generator.GeneratedCodeFiles.First().ItemSpec;
|
||||
File.Delete (xamlOutputFile);
|
||||
|
||||
Assert.DoesNotThrow (() => File.Delete (xamlInputFile));
|
||||
|
|
|
@ -24,22 +24,20 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
</View>";
|
||||
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.NotNull (rootType);
|
||||
Assert.NotNull (rootNs);
|
||||
Assert.NotNull (baseType);
|
||||
Assert.NotNull (codeMemberFields);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
Assert.NotNull(generator.RootType);
|
||||
Assert.NotNull(generator.RootClrNamespace);
|
||||
Assert.NotNull (generator.BaseType);
|
||||
Assert.NotNull (generator.NamedFields);
|
||||
|
||||
Assert.AreEqual ("CustomView", rootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests", rootNs);
|
||||
Assert.AreEqual ("Xamarin.Forms.View", baseType.BaseType);
|
||||
Assert.AreEqual (1, codeMemberFields.Count());
|
||||
Assert.AreEqual ("label0", codeMemberFields.First().Name);
|
||||
Assert.AreEqual ("Xamarin.Forms.Label", codeMemberFields.First().Type.BaseType);
|
||||
Assert.AreEqual ("CustomView", generator.RootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests", generator.RootClrNamespace);
|
||||
Assert.AreEqual ("Xamarin.Forms.View", generator.BaseType.BaseType);
|
||||
Assert.AreEqual (1, generator.NamedFields.Count());
|
||||
Assert.AreEqual ("label0", generator.NamedFields.First().Name);
|
||||
Assert.AreEqual ("Xamarin.Forms.Label", generator.NamedFields.First().Type.BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -53,22 +51,20 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
</View>";
|
||||
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.NotNull (rootType);
|
||||
Assert.NotNull (rootNs);
|
||||
Assert.NotNull (baseType);
|
||||
Assert.NotNull (codeMemberFields);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
Assert.NotNull(generator.RootType);
|
||||
Assert.NotNull(generator.RootClrNamespace);
|
||||
Assert.NotNull(generator.BaseType);
|
||||
Assert.NotNull(generator.NamedFields);
|
||||
|
||||
Assert.AreEqual ("CustomView", rootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests", rootNs);
|
||||
Assert.AreEqual ("Xamarin.Forms.View", baseType.BaseType);
|
||||
Assert.AreEqual (1, codeMemberFields.Count());
|
||||
Assert.AreEqual ("label0", codeMemberFields.First().Name);
|
||||
Assert.AreEqual ("Xamarin.Forms.Label", codeMemberFields.First().Type.BaseType);
|
||||
Assert.AreEqual("CustomView", generator.RootType);
|
||||
Assert.AreEqual("Xamarin.Forms.Xaml.UnitTests", generator.RootClrNamespace);
|
||||
Assert.AreEqual("Xamarin.Forms.View", generator.BaseType.BaseType);
|
||||
Assert.AreEqual(1, generator.NamedFields.Count());
|
||||
Assert.AreEqual("label0", generator.NamedFields.First().Name);
|
||||
Assert.AreEqual("Xamarin.Forms.Label", generator.NamedFields.First().Type.BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -90,14 +86,13 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
</ContentPage>";
|
||||
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.AreEqual (1, codeMemberFields.Count());
|
||||
Assert.AreEqual ("listView", codeMemberFields.First ().Name);
|
||||
Assert.AreEqual ("CustomListViewSample.CustomListView", codeMemberFields.First ().Type.BaseType);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.AreEqual (1, generator.NamedFields.Count());
|
||||
Assert.AreEqual ("listView", generator.NamedFields.First ().Name);
|
||||
Assert.AreEqual ("CustomListViewSample.CustomListView", generator.NamedFields.First ().Type.BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -119,14 +114,13 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
<Label x:Name=""included""/>
|
||||
</StackLayout>";
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.Contains ("included", codeMemberFields.Select(cmf => cmf.Name).ToList());
|
||||
Assert.False (codeMemberFields.Select(cmf => cmf.Name).Contains ("notincluded"));
|
||||
Assert.AreEqual (1, codeMemberFields.Count());
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.Contains ("included", generator.NamedFields.Select(cmf => cmf.Name).ToList());
|
||||
Assert.False (generator.NamedFields.Select(cmf => cmf.Name).Contains ("notincluded"));
|
||||
Assert.AreEqual (1, generator.NamedFields.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -149,13 +143,12 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
</StackLayout.Resources>
|
||||
</StackLayout>";
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.False (codeMemberFields.Select(cmf => cmf.Name).Contains ("notincluded"));
|
||||
Assert.AreEqual (0, codeMemberFields.Count());
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.False (generator.NamedFields.Select(cmf => cmf.Name).Contains ("notincluded"));
|
||||
Assert.AreEqual (0, generator.NamedFields.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -168,15 +161,14 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
x:TypeArguments=""x:String""
|
||||
/>";
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.AreEqual ("FooBar", rootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`1", baseType.BaseType);
|
||||
Assert.AreEqual (1, baseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("System.String", baseType.TypeArguments [0].BaseType);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.AreEqual("FooBar", generator.RootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`1", generator.BaseType.BaseType);
|
||||
Assert.AreEqual (1, generator.BaseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("System.String", generator.BaseType.TypeArguments [0].BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -189,16 +181,15 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
x:TypeArguments=""x:String,x:Int32""
|
||||
/>";
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.AreEqual ("FooBar", rootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", baseType.BaseType);
|
||||
Assert.AreEqual (2, baseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("System.String", baseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("System.Int32", baseType.TypeArguments [1].BaseType);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.AreEqual ("FooBar", generator.RootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", generator.BaseType.BaseType);
|
||||
Assert.AreEqual (2, generator.BaseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("System.String", generator.BaseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("System.Int32", generator.BaseType.TypeArguments [1].BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -211,16 +202,15 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
x:TypeArguments=""x:String, x:Int32""
|
||||
/>";
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.AreEqual ("FooBar", rootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", baseType.BaseType);
|
||||
Assert.AreEqual (2, baseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("System.String", baseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("System.Int32", baseType.TypeArguments [1].BaseType);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.AreEqual ("FooBar", generator.RootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", generator.BaseType.BaseType);
|
||||
Assert.AreEqual (2, generator.BaseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("System.String", generator.BaseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("System.Int32", generator.BaseType.TypeArguments [1].BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -236,16 +226,15 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
|
||||
/>";
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.AreEqual ("FooBar", rootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", baseType.BaseType);
|
||||
Assert.AreEqual (2, baseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.Interfaces.IDummyInterface", baseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.InterfacesTwo.IDummyInterfaceTwo", baseType.TypeArguments [1].BaseType);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.AreEqual ("FooBar", generator.RootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", generator.BaseType.BaseType);
|
||||
Assert.AreEqual (2, generator.BaseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.Interfaces.IDummyInterface", generator.BaseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.InterfacesTwo.IDummyInterfaceTwo", generator.BaseType.TypeArguments [1].BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -261,16 +250,15 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
|
||||
/>";
|
||||
var reader = new StringReader (xaml);
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.AreEqual ("FooBar", rootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", baseType.BaseType);
|
||||
Assert.AreEqual (2, baseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.Interfaces.IDummyInterface", baseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.InterfacesTwo.IDummyInterfaceTwo", baseType.TypeArguments [1].BaseType);
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.AreEqual ("FooBar", generator.RootType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Foo`2", generator.BaseType.BaseType);
|
||||
Assert.AreEqual (2, generator.BaseType.TypeArguments.Count);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.Interfaces.IDummyInterface", generator.BaseType.TypeArguments [0].BaseType);
|
||||
Assert.AreEqual ("Xamarin.Forms.Xaml.UnitTests.Bugzilla24258.InterfacesTwo.IDummyInterfaceTwo", generator.BaseType.TypeArguments [1].BaseType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -285,13 +273,12 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
<Label x:Name=""label0""/>
|
||||
</ContentPage>";
|
||||
using (var reader = new StringReader (xaml)) {
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml (reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.IsTrue (baseType.Options.HasFlag (CodeTypeReferenceOptions.GlobalReference));
|
||||
Assert.IsTrue (codeMemberFields.Select(cmf => cmf.Type).First ().Options.HasFlag (CodeTypeReferenceOptions.GlobalReference));
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.IsTrue (generator.BaseType.Options.HasFlag (CodeTypeReferenceOptions.GlobalReference));
|
||||
Assert.IsTrue (generator.NamedFields.Select(cmf => cmf.Type).First ().Options.HasFlag (CodeTypeReferenceOptions.GlobalReference));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,14 +299,13 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
|
||||
using (var reader = new StringReader(xaml))
|
||||
{
|
||||
string rootType, rootNs;
|
||||
CodeTypeReference baseType;
|
||||
IEnumerable<CodeMemberField> codeMemberFields;
|
||||
|
||||
XamlGTask.ParseXaml(reader, out rootType, out rootNs, out baseType, out codeMemberFields);
|
||||
Assert.That(codeMemberFields.First(cmf => cmf.Name == "privateLabel").Attributes, Is.EqualTo(MemberAttributes.Private));
|
||||
Assert.That(codeMemberFields.First(cmf => cmf.Name == "internalLabel").Attributes, Is.EqualTo(MemberAttributes.Assembly));
|
||||
Assert.That(codeMemberFields.First(cmf => cmf.Name == "publicLabel").Attributes, Is.EqualTo(MemberAttributes.Public));
|
||||
var generator = new XamlGenerator();
|
||||
generator.ParseXaml(reader);
|
||||
|
||||
Assert.That(generator.NamedFields.First(cmf => cmf.Name == "privateLabel").Attributes, Is.EqualTo(MemberAttributes.Private));
|
||||
Assert.That(generator.NamedFields.First(cmf => cmf.Name == "internalLabel").Attributes, Is.EqualTo(MemberAttributes.Assembly));
|
||||
Assert.That(generator.NamedFields.First(cmf => cmf.Name == "publicLabel").Attributes, Is.EqualTo(MemberAttributes.Public));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,17 +29,20 @@
|
|||
// Copyright 2013 Mobile Inception
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Mono.Options;
|
||||
using Xamarin.Forms.Build.Tasks;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
public class Xamlg
|
||||
{
|
||||
static readonly string HelpString = "xamlg.exe - a utility for generating partial classes from XAML.\n" +
|
||||
"xamlg.exe xamlfile[,outputfile]...\n\n" +
|
||||
"xamlg.exe xamlfile...\n\n" +
|
||||
"If an outputfile is not specified one will be created using the format <xamlfile>.g.cs\n\n";
|
||||
|
||||
public static void Main(string[] args)
|
||||
|
@ -69,18 +72,22 @@ namespace Xamarin.Forms.Xaml
|
|||
foreach (var file in extra)
|
||||
{
|
||||
var f = file;
|
||||
var n = "";
|
||||
|
||||
var sub = file.IndexOf(",", StringComparison.InvariantCulture);
|
||||
if (sub > 0)
|
||||
{
|
||||
n = f.Substring(sub + 1);
|
||||
f = f.Substring(0, sub);
|
||||
}
|
||||
else
|
||||
n = string.Concat(Path.GetFileName(f), ".g.", XamlGTask.Provider.FileExtension);
|
||||
var item = new TaskItem(f);
|
||||
item.SetMetadata("TargetPath", f);
|
||||
var generator = new XamlGTask() {
|
||||
BuildEngine = new DummyBuildEngine(),
|
||||
AssemblyName = "test",
|
||||
Language = "C#",
|
||||
XamlFiles = new[] { item },
|
||||
OutputPath = Path.GetDirectoryName(f),
|
||||
};
|
||||
|
||||
|
||||
new XamlGTask {
|
||||
XamlFiles = new[] { new TaskItem(f)}
|
||||
}.Execute();
|
||||
|
||||
XamlGTask.GenerateFile(f, f, f, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,4 +97,44 @@ namespace Xamarin.Forms.Xaml
|
|||
ops.WriteOptionDescriptions(Console.Out);
|
||||
}
|
||||
}
|
||||
|
||||
public class DummyBuildEngine : IBuildEngine
|
||||
{
|
||||
public void LogErrorEvent(BuildErrorEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public void LogWarningEvent(BuildWarningEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public void LogMessageEvent(BuildMessageEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public void LogCustomEvent(CustomBuildEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ContinueOnError {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public int LineNumberOfTaskNode {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
public int ColumnNumberOfTaskNode {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
public string ProjectFileOfTaskNode {
|
||||
get { return String.Empty; }
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче