-
-
Notifications
You must be signed in to change notification settings - Fork 173
/
Copy pathContextAwareTask.cs
111 lines (95 loc) · 4.59 KB
/
ContextAwareTask.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
namespace MSBuildExtensionTask
{
using System;
using System.IO;
using System.Linq;
using System.Reflection;
#if NETCOREAPP2_0
using System.Runtime.Loader;
#endif
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public abstract class ContextAwareTask : Task
{
protected virtual string ManagedDllDirectory => Path.GetDirectoryName(new Uri(this.GetType().GetTypeInfo().Assembly.CodeBase).LocalPath);
protected virtual string UnmanagedDllDirectory => null;
public override bool Execute()
{
#if NETCOREAPP2_0
string taskAssemblyPath = new Uri(this.GetType().GetTypeInfo().Assembly.CodeBase).LocalPath;
var ctxt = new CustomAssemblyLoader(this);
Assembly inContextAssembly = ctxt.LoadFromAssemblyPath(taskAssemblyPath);
Type innerTaskType = inContextAssembly.GetType(this.GetType().FullName);
object innerTask = Activator.CreateInstance(innerTaskType);
var outerProperties = this.GetType().GetRuntimeProperties().ToDictionary(i => i.Name);
var innerProperties = innerTaskType.GetRuntimeProperties().ToDictionary(i => i.Name);
var propertiesDiscovery = from outerProperty in outerProperties.Values
where outerProperty.SetMethod != null && outerProperty.GetMethod != null
let innerProperty = innerProperties[outerProperty.Name]
select new { outerProperty, innerProperty };
var propertiesMap = propertiesDiscovery.ToArray();
var outputPropertiesMap = propertiesMap.Where(pair => pair.outerProperty.GetCustomAttribute<OutputAttribute>() != null).ToArray();
foreach (var propertyPair in propertiesMap)
{
object outerPropertyValue = propertyPair.outerProperty.GetValue(this);
propertyPair.innerProperty.SetValue(innerTask, outerPropertyValue);
}
var executeInnerMethod = innerTaskType.GetMethod(nameof(ExecuteInner), BindingFlags.Instance | BindingFlags.NonPublic);
bool result = (bool)executeInnerMethod.Invoke(innerTask, new object[0]);
foreach (var propertyPair in outputPropertiesMap)
{
propertyPair.outerProperty.SetValue(this, propertyPair.innerProperty.GetValue(innerTask));
}
return result;
#else
// On .NET Framework (on Windows), we find native binaries by adding them to our PATH.
if (this.UnmanagedDllDirectory != null)
{
string pathEnvVar = Environment.GetEnvironmentVariable("PATH");
string[] searchPaths = pathEnvVar.Split(Path.PathSeparator);
if (!searchPaths.Contains(this.UnmanagedDllDirectory, StringComparer.OrdinalIgnoreCase))
{
pathEnvVar += Path.PathSeparator + this.UnmanagedDllDirectory;
Environment.SetEnvironmentVariable("PATH", pathEnvVar);
}
}
return this.ExecuteInner();
#endif
}
protected abstract bool ExecuteInner();
#if NETCOREAPP2_0
private class CustomAssemblyLoader : AssemblyLoadContext
{
private readonly ContextAwareTask loaderTask;
internal CustomAssemblyLoader(ContextAwareTask loaderTask)
{
this.loaderTask = loaderTask;
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = Path.Combine(this.loaderTask.ManagedDllDirectory, assemblyName.Name) + ".dll";
if (File.Exists(assemblyPath))
{
return LoadFromAssemblyPath(assemblyPath);
}
return Default.LoadFromAssemblyName(assemblyName);
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string unmanagedDllPath = Directory.EnumerateFiles(
this.loaderTask.UnmanagedDllDirectory,
$"{unmanagedDllName}.*").Concat(
Directory.EnumerateFiles(
this.loaderTask.UnmanagedDllDirectory,
$"lib{unmanagedDllName}.*"))
.FirstOrDefault();
if (unmanagedDllPath != null)
{
return this.LoadUnmanagedDllFromPath(unmanagedDllPath);
}
return base.LoadUnmanagedDll(unmanagedDllName);
}
}
#endif
}
}