Skip to content

Commit 585ab90

Browse files
committed
[Xamarin.Android.Build.Tasks] Fixed F# Resource Designer compilation issues
The latest release of F# removed support for static fields. As a result the Resource.Designer.fs file that wes being generated by our build system no longer compiled. This commit reworks the Designer.cs code to emit a C# assembly for F# projects rather than just the code. This has the benifit of us not having to produce F# code in the first place. The new system works as follows. 1) GenerateResourceDesigner Task generates a Resources.Designer.cs file as normal 2) If the target project is F# that file is then compiled into a Resource.Designer.dll 3) The new .dll is then included in the @ReferencePaths ItemGroup 4) The Resource.Designer.cs is removed from the @complie ItemGroup If the project is C# or VS the Designer file is just included as normal.
1 parent ac5269d commit 585ab90

File tree

3 files changed

+91
-38
lines changed

3 files changed

+91
-38
lines changed

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

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ public class GenerateResourceDesigner : Task
3838

3939
public ITaskItem[] References { get; set; }
4040

41+
[Required]
42+
public string IntermediateOutputPath { get; set; }
43+
44+
[Output]
45+
public bool BuildResourceAssembly { get; set; }
46+
47+
[Output]
48+
public string IntermediateDesignerFile { get; set; }
49+
4150
private Dictionary<string, string> resource_fixup = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
4251

4352
public override bool Execute ()
@@ -98,6 +107,14 @@ public override bool Execute ()
98107
bool isFSharp = string.Equals (language, "F#", StringComparison.OrdinalIgnoreCase);
99108
bool isCSharp = string.Equals (language, "C#", StringComparison.OrdinalIgnoreCase);
100109

110+
if (isFSharp) {
111+
language = "C#";
112+
isCSharp = true;
113+
NetResgenOutputFile = Path.Combine (IntermediateOutputPath, "Resource.Designer.cs");
114+
IntermediateDesignerFile = NetResgenOutputFile;
115+
}
116+
117+
BuildResourceAssembly = isFSharp;
101118
// Let VB put this in the default namespace
102119
if (isVB)
103120
Namespace = string.Empty;
@@ -125,31 +142,25 @@ public override bool Execute ()
125142
.CreateImportMethods (assemblies);
126143
}
127144

128-
AdjustConstructor (isFSharp, resources);
145+
AdjustConstructor (resources);
129146
foreach (var member in resources.Members)
130147
if (member is CodeTypeDeclaration)
131-
AdjustConstructor (isFSharp, (CodeTypeDeclaration) member);
148+
AdjustConstructor ((CodeTypeDeclaration) member);
132149

133150
// Write out our Resources.Designer.cs file
134151

135-
WriteFile (NetResgenOutputFile, resources, language, isFSharp, isCSharp);
152+
WriteFile (NetResgenOutputFile, resources, language, isCSharp, isFSharp);
153+
154+
Log.LogDebugMessage ($"[Output] BuildResourceAssembly : {BuildResourceAssembly}");
155+
Log.LogDebugMessage ($"[Output] IntermediateDesignerFile : {IntermediateDesignerFile}");
136156

137157
return !Log.HasLoggedErrors;
138158
}
139159

