forked from dotnet/msbuild
/
ToolsetRegistryReader.cs
346 lines (306 loc) · 14 KB
/
ToolsetRegistryReader.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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if FEATURE_WIN32_REGISTRY
using System;
using System.Collections.Generic;
using Microsoft.Build.Shared;
using error = Microsoft.Build.Shared.ErrorUtilities;
using RegistryKeyWrapper = Microsoft.Build.Internal.RegistryKeyWrapper;
using RegistryException = Microsoft.Build.Exceptions.RegistryException;
using InvalidToolsetDefinitionException = Microsoft.Build.Exceptions.InvalidToolsetDefinitionException;
using Constants = Microsoft.Build.Internal.Constants;
using Microsoft.Build.Construction;
using Microsoft.Build.Collections;
using Microsoft.Build.Execution;
using Microsoft.Build.Internal;
namespace Microsoft.Build.Evaluation
{
/// <summary>
/// Reads registry at the base key and returns a Dictionary keyed on ToolsVersion.
/// Dictionary contains another dictionary of (property name, property value) pairs.
/// If a registry value is not a string, this will throw a InvalidToolsetDefinitionException.
/// An example of how the registry will look (note that the DefaultToolsVersion is per-MSBuild-version)
/// [HKLM]\SOFTWARE\Microsoft
/// msbuild
/// 3.5
/// @DefaultToolsVersion = 2.0
/// ToolsVersions
/// 2.0
/// @MSBuildToolsPath = D:\SomeFolder
/// 3.5
/// @MSBuildToolsPath = D:\SomeOtherFolder
/// @MSBuildBinPath = D:\SomeOtherFolder
/// @SomePropertyName = PropertyOtherValue
/// </summary>
internal class ToolsetRegistryReader : ToolsetReader
{
/// <summary>
/// Registry location for storing tools version dependent data for msbuild
/// </summary>
private const string MSBuildRegistryPath = @"SOFTWARE\Microsoft\MSBuild";
/// <summary>
/// Cached registry wrapper at root of the msbuild entries
/// </summary>
private RegistryKeyWrapper _msbuildRegistryWrapper;
/// <summary>
/// Default constructor
/// </summary>
internal ToolsetRegistryReader(PropertyDictionary<ProjectPropertyInstance> environmentProperties, PropertyDictionary<ProjectPropertyInstance> globalProperties)
: this(environmentProperties, globalProperties, new RegistryKeyWrapper(MSBuildRegistryPath))
{
}
/// <summary>
/// Constructor overload accepting a registry wrapper for unit testing purposes only
/// </summary>
internal ToolsetRegistryReader(PropertyDictionary<ProjectPropertyInstance> environmentProperties, PropertyDictionary<ProjectPropertyInstance> globalProperties, RegistryKeyWrapper msbuildRegistryWrapper)
: base(environmentProperties, globalProperties)
{
error.VerifyThrowArgumentNull(msbuildRegistryWrapper, nameof(msbuildRegistryWrapper));
_msbuildRegistryWrapper = msbuildRegistryWrapper;
}
/// <summary>
/// Returns the list of tools versions
/// </summary>
protected override IEnumerable<ToolsetPropertyDefinition> ToolsVersions
{
get
{
string[] toolsVersionNames = Array.Empty<string>();
try
{
RegistryKeyWrapper subKey = null;
using (subKey = _msbuildRegistryWrapper.OpenSubKey("ToolsVersions"))
{
toolsVersionNames = subKey.GetSubKeyNames();
}
}
catch (RegistryException ex)
{
InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
}
foreach (string toolsVersionName in toolsVersionNames)
{
// For the purposes of error location, use the registry path instead of a file name
IElementLocation location = new RegistryLocation(_msbuildRegistryWrapper.Name + "\\ToolsVersions\\" + toolsVersionName);
yield return new ToolsetPropertyDefinition(toolsVersionName, string.Empty, location);
}
}
}
/// <summary>
/// Returns the default tools version, or null if none was specified
/// </summary>
protected override string DefaultToolsVersion
{
get
{
string defaultToolsVersion = null;
// We expect to find the DefaultToolsVersion value under a registry key named for our
// version, e.g., "3.5"
using (RegistryKeyWrapper defaultToolsVersionKey = _msbuildRegistryWrapper.OpenSubKey(Constants.AssemblyVersion))
{
if (defaultToolsVersionKey != null)
{
defaultToolsVersion = GetValue(defaultToolsVersionKey, "DefaultToolsVersion");
}
}
return defaultToolsVersion;
}
}
/// <summary>
/// Returns the path to find override tasks, or null if none was specified
/// </summary>
protected override string MSBuildOverrideTasksPath
{
get
{
string defaultToolsVersion = null;
// We expect to find the MsBuildOverrideTasksPath value under a registry key named for our
// version, e.g., "4.0"
using (RegistryKeyWrapper defaultToolsVersionKey = _msbuildRegistryWrapper.OpenSubKey(Constants.AssemblyVersion))
{
if (defaultToolsVersionKey != null)
{
defaultToolsVersion = GetValue(defaultToolsVersionKey, ReservedPropertyNames.overrideTasksPath);
}
}
return defaultToolsVersion;
}
}
/// <summary>
/// ToolsVersion to use as the default ToolsVersion for this version of MSBuild
/// </summary>
protected override string DefaultOverrideToolsVersion
{
get
{
string defaultOverrideToolsVersion = null;
// We expect to find the MsBuildOverrideTasksPath value under a registry key named for our
// version, e.g., "12.0"
using (RegistryKeyWrapper defaultOverrideToolsVersionKey = _msbuildRegistryWrapper.OpenSubKey(Constants.AssemblyVersion))
{
if (defaultOverrideToolsVersionKey != null)
{
defaultOverrideToolsVersion = GetValue(defaultOverrideToolsVersionKey, ReservedPropertyNames.defaultOverrideToolsVersion);
}
}
return defaultOverrideToolsVersion;
}
}
/// <summary>
/// Provides an enumerator over property definitions for a specified tools version
/// </summary>
/// <param name="toolsVersion">The tools version</param>
/// <returns>An enumeration of property definitions</returns>
protected override IEnumerable<ToolsetPropertyDefinition> GetPropertyDefinitions(string toolsVersion)
{
RegistryKeyWrapper toolsVersionWrapper = null;
try
{
try
{
toolsVersionWrapper = _msbuildRegistryWrapper.OpenSubKey("ToolsVersions\\" + toolsVersion);
}
catch (RegistryException ex)
{
InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
}
foreach (string propertyName in toolsVersionWrapper.GetValueNames())
{
yield return CreatePropertyFromRegistry(toolsVersionWrapper, propertyName);
}
}
finally
{
toolsVersionWrapper?.Dispose();
}
}
/// <summary>
/// Provides an enumerator over the set of sub-toolset names available to a particular
/// toolsversion
/// </summary>
/// <param name="toolsVersion">The tools version.</param>
/// <returns>An enumeration of the sub-toolsets that belong to that toolsversion.</returns>
protected override IEnumerable<string> GetSubToolsetVersions(string toolsVersion)
{
RegistryKeyWrapper toolsVersionWrapper = null;
try
{
try
{
toolsVersionWrapper = _msbuildRegistryWrapper.OpenSubKey("ToolsVersions\\" + toolsVersion);
}
catch (RegistryException ex)
{
InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
}
return toolsVersionWrapper.GetSubKeyNames();
}
finally
{
toolsVersionWrapper?.Dispose();
}
}
/// <summary>
/// Provides an enumerator over property definitions for a specified sub-toolset version
/// under a specified toolset version.
/// </summary>
/// <param name="toolsVersion">The tools version.</param>
/// <param name="subToolsetVersion">The sub-toolset version.</param>
/// <returns>An enumeration of property definitions.</returns>
protected override IEnumerable<ToolsetPropertyDefinition> GetSubToolsetPropertyDefinitions(string toolsVersion, string subToolsetVersion)
{
ErrorUtilities.VerifyThrowArgumentLength(subToolsetVersion, nameof(subToolsetVersion));
RegistryKeyWrapper toolsVersionWrapper = null;
RegistryKeyWrapper subToolsetWrapper = null;
try
{
try
{
toolsVersionWrapper = _msbuildRegistryWrapper.OpenSubKey("ToolsVersions\\" + toolsVersion);
}
catch (RegistryException ex)
{
InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
}
try
{
subToolsetWrapper = toolsVersionWrapper.OpenSubKey(subToolsetVersion);
}
catch (RegistryException ex)
{
InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
}
foreach (string propertyName in subToolsetWrapper.GetValueNames())
{
yield return CreatePropertyFromRegistry(subToolsetWrapper, propertyName);
}
}
finally
{
toolsVersionWrapper?.Dispose();
subToolsetWrapper?.Dispose();
}
}
/// <summary>
/// Returns a map of MSBuildExtensionsPath* property names/kind to list of search paths
/// </summary>
protected override Dictionary<string, ProjectImportPathMatch> GetProjectImportSearchPathsTable(string toolsVersion, string os)
{
return new Dictionary<string, ProjectImportPathMatch>();
}
/// <summary>
/// Given a registry location containing a property name and value, create the ToolsetPropertyDefinition that maps to it
/// </summary>
/// <param name="toolsetWrapper">Wrapper for the key that we're getting values from</param>
/// <param name="propertyName">The name of the property whose value we wish to generate a ToolsetPropertyDefinition for.</param>
/// <returns>A ToolsetPropertyDefinition instance corresponding to the property name requested.</returns>
private static ToolsetPropertyDefinition CreatePropertyFromRegistry(RegistryKeyWrapper toolsetWrapper, string propertyName)
{
string propertyValue = null;
if (propertyName?.Length == 0)
{
InvalidToolsetDefinitionException.Throw("PropertyNameInRegistryHasZeroLength", toolsetWrapper.Name);
}
try
{
propertyValue = GetValue(toolsetWrapper, propertyName);
}
catch (RegistryException ex)
{
InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
}
// For the purposes of error location, use the registry path instead of a file name
IElementLocation location = new RegistryLocation(toolsetWrapper.Name + "@" + propertyName);
return new ToolsetPropertyDefinition(propertyName, propertyValue, location);
}
/// <summary>
/// Reads a string value from the specified registry key
/// </summary>
/// <param name="wrapper">wrapper around key</param>
/// <param name="valueName">name of the value</param>
/// <returns>string data in the value</returns>
private static string GetValue(RegistryKeyWrapper wrapper, string valueName)
{
if (wrapper.Exists())
{
object result = wrapper.GetValue(valueName);
// RegistryKey.GetValue returns null if the value is not present
// and String.Empty if the value is present and no data is defined.
// We preserve this distinction, because a string property in the registry with
// no value really has an empty string for a value (which is a valid property value)
// rather than null for a value (which is an invalid property value)
if (result != null)
{
// Must be a value of string type
if (!(result is string))
{
InvalidToolsetDefinitionException.Throw("NonStringDataInRegistry", wrapper.Name + "@" + valueName);
}
return result.ToString();
}
}
return null;
}
}
}
#endif