Skip to content

Commit 6fa6de2

Browse files
michaelstonisglennawatson
authored andcommitted
Feature xamarin essentials events (#1682)
Provides functionality to generate event mapping for Xamarin.Essentials
1 parent b175ef0 commit 6fa6de2

17 files changed

+295
-22
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using EventBuilder.Entities;
2+
using Mono.Cecil;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace EventBuilder.Cecil
7+
{
8+
public static class StaticEventTemplateInformation
9+
{
10+
private static string GetEventArgsTypeForEvent(EventDefinition ei)
11+
{
12+
// Find the EventArgs type parameter of the event via digging around via reflection
13+
var type = ei.EventType.Resolve();
14+
var invoke = type.Methods.First(x => x.Name == "Invoke");
15+
if (invoke.Parameters.Count < 1) return null;
16+
17+
var param = invoke.Parameters.Count == 1 ? invoke.Parameters[0] : invoke.Parameters[1];
18+
var ret = param.ParameterType.FullName;
19+
20+
var generic = ei.EventType as GenericInstanceType;
21+
if (generic != null)
22+
{
23+
foreach (
24+
var kvp in
25+
type.GenericParameters.Zip(generic.GenericArguments, (name, actual) => new {name, actual}))
26+
{
27+
var realType = GetRealTypeName(kvp.actual);
28+
29+
ret = ret.Replace(kvp.name.FullName, realType);
30+
}
31+
}
32+
33+
// NB: Inner types in Mono.Cecil get reported as 'Foo/Bar'
34+
return ret.Replace('/', '.');
35+
}
36+
37+
private static string GetRealTypeName(TypeDefinition t)
38+
{
39+
if (t.GenericParameters.Count == 0) return t.FullName;
40+
41+
var ret = string.Format("{0}<{1}>",
42+
t.Namespace + "." + t.Name,
43+
string.Join(",", t.GenericParameters.Select(x => GetRealTypeName(x.Resolve()))));
44+
45+
// NB: Inner types in Mono.Cecil get reported as 'Foo/Bar'
46+
return ret.Replace('/', '.');
47+
}
48+
49+
private static string GetRealTypeName(TypeReference t)
50+
{
51+
var generic = t as GenericInstanceType;
52+
if (generic == null) return t.FullName;
53+
54+
var ret = string.Format("{0}<{1}>",
55+
generic.Namespace + "." + generic.Name,
56+
string.Join(",", generic.GenericArguments.Select(x => GetRealTypeName(x))));
57+
58+
// NB: Inner types in Mono.Cecil get reported as 'Foo/Bar'
59+
return ret.Replace('/', '.');
60+
}
61+
62+
private static EventDefinition[] GetPublicEvents(TypeDefinition t)
63+
{
64+
return
65+
t.Events
66+
67+
.Where(x =>
68+
{
69+
return x.AddMethod.IsPublic && GetEventArgsTypeForEvent(x) != null;
70+
})
71+
.ToArray();
72+
}
73+
74+
public static NamespaceInfo[] Create(AssemblyDefinition[] targetAssemblies)
75+
{
76+
var publicTypesWithEvents = targetAssemblies
77+
.SelectMany(x => SafeTypes.GetSafeTypes(x))
78+
.Where(x => x.IsPublic && !x.HasGenericParameters)
79+
.Select(x => new {Type = x, Events = GetPublicEvents(x)})
80+
.Where(x => x.Events.Length > 0)
81+
.ToArray();
82+
83+
var garbageNamespaceList = new[]
84+
{
85+
"ReactiveUI.Events"
86+
};
87+
88+
var namespaceData = publicTypesWithEvents
89+
.GroupBy(x => x.Type.Namespace)
90+
.Where(x => !garbageNamespaceList.Contains(x.Key))
91+
.Select(x => new NamespaceInfo
92+
{
93+
Name = x.Key,
94+
Types = x.Select(y => new PublicTypeInfo
95+
{
96+
Name = y.Type.Name,
97+
Type = y.Type,
98+
Events = y.Events.Select(z => new PublicEventInfo
99+
{
100+
Name = z.Name,
101+
EventHandlerType = GetRealTypeName(z.EventType),
102+
EventArgsType = GetEventArgsTypeForEvent(z)
103+
}).ToArray()
104+
}).ToArray()
105+
}).ToArray();
106+
107+
foreach (var type in namespaceData.SelectMany(x => x.Types))
108+
{
109+
var parentWithEvents = GetParents(type.Type).FirstOrDefault(x => GetPublicEvents(x).Any());
110+
if (parentWithEvents == null)
111+
continue;
112+
113+
type.Parent = new ParentInfo {Name = parentWithEvents.FullName};
114+
}
115+
116+
return namespaceData;
117+
}
118+
119+
private static IEnumerable<TypeDefinition> GetParents(TypeDefinition type)
120+
{
121+
var current = type.BaseType != null && type.BaseType.ToString() != "System.Object"
122+
? type.BaseType.Resolve()
123+
: null;
124+
125+
while (current != null)
126+
{
127+
yield return current.Resolve();
128+
129+
current = current.BaseType != null
130+
? current.BaseType.Resolve()
131+
: null;
132+
}
133+
}
134+
}
135+
}

src/EventBuilder/CommandLineOptions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public enum AutoPlatform
1818
WPF,
1919
XamForms,
2020
UWP,
21-
Winforms
21+
Winforms,
22+
Essentials
2223
}
2324

