diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets index cf289da4f98..aeb5c93f523 100644 --- a/build-tools/installers/create-installers.targets +++ b/build-tools/installers/create-installers.targets @@ -261,6 +261,7 @@ <_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.CSharp.targets" ExcludeFromAndroidNETSdk="true" /> <_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.D8.targets" /> <_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Designer.targets" /> + <_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Resource.Designer.targets" /> <_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.DesignTime.targets" /> <_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.DX.targets" ExcludeFromAndroidNETSdk="true" /> <_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.EmbeddedResource.targets" /> diff --git a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets index 751cd82eaee..56f418029da 100644 --- a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets +++ b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets @@ -155,7 +155,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. <_Aapt2ProguardRules Condition=" '$(AndroidLinkTool)' != '' ">$(IntermediateOutputPath)aapt_rules.txt + + + + + + + True + False + <_GenerateResourceDesignerAssemblyOutput>$(IntermediateOutputPath)Xamarin.Android.Resource.Designer.dll + + + + + <_DesignerNamespaces Include="$(RootNamespace)" /> + + + + + + + true + + + + + + + + $(_GenerateResourceDesignerAssembly) + + + + + + \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index 418f95bd059..f99550826c4 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -121,6 +121,7 @@ projects, these properties are set in Xamarin.Android.Legacy.targets. _CheckForDeletedResourceFile; _ComputeAndroidResourcePaths; _UpdateAndroidResgen; + _GenerateResourceDesignerAssembly; _CreateAar; diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs new file mode 100644 index 00000000000..36c54d6c6fd --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs @@ -0,0 +1,184 @@ +// Copyright (C) 2011 Xamarin, Inc. All rights reserved. +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using System.IO; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Android.Build.Tasks; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Xamarin.Android.Tasks +{ + public class GenerateResourceDesignerAssembly : AndroidTask + { + const string DesignerAssemblyName = "Xamarin.Android.Resource.Designer"; + public override string TaskPrefix => "GRDA"; + + [Required] + public ITaskItem RTxtFile { get; set; } + + public ITaskItem ResourceMap { get; set; } + + [Required] + public bool IsApplication { get; set; } + + [Required] + public bool DesignTimeBuild { get; set; } + + [Required] + public ITaskItem OutputFile { get; set; } + + [Required] + public string TargetFrameworkVersion { get; set; } + + [Required] + public string TargetFrameworkIdentifier { get; set; } + + [Required] + public bool UsingAndroidNETSdk { get; set; } + + public string[] Namespaces { get; set; } + + public override bool RunTask () + { + // Generate an assembly which contains all the values in the provided + // R.txt file. + var assembly = AssemblyDefinition.CreateAssembly ( + new AssemblyNameDefinition (DesignerAssemblyName, new Version ()), + DesignerAssemblyName, + ModuleKind.Dll); + + var module = assembly.MainModule; + + if (!IsApplication) { + MethodReference referenceAssemblyConstructor = module.ImportReference ( typeof (ReferenceAssemblyAttribute).GetConstructor (Type.EmptyTypes)); + module.Assembly.CustomAttributes.Add (new CustomAttribute (referenceAssemblyConstructor)); + } + + MethodReference targetFrameworkConstructor = module.ImportReference (typeof (TargetFrameworkAttribute).GetConstructor(new [] { typeof (string) })); + var attr = new CustomAttribute (targetFrameworkConstructor); + attr.ConstructorArguments.Add (new CustomAttributeArgument (module.TypeSystem.String, $"{TargetFrameworkIdentifier},Version={TargetFrameworkVersion}")); + module.Assembly.CustomAttributes.Add (attr); + + if (UsingAndroidNETSdk) { + // add .net 6 specific attributes we might not need these. + // TargetPlatform + // SupportedOSPlatform + } + + if (!UsingAndroidNETSdk) + module.AssemblyReferences.Add(AssemblyNameReference.Parse("mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e")); + module.AssemblyReferences.Add(AssemblyNameReference.Parse("Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065")); + module.AssemblyReferences.Add(AssemblyNameReference.Parse("System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e")); + + var att = TypeAttributes.Class | TypeAttributes.Public; + + var resourceDesigner = new TypeDefinition( + DesignerAssemblyName, + "Resource", + att, + module.TypeSystem.Object); + module.Types.Add(resourceDesigner); + + ProcessRtxtFile (RTxtFile.ItemSpec, resourceDesigner, module); + + foreach (var ns in (Namespaces ?? Array.Empty ())) { + module.Types.Add (new TypeDefinition (ns, "Resource", att, resourceDesigner)); + } + + assembly.Write (OutputFile.ItemSpec); + return !Log.HasLoggedErrors; + } + + void ProcessRtxtFile (string file, TypeDefinition resourceDesigner, ModuleDefinition module) + { + var lines = System.IO.File.ReadLines (file); + foreach (var line in lines) { + var items = line.Split (new char [] { ' ' }, 4); + int value = items [1] != "styleable" ? Convert.ToInt32 (items [3], 16) : -1; + string itemName = items [2]; + switch (items [1]) { + case "anim": + case "animator": + case "attr": + case "array": + case "bool": + case "color": + case "dimen": + case "drawable": + case "font": + case "id": + case "integer": + case "interpolator": + case "layout": + case "menu": + case "mipmap": + case "plurals": + case "raw": + case "string": + case "style": + case "transition": + case "xml": + CreateIntProperty (items [1], itemName, value, resourceDesigner, module); + break; + // case "styleable": + // switch (items [0]) { + // case "int": + // CreateIntField (styleable, itemName, Convert.ToInt32 (items [3], 10)); + // break; + // case "int[]": + // var arrayValues = items [3].Trim (new char [] { '{', '}' }) + // .Replace (" ", "") + // .Split (new char [] { ',' }); + // CreateIntArrayField (styleable, itemName, arrayValues.Length, + // arrayValues.Select (x => string.IsNullOrEmpty (x) ? -1 : Convert.ToInt32 (x, 16)).ToArray ()); + // break; + // } + // break; + // for custom views + default: + CreateIntProperty (items [1], itemName, value, resourceDesigner, module); + break; + } + } + } + + void CreateIntProperty (string resourceClass, string propertyName, int value, TypeDefinition resourceDesigner, ModuleDefinition module) + { + TypeDefinition nestedType = CreateResourceClass (resourceDesigner, resourceClass, module); + PropertyDefinition p = CreateProperty (propertyName, value, module); + nestedType.Properties.Add (p); + nestedType.Methods.Add (p.GetMethod); + } + + Dictionary resourceClasses = new Dictionary (); + + TypeDefinition CreateResourceClass (TypeDefinition resourceDesigner, string className, ModuleDefinition module) + { + string name = ResourceParser.GetNestedTypeName (className); + if (resourceClasses.ContainsKey (name)) { + return resourceClasses[name]; + } + var resourceClass = new TypeDefinition (DesignerAssemblyName, name, TypeAttributes.Class | TypeAttributes.Public, module.TypeSystem.Object); + resourceDesigner.NestedTypes.Add(resourceClass); + resourceClasses [name] = resourceClass; + return resourceClass; + } + + PropertyDefinition CreateProperty (string propertyName, int value, ModuleDefinition module) + { + var p = new PropertyDefinition (propertyName, PropertyAttributes.None, module.TypeSystem.Int32); + var getter = new MethodDefinition ($"{propertyName}_get", MethodAttributes.Public | MethodAttributes.Static, module.TypeSystem.Int32); + p.GetMethod = getter; + p.SetMethod = null; + var il = p.GetMethod.Body.GetILProcessor (); + il.Emit (OpCodes.Ldc_I4, value); + il.Emit (OpCodes.Ret); + return p; + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssemblies.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssemblies.cs index 55c7bf7d804..a0a3587ee20 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssemblies.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssemblies.cs @@ -86,6 +86,7 @@ bool Execute (DirectoryAssemblyResolver res) { // Put every assembly we'll need in the resolver foreach (var assembly in ResolvedAssemblies) { + Log.LogDebugMessage ($"Preloading {assembly.ItemSpec}"); res.Load (Path.GetFullPath (assembly.ItemSpec)); } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets index 5ff1dba9842..53bd2e1f03a 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets @@ -114,6 +114,10 @@ PreserveNewest Xamarin.Android.Designer.targets + + PreserveNewest + Xamarin.Android.Resource.Designer.targets + PreserveNewest Xamarin.Android.Aapt.targets diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index e705656efd5..ca69b3d79b1 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -359,6 +359,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Legacy.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Legacy.targets index 687a26d96ff..20f2bff89da 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Legacy.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Legacy.targets @@ -157,6 +157,7 @@ projects. .NET 5 projects will not import this file. _CheckForDeletedResourceFile; _ComputeAndroidResourcePaths; _UpdateAndroidResgen; + _GenerateResourceDesignerAssembly; _CreateManagedLibraryResourceArchive; @@ -298,6 +299,10 @@ projects. .NET 5 projects will not import this file. Include="$(OutDir)$(TargetFileName)" Condition="Exists ('$(OutDir)$(TargetFileName)')" /> +