Skip to content

Commit 0148ce9

Browse files
committed
First Pass at using Resource Designer assembly [WIP]
Context #6310
1 parent b1180c8 commit 0148ce9

File tree

9 files changed

+261
-1
lines changed

9 files changed

+261
-1
lines changed

build-tools/installers/create-installers.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@
261261
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.CSharp.targets" ExcludeFromAndroidNETSdk="true" />
262262
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.D8.targets" />
263263
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Designer.targets" />
264+
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Resource.Designer.targets" />
264265
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.DesignTime.targets" />
265266
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.DX.targets" ExcludeFromAndroidNETSdk="true" />
266267
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.EmbeddedResource.targets" />

src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
155155
<_Aapt2ProguardRules Condition=" '$(AndroidLinkTool)' != '' ">$(IntermediateOutputPath)aapt_rules.txt</_Aapt2ProguardRules>
156156
</PropertyGroup>
157157
<Aapt2Link
158-
Condition=" '$(_AndroidResourceDesignerFile)' != '' "
158+
Condition=" '$(_AndroidResourceDesignerFile)' != '' Or '$(AndroidUseDesignerAssembly)' == 'True' "
159159
ContinueOnError="$(DesignTimeBuild)"
160160
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
161161
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<!--
2+
***********************************************************************************************
3+
Xamarin.Android.Resource.Designer.targets
4+
5+
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
6+
created a backup copy. Incorrect changes to this file will make it
7+
impossible to load or build your projects from the command-line or the IDE.
8+
9+
This file imports the version- and platform-specific targets for the project importing
10+
this file. This file also defines targets to produce an error if the specified targets
11+
file does not exist, but the project is built anyway (command-line or IDE build).
12+
13+
Copyright (C) 2016 Xamarin. All rights reserved.
14+
***********************************************************************************************
15+
-->
16+
17+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
18+
19+
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateResourceDesignerAssembly" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
20+
21+
<PropertyGroup>
22+
<AndroidUseDesignerAssembly Condition=" '$(AndroidUseIntermediateDesignerFile)' == 'False' And '$(AndroidUseDesignerAssembly)' == '' ">True</AndroidUseDesignerAssembly>
23+
<AndroidUseDesignerAssembly Condition=" '$(AndroidUseDesignerAssembly)' == '' ">False</AndroidUseDesignerAssembly>
24+
<_GenerateResourceDesignerAssemblyOutput>$(IntermediateOutputPath)Xamarin.Android.Resource.Designer.dll</_GenerateResourceDesignerAssemblyOutput>
25+
</PropertyGroup>
26+
27+
<Target Name="_GenerateResourceDesignerAssembly"
28+
Inputs="$(IntermediateOutputPath)R.txt"
29+
Outputs="$(_GenerateResourceDesignerAssemblyOutput)"
30+
Condition=" '$(AndroidUseDesignerAssembly)' == 'True' And Exists ('$(IntermediateOutputPath)R.txt') ">
31+
<ItemGroup>
32+
<_DesignerNamespaces Include="$(RootNamespace)" />
33+
</ItemGroup>
34+
<GenerateResourceDesignerAssembly
35+
ContinueOnError="$(DesignTimeBuild)"
36+
RTxtFile="$(IntermediateOutputPath)R.txt"
37+
IsApplication="$(AndroidApplication)"
38+
DesignTimeBuild="$(DesignTimeBuild)"
39+
OutputFile="$(_GenerateResourceDesignerAssemblyOutput)"
40+
Namespaces="@(_DesignerNamespaces)"
41+
TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)"
42+
TargetFrameworkVersion="$(TargetFrameworkVersion)"
43+
UsingAndroidNETSdk="$(UsingAndroidNETSdk)"
44+
>
45+
</GenerateResourceDesignerAssembly>
46+
<ItemGroup>
47+
<FileWrites Include="$(_GenerateResourceDesignerAssemblyOutput)" />
48+
<ReferencePath Include="$(_GenerateResourceDesignerAssemblyOutput)">
49+
<CopyLocal>true</CopyLocal>
50+
</ReferencePath>
51+
<Compile Remove="$(_AndroidResourceDesignerFile)" />
52+
</ItemGroup>
53+
</Target>
54+
55+
<PropertyGroup>
56+
<BuildResourceDesignerDependsOn>
57+
$(_GenerateResourceDesignerAssembly)
58+
</BuildResourceDesignerDependsOn>
59+
</PropertyGroup>
60+
61+
<Target Name="BuildResourceDesigner" Condition=" '$(AndroidUseDesignerAssembly)' == 'True' "
62+
DependsOnTargets="$(BuildResourceDesignerDependsOn)" />
63+
64+
</Project>

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ projects, these properties are set in Xamarin.Android.Legacy.targets.
121121
_CheckForDeletedResourceFile;
122122
_ComputeAndroidResourcePaths;
123123
_UpdateAndroidResgen;
124+
_GenerateResourceDesignerAssembly;
124125
_CreateAar;
125126
</_UpdateAndroidResourcesDependsOn>
126127
<CompileDependsOn>
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright (C) 2011 Xamarin, Inc. All rights reserved.
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.Versioning;
6+
using System.IO;
7+
using System.Linq;
8+
using Microsoft.Build.Framework;
9+
using Microsoft.Build.Utilities;
10+
using Microsoft.Android.Build.Tasks;
11+
using Mono.Cecil;
12+
using Mono.Cecil.Cil;
13+
14+
namespace Xamarin.Android.Tasks
15+
{
16+
public class GenerateResourceDesignerAssembly : AndroidTask
17+
{
18+
const string DesignerAssemblyName = "Xamarin.Android.Resource.Designer";
19+
public override string TaskPrefix => "GRDA";
20+
21+
[Required]
22+
public ITaskItem RTxtFile { get; set; }
23+
24+
public ITaskItem ResourceMap { get; set; }
25+
26+
[Required]
27+
public bool IsApplication { get; set; }
28+
29+
[Required]
30+
public bool DesignTimeBuild { get; set; }
31+
32+
[Required]
33+
public ITaskItem OutputFile { get; set; }
34+
35+
[Required]
36+
public string TargetFrameworkVersion { get; set; }
37+
38+
[Required]
39+
public string TargetFrameworkIdentifier { get; set; }
40+
41+
public bool UsingAndroidNETSdk { get; set; } = false;
42+
43+
public string[] Namespaces { get; set; }
44+
45+
public override bool RunTask ()
46+
{
47+
// Generate an assembly which contains all the values in the provided
48+
// R.txt file.
49+
var assembly = AssemblyDefinition.CreateAssembly (
50+
new AssemblyNameDefinition (DesignerAssemblyName, new Version ()),
51+
DesignerAssemblyName,
52+
ModuleKind.Dll);
53+
54+
var module = assembly.MainModule;
55+
56+
if (!IsApplication) {
57+
MethodReference referenceAssemblyConstructor = module.ImportReference ( typeof (ReferenceAssemblyAttribute).GetConstructor (Type.EmptyTypes));
58+
module.Assembly.CustomAttributes.Add (new CustomAttribute (referenceAssemblyConstructor));
59+
}
60+
61+
MethodReference targetFrameworkConstructor = module.ImportReference (typeof (TargetFrameworkAttribute).GetConstructor(new [] { typeof (string) }));
62+
var attr = new CustomAttribute (targetFrameworkConstructor);
63+
attr.ConstructorArguments.Add (new CustomAttributeArgument (module.TypeSystem.String, $"{TargetFrameworkIdentifier},Version={TargetFrameworkVersion}"));
64+
module.Assembly.CustomAttributes.Add (attr);
65+
66+
if (UsingAndroidNETSdk) {
67+
// add .net 6 specific attributes we might not need these.
68+
// TargetPlatform
69+
// SupportedOSPlatform
70+
}
71+
72+
if (!UsingAndroidNETSdk)
73+
module.AssemblyReferences.Add(AssemblyNameReference.Parse("mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"));
74+
module.AssemblyReferences.Add(AssemblyNameReference.Parse("Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065"));
75+
module.AssemblyReferences.Add(AssemblyNameReference.Parse("System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"));
76+
77+
var att = TypeAttributes.Class | TypeAttributes.Public;
78+
79+
var resourceDesigner = new TypeDefinition(
80+
DesignerAssemblyName,
81+
"Resource",
82+
att,
83+
module.TypeSystem.Object);
84+
module.Types.Add(resourceDesigner);
85+
86+
ProcessRtxtFile (RTxtFile.ItemSpec, resourceDesigner, module);
87+
88+
foreach (var ns in (Namespaces ?? Array.Empty<string> ())) {
89+
module.Types.Add (new TypeDefinition (ns, "Resource", att, resourceDesigner));
90+
}
91+
92+
assembly.Write (OutputFile.ItemSpec);
93+
return !Log.HasLoggedErrors;
94+
}
95+
96+
void ProcessRtxtFile (string file, TypeDefinition resourceDesigner, ModuleDefinition module)
97+
{
98+
var lines = System.IO.File.ReadLines (file);
99+
foreach (var line in lines) {
100+
var items = line.Split (new char [] { ' ' }, 4);
101+
int value = items [1] != "styleable" ? Convert.ToInt32 (items [3], 16) : -1;
102+
string itemName = items [2];
103+
switch (items [1]) {
104+
case "anim":
105+
case "animator":
106+
case "attr":
107+
case "array":
108+
case "bool":
109+
case "color":
110+
case "dimen":
111+
case "drawable":
112+
case "font":
113+
case "id":
114+
case "integer":
115+
case "interpolator":
116+
case "layout":
117+
case "menu":
118+
case "mipmap":
119+
case "plurals":
120+
case "raw":
121+
case "string":
122+
case "style":
123+
case "transition":
124+
case "xml":
125+
CreateIntProperty (items [1], itemName, value, resourceDesigner, module);
126+
break;
127+
// case "styleable":
128+
// switch (items [0]) {
129+
// case "int":
130+
// CreateIntField (styleable, itemName, Convert.ToInt32 (items [3], 10));
131+
// break;
132+
// case "int[]":
133+
// var arrayValues = items [3].Trim (new char [] { '{', '}' })
134+
// .Replace (" ", "")
135+
// .Split (new char [] { ',' });
136+
// CreateIntArrayField (styleable, itemName, arrayValues.Length,
137+
// arrayValues.Select (x => string.IsNullOrEmpty (x) ? -1 : Convert.ToInt32 (x, 16)).ToArray ());
138+
// break;
139+
// }
140+
// break;
141+
// for custom views
142+
default:
143+
CreateIntProperty (items [1], itemName, value, resourceDesigner, module);
144+
break;
145+
}
146+
}
147+
}
148+
149+
void CreateIntProperty (string resourceClass, string propertyName, int value, TypeDefinition resourceDesigner, ModuleDefinition module)
150+
{
151+
TypeDefinition nestedType = CreateResourceClass (resourceDesigner, resourceClass, module);
152+
PropertyDefinition p = CreateProperty (propertyName, value, module);
153+
nestedType.Properties.Add (p);
154+
nestedType.Methods.Add (p.GetMethod);
155+
}
156+
157+
Dictionary<string, TypeDefinition> resourceClasses = new Dictionary<string, TypeDefinition> ();
158+
159+
TypeDefinition CreateResourceClass (TypeDefinition resourceDesigner, string className, ModuleDefinition module)
160+
{
161+
string name = ResourceParser.GetNestedTypeName (className);
162+
if (resourceClasses.ContainsKey (name)) {
163+
return resourceClasses[name];
164+
}
165+
var resourceClass = new TypeDefinition (DesignerAssemblyName, name, TypeAttributes.Class | TypeAttributes.Public, module.TypeSystem.Object);
166+
resourceDesigner.NestedTypes.Add(resourceClass);
167+
resourceClasses [name] = resourceClass;
168+
return resourceClass;
169+
}
170+
171+
PropertyDefinition CreateProperty (string propertyName, int value, ModuleDefinition module)
172+
{
173+
var p = new PropertyDefinition (propertyName, PropertyAttributes.None, module.TypeSystem.Int32);
174+
var getter = new MethodDefinition ($"{propertyName}_get", MethodAttributes.Public | MethodAttributes.Static, module.TypeSystem.Int32);
175+
p.GetMethod = getter;
176+
p.SetMethod = null;
177+
var il = p.GetMethod.Body.GetILProcessor ();
178+
il.Emit (OpCodes.Ldc_I4, value);
179+
il.Emit (OpCodes.Ret);
180+
return p;
181+
}
182+
}
183+
}