2425
public class CommandLineOptions
Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
1-
<Project Sdk="MSBuild.Sdk.Extras">
2-
3-
<PropertyGroup>
4-
<OutputType>Exe</OutputType>
5-
<TargetFrameworks>net461</TargetFrameworks>
6-
<AssemblyName>EventBuilder</AssemblyName>
7-
<RootNamespace>EventBuilder</RootNamespace>
8-
</PropertyGroup>
9-
10-
11-
<ItemGroup>
12-
<Content Include="DefaultTemplate.mustache">
13-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
14-
</Content>
15-
</ItemGroup>
16-
17-
<ItemGroup>
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net461</TargetFramework>
5+
<AssemblyName>EventBuilder</AssemblyName>
6+
<RootNamespace>EventBuilder</RootNamespace>
7+
</PropertyGroup>
8+
9+
10+
<ItemGroup>
11+
<Content Include="DefaultTemplate.mustache">
12+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
13+
</Content>
14+
<Content Include="XamarinEssentialsTemplate.mustache">
15+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
16+
</Content>
17+
</ItemGroup>
18+
19+
<ItemGroup>
1820
<PackageReference Include="CommandLineParser" Version="1.9.71" />
1921
<PackageReference Include="Microsoft.Web.Xdt" Version="2.1.1" />
2022
<PackageReference Include="Mono.Cecil" Version="0.9.6.1" />
21-
<PackageReference Include="NuGet.Core" Version="2.10.1" />
2223
<PackageReference Include="Nustache" Version="1.15.3.7" />
2324
<PackageReference Include="Polly" Version="3.0.0" />
2425
<PackageReference Include="Serilog" Version="1.5.14" />
26+
<PackageReference Include="NuGet.Core" Version="2.14.0" />
2527
</ItemGroup>
2628
</Project>

