-
Notifications
You must be signed in to change notification settings - Fork 418
/
Copy pathMSBuildLocator.cs
247 lines (205 loc) · 8.57 KB
/
MSBuildLocator.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
using System;
using System.Collections.Immutable;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.Extensions.Logging;
using OmniSharp.MSBuild.Discovery.Providers;
using OmniSharp.Services;
using OmniSharp.Utilities;
namespace OmniSharp.MSBuild.Discovery
{
internal class MSBuildLocator : DisposableObject, IMSBuildLocator
{
private static readonly ImmutableHashSet<string> s_msbuildAssemblies = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase,
"Microsoft.Build",
"Microsoft.Build.Framework",
"Microsoft.Build.Tasks.Core",
"Microsoft.Build.Utilities.Core");
private readonly ILogger _logger;
private readonly IAssemblyLoader _assemblyLoader;
private readonly ImmutableArray<MSBuildInstanceProvider> _providers;
private MSBuildInstance _registeredInstance;
public MSBuildInstance RegisteredInstance => _registeredInstance;
private MSBuildLocator(ILoggerFactory loggerFactory, IAssemblyLoader assemblyLoader, ImmutableArray<MSBuildInstanceProvider> providers)
{
_logger = loggerFactory.CreateLogger<MSBuildLocator>();
_assemblyLoader = assemblyLoader;
_providers = providers;
}
protected override void DisposeCore(bool disposing)
{
if (_registeredInstance != null)
{
AppDomain.CurrentDomain.AssemblyResolve -= Resolve;
_registeredInstance = null;
}
}
public static MSBuildLocator CreateDefault(ILoggerFactory loggerFactory, IAssemblyLoader assemblyLoader)
=> new MSBuildLocator(loggerFactory, assemblyLoader,
ImmutableArray.Create<MSBuildInstanceProvider>(
new DevConsoleInstanceProvider(loggerFactory),
new VisualStudioInstanceProvider(loggerFactory),
new MonoInstanceProvider(loggerFactory),
new StandAloneInstanceProvider(loggerFactory, allowMonoPaths: true)));
public static MSBuildLocator CreateStandAlone(ILoggerFactory loggerFactory, IAssemblyLoader assemblyLoader, bool allowMonoPaths)
=> new MSBuildLocator(loggerFactory, assemblyLoader,
ImmutableArray.Create<MSBuildInstanceProvider>(
new StandAloneInstanceProvider(loggerFactory, allowMonoPaths)));
public void RegisterInstance(MSBuildInstance instance)
{
if (_registeredInstance != null)
{
throw new InvalidOperationException("An MSBuild instance is already registered.");
}
_registeredInstance = instance ?? throw new ArgumentNullException(nameof(instance));
foreach (var assemblyName in s_msbuildAssemblies)
{
LoadAssemblyByNameOnly(assemblyName);
}
AppDomain.CurrentDomain.AssemblyResolve += Resolve;
if (instance.SetMSBuildExePathVariable)
{
var msbuildExePath = Path.Combine(instance.MSBuildPath, "MSBuild.exe");
var msbuildDllPath = Path.Combine(instance.MSBuildPath, "MSBuild.dll");
string msbuildPath = null;
if (File.Exists(msbuildExePath))
{
msbuildPath = msbuildExePath;
}
else if (File.Exists(msbuildDllPath))
{
msbuildPath = msbuildDllPath;
}
if (!string.IsNullOrEmpty(msbuildPath))
{
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", msbuildPath);
_logger.LogInformation($"MSBUILD_EXE_PATH environment variable set to '{msbuildPath}'");
}
else
{
_logger.LogError("Could not find MSBuild executable path.");
}
}
var builder = new StringBuilder();
builder.Append($"Registered MSBuild instance: {instance}");
foreach (var kvp in instance.PropertyOverrides)
{
builder.Append($"{Environment.NewLine} {kvp.Key} = {kvp.Value}");
}
_logger.LogInformation(builder.ToString());
}
private Assembly Resolve(object sender, ResolveEventArgs e)
{
var assemblyName = new AssemblyName(e.Name);
_logger.LogDebug($"Attempting to resolve '{assemblyName}'");
return s_msbuildAssemblies.Contains(assemblyName.Name)
? LoadAssemblyByNameOnly(assemblyName.Name)
: LoadAssemblyByFullName(assemblyName);
}
private Assembly LoadAssemblyByNameOnly(string assemblyName)
{
var assemblyPath = Path.Combine(_registeredInstance.MSBuildPath, assemblyName + ".dll");
var result = File.Exists(assemblyPath)
? _assemblyLoader.LoadFrom(assemblyPath)
: null;
if (result != null)
{
_logger.LogDebug($"SUCCESS: Resolved to '{assemblyPath}' (name-only).");
}
return result;
}
private Assembly LoadAssemblyByFullName(AssemblyName assemblyName)
{
var assemblyPath = Path.Combine(_registeredInstance.MSBuildPath, assemblyName.Name + ".dll");
if (!File.Exists(assemblyPath))
{
_logger.LogDebug($"FAILURE: Could not locate '{assemblyPath}'.");
return null;
}
if (!TryGetAssemblyName(assemblyPath, out var resultAssemblyName))
{
_logger.LogDebug($"FAILURE: Could not retrieve {nameof(AssemblyName)} for '{assemblyPath}'.");
return null;
}
if (assemblyName.Name != resultAssemblyName.Name ||
assemblyName.Version != resultAssemblyName.Version ||
!AreEqual(assemblyName.GetPublicKeyToken(), resultAssemblyName.GetPublicKeyToken()))
{
_logger.LogDebug($"FAILURE: Found '{assemblyPath}' but name, '{resultAssemblyName}', did not match.");
return null;
}
// Note: don't bother testing culture. If the assembly has a different culture than what we're
// looking for, go ahead and use it.
var resultAssembly = _assemblyLoader.LoadFrom(assemblyPath);
if (resultAssembly != null)
{
_logger.LogDebug($"SUCCESS: Resolved to '{assemblyPath}'");
}
return resultAssembly;
}
private static bool AreEqual(byte[] array1, byte[] array2)
{
if (array1 == null)
{
return array2 == null;
}
if (array1 == null)
{
return false;
}
if (array1.Length != array2.Length)
{
return false;
}
for (int i = 0; i < array1.Length; i++)
{
if (array1[i] != array2[i])
{
return false;
}
}
return true;
}
private static bool TryGetAssemblyName(string assemblyPath, out AssemblyName assemblyName)
{
try
{
assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
return assemblyName != null;
}
catch
{
assemblyName = null;
return false;
}
}
public ImmutableArray<MSBuildInstance> GetInstances()
{
var builder = ImmutableArray.CreateBuilder<MSBuildInstance>();
foreach (var provider in _providers)
{
foreach (var instance in provider.GetInstances())
{
if (instance != null)
{
builder.Add(instance);
}
}
}
var result = builder.ToImmutable();
LogInstances(result);
return result;
}
private void LogInstances(ImmutableArray<MSBuildInstance> instances)
{
var builder = new StringBuilder();
builder.Append($"Located {instances.Length} MSBuild instance(s)");
for (int i = 0; i < instances.Length; i++)
{
builder.Append($"{Environment.NewLine} {i + 1}: {instances[i]}");
}
_logger.LogInformation(builder.ToString());
}
}
}