src/Xamarin.Android.Build.Tasks/Tasks/LinkAssemblies.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ bool Execute (DirectoryAssemblyResolver res)
8686
{
8787
// Put every assembly we'll need in the resolver
8888
foreach (var assembly in ResolvedAssemblies) {
89+
Log.LogDebugMessage ($"Preloading {assembly.ItemSpec}");
8990
res.Load (Path.GetFullPath (assembly.ItemSpec));
9091
}
9192

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@
114114
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
115115
<Link>Xamarin.Android.Designer.targets</Link>
116116
</None>
117+
<None Include="MSBuild\Xamarin\Android\Xamarin.Android.Resource.Designer.targets">
118+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
119+
<Link>Xamarin.Android.Resource.Designer.targets</Link>
120+
</None>
117121
<None Include="MSBuild\Xamarin\Android\Xamarin.Android.Aapt.targets">
118122
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
119123
<Link>Xamarin.Android.Aapt.targets</Link>

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
359359
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.Aapt2.targets" Condition=" '$(AndroidUseAapt2)' == 'true' or '$(UsingAndroidNETSdk)' == 'true' " />
360360
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.DesignTime.targets" />
361361
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.EmbeddedResource.targets" />
362+
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.Resource.Designer.targets" />
362363
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.SkipCases.projitems" />
363364
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.Tooling.targets" />
364365
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.Legacy.targets" Condition=" '$(UsingAndroidNETSdk)' != 'True' " />

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Legacy.targets

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ projects. .NET 5 projects will not import this file.
157157
_CheckForDeletedResourceFile;
158158
_ComputeAndroidResourcePaths;
159159
_UpdateAndroidResgen;
160+
_GenerateResourceDesignerAssembly;
160161
_CreateManagedLibraryResourceArchive;
161162
</_UpdateAndroidResourcesDependsOn>
162163
<GetAndroidDependenciesDependsOn>
@@ -298,6 +299,10 @@ projects. .NET 5 projects will not import this file.
298299
Include="$(OutDir)$(TargetFileName)"
299300
Condition="Exists ('$(OutDir)$(TargetFileName)')"
300301
/>
302+
<FilteredAssemblies
303+
Include="$(_GenerateResourceDesignerAssemblyOutput)"
304+
Condition="Exists ('$(_GenerateResourceDesignerAssemblyOutput)')"
305+
/>
301306
<FilteredAssemblies
302307
Include="@(ReferenceCopyLocalPaths)"
303308
Condition="'%(ReferenceCopyLocalPaths.ResolvedFrom)' != 'ImplicitlyExpandDesignTimeFacades' And '%(ReferenceCopyLocalPaths.Extension)' == '.dll' And '%(ReferenceCopyLocalPaths.RelativeDir)' == '' And Exists('%(ReferenceCopyLocalPaths.Identity)') "

0 commit comments

Comments
 (0)