140160
// Remove private constructor in F#.
141161
// Add static constructor. (but ignored in F#)
142-
void AdjustConstructor (bool isFSharp, CodeTypeDeclaration type)
162+
void AdjustConstructor (CodeTypeDeclaration type)
143163
{
144-
if (isFSharp) {
145-
foreach (CodeTypeMember tm in type.Members) {
146-
if (tm is CodeConstructor) {
147-
type.Members.Remove (tm);
148-
break;
149-
}
150-
}
151-
}
152-
153164
var staticCtor = new CodeTypeConstructor () { Attributes = MemberAttributes.Static };
154165
staticCtor.Statements.Add (
155166
new CodeExpressionStatement (
@@ -162,11 +173,9 @@ void AdjustConstructor (bool isFSharp, CodeTypeDeclaration type)
162173
type.Members.Add (staticCtor);
163174
}
164175

165-
private void WriteFile (string file, CodeTypeDeclaration resources, string language, bool isFSharp, bool isCSharp)
176+
private void WriteFile (string file, CodeTypeDeclaration resources, string language, bool isCSharp, bool isFSharp)
166177
{
167-
CodeDomProvider provider =
168-
isFSharp ? new FSharp.Compiler.CodeDom.FSharpCodeProvider () :
169-
CodeDomProvider.CreateProvider (language);
178+
CodeDomProvider provider = CodeDomProvider.CreateProvider (language);
170179

171180
string code = null;
172181
using (var o = new StringWriter ()) {
@@ -182,6 +191,24 @@ private void WriteFile (string file, CodeTypeDeclaration resources, string langu
182191
if (resources != null)
183192
ns.Types.Add (resources);
184193

194+
if (isFSharp) {
195+
foreach (CodeTypeMember member in resources.Members) {
196+
var codeType = member as CodeTypeDeclaration;
197+
if (codeType == null || !codeType.IsClass)
198+
continue;
199+
var dec = new CodeTypeDeclaration (resources.Name + "_" + member.Name);
200+
dec.IsClass = true;
201+
dec.IsPartial = true;
202+
dec.TypeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Sealed;
203+
dec.BaseTypes.Add (new CodeTypeReference (resources.Name + "." + member.Name));
204+
var codeAttrDecl = new CodeAttributeDeclaration (
205+
"System.ObsoleteAttribute",
206+
new CodeAttributeArgument (new CodePrimitiveExpression ($"You should use {resources.Name}.{member.Name} instead")));
207+
dec.CustomAttributes.Add (codeAttrDecl);
208+
ns.Types.Add (dec);
209+
}
210+
}
211+
185212
var unit = new CodeCompileUnit ();
186213
unit.Namespaces.Add (ns);
187214

@@ -201,23 +228,6 @@ private void WriteFile (string file, CodeTypeDeclaration resources, string langu
201228
provider.GenerateCodeFromCompileUnit(new CodeSnippetCompileUnit("#pragma warning restore 1591"), o, options);
202229

203230
code = o.ToString ();
204-
205-
// post-processing for F#
206-
if (isFSharp) {
207-
code = code.Replace ("\r\n", "\n");
208-
while (true) {
209-
int skipLen = " = class".Length;
210-
int idx = code.IndexOf (" = class");
211-
if (idx < 0)
212-
break;
213-
int end = code.IndexOf (" end");
214-
string head = code.Substring (0, idx);
215-
string mid = end < 0 ? code.Substring (idx) : code.Substring (idx + skipLen, end - idx - skipLen);
216-
string last = end < 0 ? null : code.Substring (end + " end".Length);
217-
code = head + @" () =
218-
static do Android.Runtime.ResourceIdManager.UpdateIdValues()" + mid + "\n" + last;
219-
}
220-
}
221231
}
222232

223233
var temp_o = Path.Combine (Path.GetDirectoryName (file), "__" + Path.GetFileName (file) + ".new");

src/Xamarin.Android.Build.Tasks/Utilities/JavaResourceParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ static KeyValuePair<Regex, Func<Match, bool, CodeTypeDeclaration, Dictionary<str
7474
TypeAttributes = TypeAttributes.Public,
7575
};
7676
t.Members.Add (new CodeConstructor () {
77-
Attributes = MemberAttributes.Private,
77+
Attributes = MemberAttributes.Family,
7878
});
7979
g.Members.Add (t);
8080
return g;

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

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
466466
UpdateAndroidResources;
467467
$(ApplicationResolveReferencesDependsOn);
468468
</ResolveReferencesDependsOn>
469+
<ResolveAssemblyReferencesDependsOn>
470+
_GenerateResourceDesignerStub;
471+
$(ResolveAssemblyReferencesDependsOn)
472+
</ResolveAssemblyReferencesDependsOn>
469473
</PropertyGroup>
470474

471475
<PropertyGroup Condition="'$(AndroidApplication)' != '' And $(AndroidApplication)">
@@ -992,8 +996,31 @@ because xbuild doesn't support framework reference assemblies.
992996
<!-- Resource Build -->
993997

994998
<Target Name="UpdateAndroidResources"
995-
DependsOnTargets="$(CoreResolveReferencesDependsOn);_CheckForDeletedResourceFile;_ComputeAndroidResourcePaths;_UpdateAndroidResgen;_AddLibraryProjectsEmbeddedResourceToProject;_GenerateJavaDesignerForComponent" />
996-
999+
DependsOnTargets="$(CoreResolveReferencesDependsOn);_CheckForDeletedResourceFile;_ComputeAndroidResourcePaths;_UpdateAndroidResgen;_UseCompiledDesignerAssembly;_AddLibraryProjectsEmbeddedResourceToProject;_GenerateJavaDesignerForComponent" />
1000+
1001+
1002+
<Target Name="_GenerateResourceDesignerStub" Condition=" !Exists ('$(IntermediateOutputPath)Resource.Designer.dll') ">
1003+
<Touch Files="$(IntermediateOutputPath)\Resource.Designer.stub.cs" AlwaysCreate="True" />
1004+
<Csc
1005+
KeyFile="$(KeyFile)"
1006+
KeyContainer="$(KeyContainer)"
1007+
NoStandardLib="False"
1008+
TargetType="library"
1009+
Sources="$(IntermediateOutputPath)\Resource.Designer.stub.cs"
1010+
OutputAssembly="$(IntermediateOutputPath)Resource.Designer.dll"
1011+
/>
1012+
<ItemGroup>
1013+
<Reference Include="$(IntermediateOutputPath)Resource.Designer.dll" />
1014+
</ItemGroup>
1015+
</Target>
1016+
1017+
<Target Name="_UseCompiledDesignerAssembly"
1018+
Condition=" Exists ('$(IntermediateOutputPath)Resource.Designer.dll') And '$(_BuildResourceAssembly)' == 'true' ">
1019+
<ItemGroup>
1020+
<ReferencePath Include="$(IntermediateOutputPath)Resource.Designer.dll" />
1021+
<Compile Remove="$(_AndroidResourceDesignerFile)" />
1022+
</ItemGroup>
1023+
</Target>
9971024
<!-- Handle a case where the designer file has been deleted, but the flag file still exists -->
9981025
<Target Name="_CheckForDeletedResourceFile">
9991026
<Delete Files="$(_AndroidResgenFlagFile)"
@@ -1301,7 +1328,11 @@ because xbuild doesn't support framework reference assemblies.
13011328
AdditionalResourceDirectories="@(LibraryResourceDirectories)"
13021329
IsApplication="$(AndroidApplication)"
13031330
References="@(ReferencePath)"
1304-
/>
1331+
IntermediateOutputPath="$(IntermediateOutputPath)"
1332+
>
1333+
<Output TaskParameter="BuildResourceAssembly" PropertyName="_BuildResourceAssembly" />
1334+
<Output TaskParameter="IntermediateDesignerFile" PropertyName="_IntermediateDesignerFile" />
1335+
</GenerateResourceDesigner>
13051336

13061337
<!-- Only copy if the file contents changed, so users only get Reload? dialog for real changes -->
13071338
<CopyIfChanged
@@ -1312,6 +1343,16 @@ because xbuild doesn't support framework reference assemblies.
13121343

13131344
<!-- Delete our temporary directory -->
13141345
<RemoveDirFixed Directories="$(ResgenTemporaryDirectory)" />
1346+
1347+
<Csc Condition=" '$(_BuildResourceAssembly)' == 'true' "
1348+
KeyFile="$(KeyFile)"
1349+
KeyContainer="$(KeyContainer)"
1350+
NoStandardLib="True"
1351+
TargetType="library"
1352+
References="@(ReferencePath);@(ReferenceDependencyPaths);$(_XATargetFrameworkDirectories)\Java.Interop.dll;$(_XATargetFrameworkDirectories)\mscorlib.dll"
1353+
Sources="$(_IntermediateDesignerFile)"
1354+
OutputAssembly="$(IntermediateOutputPath)Resource.Designer.dll"
1355+
/>
13151356

13161357
<!-- If there are no _AndroidResource items, create a blank file -->
13171358
<CreateAndroidResourceStamp
@@ -2500,6 +2541,8 @@ because xbuild doesn't support framework reference assemblies.
25002541
<Delete Files="$(_AndroidLibraryImportsCache)" />
25012542
<Delete Files="$(_AndroidStaticResourcesFlag)" />
25022543
<Delete Files="$(_AndroidLibraryProjectImportsCache)" />
2544+
<Delete Files="$(IntermediateOutputPath)Resource.Designer.dll" />
2545+
<Delete Files="$(IntermediateOutputPath)\Resource.Designer.stub.cs" />
25032546
</Target>
25042547

25052548
<Target Name="_CollectMonoAndroidOutputs" DependsOnTargets="_ValidateAndroidPackageProperties">

0 commit comments

Comments
 (0)