Skip to content

Commit

Permalink
(GH-17) Create assembly loader to support dynamic addins
Browse files Browse the repository at this point in the history
  • Loading branch information
AdmiringWorm committed Jul 19, 2020
1 parent fc22d28 commit fb4bb1a
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
159 changes: 159 additions & 0 deletions Cake.Issues.Recipe/Content/loader/AddinData.cake
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System.Reflection;
using Cake.Core.Annotations;

public abstract class AddinData
{
protected AddinData(ICakeContext context, string packageName, string packageVersion, string assemblyName = null)
{
this.Initialize(context, packageName, packageVersion, assemblyName);
}

public Assembly AddinAssembly { get; private set; }
public IEnumerable<Type> DeclaredEnums { get; private set; }
public IEnumerable<MethodInfo> DefinedAliases { get; private set; }
public IEnumerable<TypeInfo> DefinedClasses { get; private set; }
public IEnumerable<MethodInfo> DefinedMethods { get; private set; }

protected void Initialize(ICakeContext context, string packageName, string packageVersion, string assemblyName = null)
{
if (string.IsNullOrEmpty(assemblyName))
{
assemblyName = packageName;
}

var assembly = LoadAddinAssembly(context, packageName, packageVersion, assemblyName);

if (assembly is null)
{
return;
}

AddinAssembly = assembly;

var declaredEnums = new List<Type>();
var definedClasses = new List<TypeInfo>();

foreach (var ti in assembly.DefinedTypes.Where(ti => ti.IsPublic))
{
if (ti.IsEnum)
{
declaredEnums.Add(ti.AsType());
EnumerationFound(context, ti.AsType());
}
else if(ti.IsClass && (!ti.IsAbstract || ti.IsStatic()) && !ti.IsGenericTypeDefinition)
{
definedClasses.Add(ti);
ClassFound(context, ti);
ParseClass(context, ti);
}
}

DeclaredEnums = declaredEnums;
DefinedClasses = definedClasses;
}

protected virtual void AliasFound(ICakeContext context, MethodInfo method)
{
}

protected virtual void ClassFound(ICakeContext context, TypeInfo classTypeInfo)
{
}

protected void ParseClass(ICakeContext context, TypeInfo classTypeInfo)
{
var aliases = new List<MethodInfo>();
var methods = new List<MethodInfo>();

foreach (var mi in classTypeInfo.DeclaredMethods.Where(i => i.IsPublic))
{
if (mi.GetCustomAttribute<CakeMethodAliasAttribute>() != null)
{
aliases.Add(mi);
AliasFound(context, mi);
}
else
{
methods.Add(mi);
MethodFound(context, mi);
}
}

DefinedAliases = aliases;
DefinedMethods = methods;
}

protected virtual void EnumerationFound(ICakeContext context, Type enumType)
{
}

protected virtual void MethodFound(ICakeContext context, MethodInfo method)
{
}

private static Assembly LoadAddinAssembly(ICakeContext context, string packageName, string packageVersion, string assemblyName)
{
var dllPath = GetAssemblyPath(context, packageName, packageVersion, assemblyName);

if (dllPath is null)
{
var script = $"#tool nuget:?package={packageName}&version={packageVersion}&prerelease";
var tempFile = Guid.NewGuid() + ".cake";

System.IO.File.WriteAllText(tempFile, script);

try
{
context.CakeExecuteScript(tempFile);
}
finally
{
if (context.FileExists(tempFile))
{
context.DeleteFile(tempFile);
}
}
}

dllPath = GetAssemblyPath(context, packageName, packageVersion, assemblyName);

if (dllPath is null)
{
context.Warning("Unable to find path to the {0} package assembly!", packageName);
return null;
}

var assembly = Assembly.LoadFrom(dllPath.FullPath);
return assembly;
}

private static FilePath GetAssemblyPath(ICakeContext context, string packageName, string packageVersion, string assemblyName)
{
FilePath dllPath = null;
const string pathFormat = "{0}.{1}/**/{2}*/{3}.dll";

var possibleFrameworks = new List<String>();

if (context.Environment.Runtime.IsCoreClr)
{
possibleFrameworks.Add("netcoreapp");
possibleFrameworks.Add("net4"); // TODO: Remove this after debugging
}
else
{
possibleFrameworks.Add("net4");
}
possibleFrameworks.Add("netstandard");

foreach (var framework in possibleFrameworks)
{
dllPath = context.Tools.Resolve(string.Format(pathFormat, packageName, packageVersion, framework, assemblyName));
if (dllPath is null)
dllPath = context.Tools.Resolve(string.Format(pathFormat, packageName, "*", framework, assemblyName));
if (dllPath is object)
break;
}

return dllPath;
}
}
1 change: 1 addition & 0 deletions Cake.Issues.Recipe/Content/loader/loader.cake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#load ./AddinData.cake

0 comments on commit fb4bb1a

Please sign in to comment.