src/EventBuilder/Platforms/Android.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace EventBuilder.Platforms
99
{
1010
public class Android : BasePlatform
1111
{
12+
public override AutoPlatform Platform => AutoPlatform.Android;
13+
1214
public Android(string referenceAssembliesLocation)
1315
{
1416
if (PlatformHelper.IsRunningOnMono()) {

src/EventBuilder/Platforms/BasePlatform.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66

77
namespace EventBuilder.Platforms
88
{
9-
public class BasePlatform : IPlatform
9+
public abstract class BasePlatform : IPlatform
1010
{
1111
public BasePlatform()
1212
{
1313
Assemblies = new List<string>();
1414
CecilSearchDirectories = new List<string>();
1515
}
1616

17+
public abstract AutoPlatform Platform { get; }
18+
1719
public List<string> Assemblies { get; set; }
1820
public List<string> CecilSearchDirectories { get; set; }
1921
}

src/EventBuilder/Platforms/Bespoke.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ namespace EventBuilder.Platforms
66
{
77
public class Bespoke : BasePlatform
88
{
9+
public override AutoPlatform Platform => AutoPlatform.None;
910
}
1011
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using NuGet;
2+
using Polly;
3+
using Serilog;
4+
using System;
5+
using System.IO;
6+
using System.Linq;
7+
8+
namespace EventBuilder.Platforms
9+
{
10+
public class Essentials : BasePlatform
11+
{
12+
private const string _packageName = "Xamarin.Essentials";
13+
14+
public override AutoPlatform Platform => AutoPlatform.Essentials;
15+
16+
public Essentials()
17+
{
18+
var packageUnzipPath = Environment.CurrentDirectory;
19+
20+
var retryPolicy = Policy
21+
.Handle<Exception>()
22+
.WaitAndRetry(
23+
5,
24+
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
25+
(exception, timeSpan, context) =>
26+
{
27+
Log.Warning(
28+
"An exception was thrown whilst retrieving or installing {packageName}: {exception}",
29+
_packageName, exception);
30+
});
31+
32+
retryPolicy.Execute(() =>
33+
{
34+
var repo = PackageRepositoryFactory.Default.CreateRepository("https://packages.nuget.org/api/v2");
35+
var packageManager = new PackageManager(repo, packageUnzipPath);
36+
var fpid = packageManager.SourceRepository.FindPackagesById(_packageName);
37+
var package = fpid.Single(x => x.Version.ToString() == "0.9.1-preview");
38+
39+
packageManager.InstallPackage(package, true, true);
40+
41+
Log.Debug("Using Xamarin Essentials {Version} released on {Published}", package.Version, package.Published);
42+
Log.Debug("{ReleaseNotes}", package.ReleaseNotes);
43+
});
44+
45+
var xamarinForms =
46+
Directory.GetFiles(packageUnzipPath,
47+
"Xamarin.Essentials.dll", SearchOption.AllDirectories);
48+
49+
var latestVersion = xamarinForms.First(x => x.Contains("netstandard1.0"));
50+
Assemblies.Add(latestVersion);
51+
52+
if (PlatformHelper.IsRunningOnMono())
53+
{
54+
CecilSearchDirectories.Add(
55+
@"/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/xbuild-frameworks/.NETPortable/v4.5/Profile/Profile111");
56+
}
57+
else
58+
{
59+
CecilSearchDirectories.Add(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile111");
60+
}
61+
}
62+
}
63+
}

src/EventBuilder/Platforms/IPlatform.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace EventBuilder.Platforms
88
{
99
public interface IPlatform
1010
{
11+
AutoPlatform Platform { get; }
12+
1113
List<string> Assemblies { get; set; }
1214

1315
// Cecil when run on Mono needs some direction as to the location of the platform specific MSCORLIB.

src/EventBuilder/Platforms/Mac.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ namespace EventBuilder.Platforms
1010
// ReSharper disable once InconsistentNaming
1111
public class Mac : BasePlatform
1212
{
13+
public override AutoPlatform Platform => AutoPlatform.Mac;
14+
1315
public Mac(string referenceAssembliesLocation)
1416
{
1517
if (PlatformHelper.IsRunningOnMono()) {

src/EventBuilder/Platforms/Tizen.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ public class Tizen : BasePlatform
1515
{
1616
private const string _packageName = "Tizen.NET";
1717

18+
public override AutoPlatform Platform => AutoPlatform.Tizen;
19+
1820
public Tizen()
1921
{
2022
var packageUnzipPath = Environment.CurrentDirectory;

0 commit comments

Comments
 (0)