diff --git a/src/Controls/src/SourceGen/CodeBehindCodeWriter.cs b/src/Controls/src/SourceGen/CodeBehindCodeWriter.cs index 94292cee8001..479ddbc0586a 100644 --- a/src/Controls/src/SourceGen/CodeBehindCodeWriter.cs +++ b/src/Controls/src/SourceGen/CodeBehindCodeWriter.cs @@ -53,9 +53,9 @@ public static string GenerateXamlCodeBehind(XamlProjectItemForCB? xamlItem, Comp var hintName = $"{(string.IsNullOrEmpty(Path.GetDirectoryName(projItem!.TargetPath)) ? "" : Path.GetDirectoryName(projItem.TargetPath) + Path.DirectorySeparatorChar)}{Path.GetFileNameWithoutExtension(projItem.TargetPath)}.{projItem.Kind.ToLowerInvariant()}.sg.cs".Replace(Path.DirectorySeparatorChar, '_'); - if (projItem.ManifestResourceName != null && projItem.TargetPath != null) + if (projItem.ManifestResourceName != null && projItem.RelativePath != null) { - sb.AppendLine($"[assembly: global::Microsoft.Maui.Controls.Xaml.XamlResourceId(\"{projItem.ManifestResourceName}\", \"{projItem.TargetPath.Replace('\\', '/')}\", {(rootType == null ? "null" : "typeof(global::" + rootClrNamespace + "." + rootType + ")")})]"); + sb.AppendLine($"[assembly: global::Microsoft.Maui.Controls.Xaml.XamlResourceId(\"{projItem.ManifestResourceName}\", \"{projItem.RelativePath.Replace('\\', '/')}\", {(rootType == null ? "null" : "typeof(global::" + rootClrNamespace + "." + rootType + ")")})]"); } if (XamlResourceIdOnly) @@ -71,9 +71,9 @@ public static string GenerateXamlCodeBehind(XamlProjectItemForCB? xamlItem, Comp var rootSymbol = compilation.GetTypeByMetadataName($"{rootClrNamespace}.{rootType}"); bool alreadyHasXamlCompilationAttribute = rootSymbol?.GetAttributes().Any(a => a.AttributeClass != null && a.AttributeClass.Equals(compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.XamlCompilationAttribute")!, SymbolEqualityComparer.Default)) ?? false; - var generateInflatorSwitch = compilation.AssemblyName == "Microsoft.Maui.Controls.Xaml.UnitTests"; + var generateInflatorSwitch = compilation.AssemblyName == "Microsoft.Maui.Controls.Xaml.UnitTests" && !generateDefaultCtor; var xamlInflators = projItem.Inflator; - + //if there's only the XamlC inflator, prevent non-assigned errors if (xamlInflators == XamlInflator.XamlC) sb.AppendLine("#pragma warning disable CS0649"); diff --git a/src/Controls/src/SourceGen/CodeBehindGenerator.cs b/src/Controls/src/SourceGen/CodeBehindGenerator.cs index e6b5aab713dc..0d8e5e99125c 100644 --- a/src/Controls/src/SourceGen/CodeBehindGenerator.cs +++ b/src/Controls/src/SourceGen/CodeBehindGenerator.cs @@ -246,7 +246,8 @@ static bool ShouldGenerateSourceGenInitializeComponent(XamlProjectItemForIC xaml ITypeSymbol xamlResIdAttr = compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.XamlResourceIdAttribute")!; INamedTypeSymbol? GetTypeForResourcePath(string resourcePath, IAssemblySymbol assembly) { - var attr = assembly.GetAttributes(xamlResIdAttr).FirstOrDefault(attr => (string)attr.ConstructorArguments[1].Value! == resourcePath); + //XRID use paths with forward slashes. we can't change that it's used by HR + var attr = assembly.GetAttributes(xamlResIdAttr).FirstOrDefault(attr => ((string)attr.ConstructorArguments[1].Value!).Replace('/', Path.DirectorySeparatorChar) == resourcePath); return attr?.ConstructorArguments[2].Value as INamedTypeSymbol; } diff --git a/src/Controls/src/SourceGen/InitializeComponentCodeWriter.cs b/src/Controls/src/SourceGen/InitializeComponentCodeWriter.cs index b015cd985be6..9c85f09b7c4d 100644 --- a/src/Controls/src/SourceGen/InitializeComponentCodeWriter.cs +++ b/src/Controls/src/SourceGen/InitializeComponentCodeWriter.cs @@ -38,6 +38,7 @@ PrePost newblock() => string accessModifier = "public"; INamedTypeSymbol? rootType = null; + var generatedDefaultCtor = false; if (root.Properties.TryGetValue(XmlName.xClass, out var classNode)) { if ((classNode as ValueNode)?.Value is not string rootClass) @@ -55,10 +56,12 @@ PrePost newblock() => } else { //no x:Class, but it can be an autogenerated type (starting with __Type, and with a XamlResourceId attribute) + generatedDefaultCtor = true; ITypeSymbol xamlResIdAttr = compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.XamlResourceIdAttribute")!; INamedTypeSymbol? GetTypeForResourcePath(string resourcePath, IAssemblySymbol assembly) { - var attr = assembly.GetAttributes(xamlResIdAttr).FirstOrDefault(attr => (string)attr.ConstructorArguments[1].Value! == resourcePath); + //XRID use paths with forward slashes. we can't change that it's used by HR + var attr = assembly.GetAttributes(xamlResIdAttr).FirstOrDefault(attr => ((string)attr.ConstructorArguments[1].Value!).Replace('/', Path.DirectorySeparatorChar) == resourcePath); return attr?.ConstructorArguments[2].Value as INamedTypeSymbol; } @@ -68,7 +71,8 @@ PrePost newblock() => if (rootType == null) goto exit; - var genSwitch = compilation.AssemblyName == "Microsoft.Maui.Controls.Xaml.UnitTests"; + + var genSwitch = compilation.AssemblyName == "Microsoft.Maui.Controls.Xaml.UnitTests" && !generatedDefaultCtor; var xamlInflators = xamlItem.ProjectItem.Inflator; var generate = (xamlInflators & XamlInflator.SourceGen) == XamlInflator.SourceGen; diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/ResourceDictionary.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/ResourceDictionary.cs new file mode 100644 index 000000000000..05c5d756eacc --- /dev/null +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/ResourceDictionary.cs @@ -0,0 +1,59 @@ +using System.Linq; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.SourceGen.UnitTests; + +public class ResourceDictionary : SourceGenXamlInitializeComponentTestBase +{ + [Test] + public void ResourceDictionaryWithoutXClass() + { + var xaml = +""" + + + #FF4B14 + +"""; + var expected = +""" + +//------------------------------------------------------------------------------ +// +// This code was generated by a .NET MAUI source generator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable + +namespace __XamlGeneratedCode__; + +public partial class __TypeDBD64C1C77CDA760 +{ + private partial void InitializeComponent() + { + var color = global::Microsoft.Maui.Graphics.Color.FromArgb("#FF4B14"); + global::Microsoft.Maui.VisualDiagnostics.RegisterSourceInfo(color!, new global::System.Uri(@"Styles.xaml;assembly=SourceGeneratorDriver.Generated", global::System.UriKind.Relative), 6, 4); + var __root = this; + global::Microsoft.Maui.VisualDiagnostics.RegisterSourceInfo(__root!, new global::System.Uri(@"Styles.xaml;assembly=SourceGeneratorDriver.Generated", global::System.UriKind.Relative), 2, 2); +#if !_MAUIXAML_SG_NAMESCOPE_DISABLE + global::Microsoft.Maui.Controls.Internals.INameScope iNameScope = new global::Microsoft.Maui.Controls.Internals.NameScope(); +#endif + __root.Add("AccentColor", color); + } +} + +"""; + + var (result, generated) = RunGenerator(xaml, "", path: "Styles.xaml"); + Assert.IsFalse(result.Diagnostics.Any()); + + Assert.AreEqual(expected, generated); + + } +} diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs index 887984940dde..3deaba327972 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs @@ -14,12 +14,12 @@ public class SourceGenXamlInitializeComponentTestBase : SourceGenTestsBase protected record AdditionalXamlFile(string Path, string Content, string? RelativePath = null, string? TargetPath = null, string? ManifestResourceName = null, string? TargetFramework = null, string? NoWarn = null) : AdditionalFile(Text: ToAdditionalText(Path, Content), Kind: "Xaml", RelativePath: RelativePath ?? Path, TargetPath: TargetPath, ManifestResourceName: ManifestResourceName, TargetFramework: TargetFramework, NoWarn: NoWarn); - protected (GeneratorDriverRunResult result, string? text) RunGenerator(string xaml, string code, string noWarn = "", string targetFramework = "") + protected (GeneratorDriverRunResult result, string? text) RunGenerator(string xaml, string code, string noWarn = "", string targetFramework = "", string? path = null) { var compilation = CreateMauiCompilation(); compilation = compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); var workingDirectory = Environment.CurrentDirectory; - var xamlFile = new AdditionalXamlFile(Path.Combine(workingDirectory, "Test.xaml"), xaml, RelativePath: "Test.xaml", TargetFramework: targetFramework, NoWarn: noWarn); + var xamlFile = new AdditionalXamlFile(Path.Combine(workingDirectory, path ?? "Test.xaml"), xaml, RelativePath: path ?? "Test.xaml", TargetFramework: targetFramework, NoWarn: noWarn, ManifestResourceName: $"{compilation.AssemblyName}.Test.xaml"); var result = RunGenerator(compilation, xamlFile); var generated = result.Results.SingleOrDefault().GeneratedSources.SingleOrDefault(gs => gs.HintName.EndsWith(".xsg.cs")).SourceText?.ToString(); diff --git a/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.rt.xaml b/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.rt.xaml new file mode 100644 index 000000000000..1d3f438a83d4 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.rt.xaml @@ -0,0 +1,27 @@ + + + #FF4B14 + #99253748 + #253748 + #F8F8F8 + #ED0241 + #ACB1B4 + #203446 + #FFFFFF + #368F95 + + #FF96F3 + #1976D2 + #96d1ff + #FAFAFA + #C0C0C0 + #4d4d4d + #999999 + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.sgen.xaml b/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.sgen.xaml new file mode 100644 index 000000000000..1d3f438a83d4 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.sgen.xaml @@ -0,0 +1,27 @@ + + + #FF4B14 + #99253748 + #253748 + #F8F8F8 + #ED0241 + #ACB1B4 + #203446 + #FFFFFF + #368F95 + + #FF96F3 + #1976D2 + #96d1ff + #FAFAFA + #C0C0C0 + #4d4d4d + #999999 + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.xc.xaml b/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.xc.xaml new file mode 100644 index 000000000000..1d3f438a83d4 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/AppResources/Colors.xc.xaml @@ -0,0 +1,27 @@ + + + #FF4B14 + #99253748 + #253748 + #F8F8F8 + #ED0241 + #ACB1B4 + #203446 + #FFFFFF + #368F95 + + #FF96F3 + #1976D2 + #96d1ff + #FAFAFA + #C0C0C0 + #4d4d4d + #999999 + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj b/src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj index 291df11afb77..d94f31522672 100644 --- a/src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj +++ b/src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj @@ -14,7 +14,6 @@ - $(DefineConstants);FIXME_BEFORE_PUBLIC_RELEASE true Generated diff --git a/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml b/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml index 32645e01336a..9c9f26851263 100644 --- a/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml +++ b/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml @@ -8,6 +8,9 @@ + + + diff --git a/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml.cs b/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml.cs index 6488b7b641db..8ab6713cccf5 100644 --- a/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/ResourceDictionaryWithSource.xaml.cs @@ -32,17 +32,51 @@ public void RelativeAndAbsoluteURI([Values] XamlInflator inflator) } [Test] - public void XRIDIsGeneratedForRDWithoutCodeBehind() + public void CanLoadInflatedResources([Values] XamlInflator from, [Values] XamlInflator rd) { + var layout = new ResourceDictionaryWithSource(from); + var key = "Colors"; + switch (rd) + { + case XamlInflator.Runtime: + key = "Colors.rt"; + break; + case XamlInflator.SourceGen: + key = "Colors.sgen"; + break; + case XamlInflator.XamlC: + key = "Colors.xc"; + break; + } + var rdLoaded = layout.Resources[key] as ResourceDictionary; + Assert.That(rdLoaded, Is.Not.Null); + Assert.That(rdLoaded["MediumGrayTextColor"], Is.TypeOf()); + Assert.That(rdLoaded["MediumGrayTextColor"] as Color, Is.EqualTo(Color.Parse("#ff4d4d4d"))); + } + + [Test] + public void XRIDIsGeneratedForRDWithoutCodeBehind([Values] XamlInflator rd) + { + var path = "AppResources/Colors.xaml"; + switch (rd) + { + case XamlInflator.Runtime: + path = "AppResources/Colors.rt.xaml"; + break; + case XamlInflator.SourceGen: + path = "AppResources/Colors.sgen.xaml"; + break; + case XamlInflator.XamlC: + path = "AppResources/Colors.xc.xaml"; + break; + } + var asm = typeof(ResourceDictionaryWithSource).Assembly; - var resourceId = XamlResourceIdAttribute.GetResourceIdForPath(asm, "AppResources/Colors.rtxc.xaml"); + var resourceId = XamlResourceIdAttribute.GetResourceIdForPath(asm, path); Assert.That(resourceId, Is.Not.Null); var type = XamlResourceIdAttribute.GetTypeForResourceId(asm, resourceId); - Assert.That(type?.Name, Does.StartWith("__Type"), "xaml-comp default to true, this should have a type associated with it"); + Assert.That(type?.Name, Does.StartWith("__Type"), "We add a type for all RD without Class, this should have a type associated with it"); -#if !FIXME_BEFORE_PUBLIC_RELEASE - Assert.Fail(); //make sure to have the same default for sourcegen -#endif } [Test]