1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.Collections.Concurrent; 6 using System.Collections.Generic; 7 #if FEATURE_SYSTEM_CONFIGURATION 8 using System.Configuration; 9 #endif 10 using System.IO; 11 using System.Linq; 12 using System.Runtime.Versioning; 13 14 using Microsoft.Build.Evaluation; 15 using Microsoft.Win32; 16 17 #if FEATURE_SYSTEM_CONFIGURATION 18 using PropertyElement = Microsoft.Build.Evaluation.ToolsetElement.PropertyElement; 19 #endif 20 using System.Reflection; 21 using System.Runtime.InteropServices; 22 23 namespace Microsoft.Build.Shared 24 { 25 /// <summary> 26 /// Used to specify the targeted bitness of the .NET Framework for some methods of FrameworkLocationHelper 27 /// </summary> 28 internal enum DotNetFrameworkArchitecture 29 { 30 /// <summary> 31 /// Indicates the .NET Framework that is currently being run under. 32 /// </summary> 33 Current = 0, 34 35 /// <summary> 36 /// Indicates the 32-bit .NET Framework 37 /// </summary> 38 Bitness32 = 1, 39 40 /// <summary> 41 /// Indicates the 64-bit .NET Framework 42 /// </summary> 43 Bitness64 = 2 44 } 45 46 /// <summary> 47 /// FrameworkLocationHelper provides utility methods for locating .NET Framework and .NET Framework SDK directories and files 48 /// </summary> 49 internal static class FrameworkLocationHelper 50 { 51 #region Constants 52 53 internal const string dotNetFrameworkIdentifier = ".NETFramework"; 54 55 // .net versions. 56 internal static readonly Version dotNetFrameworkVersion11 = new Version(1, 1); 57 internal static readonly Version dotNetFrameworkVersion20 = new Version(2, 0); 58 internal static readonly Version dotNetFrameworkVersion30 = new Version(3, 0); 59 internal static readonly Version dotNetFrameworkVersion35 = new Version(3, 5); 60 internal static readonly Version dotNetFrameworkVersion40 = new Version(4, 0); 61 internal static readonly Version dotNetFrameworkVersion45 = new Version(4, 5); 62 internal static readonly Version dotNetFrameworkVersion451 = new Version(4, 5, 1); 63 internal static readonly Version dotNetFrameworkVersion452 = new Version(4, 5, 2); 64 internal static readonly Version dotNetFrameworkVersion46 = new Version(4, 6); 65 internal static readonly Version dotNetFrameworkVersion461 = new Version(4, 6, 1); 66 internal static readonly Version dotNetFrameworkVersion462 = new Version(4, 6, 2); 67 internal static readonly Version dotNetFrameworkVersion47 = new Version(4, 7); 68 internal static readonly Version dotNetFrameworkVersion471 = new Version(4, 7, 1); 69 70 // visual studio versions. 71 internal static readonly Version visualStudioVersion100 = new Version(10, 0); 72 internal static readonly Version visualStudioVersion110 = new Version(11, 0); 73 internal static readonly Version visualStudioVersion120 = new Version(12, 0); 74 internal static readonly Version visualStudioVersion140 = new Version(14, 0); 75 internal static readonly Version visualStudioVersion150 = new Version(15, 0); 76 77 // keep this up-to-date; always point to the latest visual studio version. 78 internal static readonly Version visualStudioVersionLatest = visualStudioVersion150; 79 80 private const string dotNetFrameworkRegistryPath = "SOFTWARE\\Microsoft\\.NETFramework"; 81 private const string dotNetFrameworkSetupRegistryPath = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP"; 82 private const string dotNetFrameworkSetupRegistryInstalledName = "Install"; 83 84 internal const string fullDotNetFrameworkRegistryKey = "HKEY_LOCAL_MACHINE\\" + dotNetFrameworkRegistryPath; 85 private const string dotNetFrameworkAssemblyFoldersRegistryPath = dotNetFrameworkRegistryPath + "\\AssemblyFolders"; 86 private const string referenceAssembliesRegistryValueName = "All Assemblies In"; 87 88 internal const string dotNetFrameworkSdkInstallKeyValueV11 = "SDKInstallRootv1.1"; 89 internal static string dotNetFrameworkVersionFolderPrefixV11 = NativeMethodsShared.IsWindows ? "v1.1" : "1.1"; // v1.1 is for Everett. 90 private const string dotNetFrameworkVersionV11 = "v1.1.4322"; // full Everett version to pass to NativeMethodsShared.GetRequestedRuntimeInfo(). 91 private const string dotNetFrameworkRegistryKeyV11 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionV11; 92 93 internal const string dotNetFrameworkSdkInstallKeyValueV20 = "SDKInstallRootv2.0"; 94 internal static string dotNetFrameworkVersionFolderPrefixV20 = NativeMethodsShared.IsWindows ? "v2.0" : "2.0"; // v2.0 is for Whidbey. 95 private const string dotNetFrameworkVersionV20 = "v2.0.50727"; // full Whidbey version to pass to NativeMethodsShared.GetRequestedRuntimeInfo(). 96 private const string dotNetFrameworkRegistryKeyV20 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionV20; 97 98 internal static string dotNetFrameworkVersionFolderPrefixV30 = NativeMethodsShared.IsWindows ? "v3.0" : "3.0"; // v3.0 is for WinFx. 99 private static string s_dotNetFrameworkRegistryKeyV30 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV30 + "\\Setup"; 100 101 private const string fallbackDotNetFrameworkSdkRegistryInstallPath = "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"; 102 internal const string fallbackDotNetFrameworkSdkInstallKeyValue = "CurrentInstallFolder"; 103 104 private const string dotNetFrameworkSdkRegistryPathForV35ToolsOnWinSDK70A = @"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A\WinSDK-NetFx35Tools-x86"; 105 private const string fullDotNetFrameworkSdkRegistryPathForV35ToolsOnWinSDK70A = "HKEY_LOCAL_MACHINE\\" + dotNetFrameworkSdkRegistryPathForV35ToolsOnWinSDK70A; 106 107 private const string dotNetFrameworkSdkRegistryPathForV35ToolsOnManagedToolsSDK80A = @"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0A\WinSDK-NetFx35Tools-x86"; 108 private const string fullDotNetFrameworkSdkRegistryPathForV35ToolsOnManagedToolsSDK80A = "HKEY_LOCAL_MACHINE\\" + dotNetFrameworkSdkRegistryPathForV35ToolsOnManagedToolsSDK80A; 109 110 internal static string dotNetFrameworkVersionFolderPrefixV35 = NativeMethodsShared.IsWindows ? "v3.5" : "3.5"; // v3.5 is for Orcas. 111 private static string s_dotNetFrameworkRegistryKeyV35 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV35; 112 113 internal const string fullDotNetFrameworkSdkRegistryKeyV35OnVS10 = fullDotNetFrameworkSdkRegistryPathForV35ToolsOnWinSDK70A; 114 internal const string fullDotNetFrameworkSdkRegistryKeyV35OnVS11 = fullDotNetFrameworkSdkRegistryPathForV35ToolsOnManagedToolsSDK80A; 115 116 internal static string dotNetFrameworkVersionFolderPrefixV40 = NativeMethodsShared.IsWindows ? "v4.0" : "4.0"; 117 internal static string dotNetFrameworkVersionFolderPrefixV45 = NativeMethodsShared.IsWindows ? "v4.5" : "4.5"; 118 119 /// <summary> 120 /// Path to the ToolsVersion definitions in the registry 121 /// </summary> 122 private const string ToolsVersionsRegistryPath = @"SOFTWARE\Microsoft\MSBuild\ToolsVersions"; 123 124 #endregion // Constants 125 126 #region Static member variables 127 128 /// <summary> 129 /// By default when a root path is not specified we would like to use the program files directory \ reference assemblies\framework as the root location 130 /// to generate the reference assembly paths from. 131 /// </summary> 132 #if FEATURE_SPECIAL_FOLDERS 133 internal static readonly string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); 134 #else 135 internal static readonly string programFiles = FileUtilities.GetFolderPath(FileUtilities.SpecialFolder.ProgramFiles); 136 #endif 137 internal static readonly string programFiles32 = GenerateProgramFiles32(); 138 internal static readonly string programFiles64 = GenerateProgramFiles64(); 139 internal static readonly string programFilesReferenceAssemblyLocation = GenerateProgramFilesReferenceAssemblyRoot(); 140 141 private static string s_fallbackDotNetFrameworkSdkInstallPath; 142 143 private static string s_pathToV35ToolsInFallbackDotNetFrameworkSdk; 144 145 private static string s_pathToV4ToolsInFallbackDotNetFrameworkSdk; 146 147 /// <summary> 148 /// List the supported .net versions. 149 /// </summary> 150 private static readonly DotNetFrameworkSpec[] s_dotNetFrameworkSpecs = 151 { 152 // v1.1 153 new DotNetFrameworkSpecLegacy( 154 dotNetFrameworkVersion11, 155 dotNetFrameworkRegistryKeyV11, 156 dotNetFrameworkSetupRegistryInstalledName, 157 dotNetFrameworkVersionFolderPrefixV11, 158 dotNetFrameworkSdkInstallKeyValueV11, 159 hasMSBuild: false), 160 161 // v2.0 162 new DotNetFrameworkSpecLegacy( 163 dotNetFrameworkVersion20, 164 dotNetFrameworkRegistryKeyV20, 165 dotNetFrameworkSetupRegistryInstalledName, 166 dotNetFrameworkVersionFolderPrefixV20, 167 dotNetFrameworkSdkInstallKeyValueV20, 168 hasMSBuild: true), 169 170 // v3.0 171 new DotNetFrameworkSpecV3( 172 dotNetFrameworkVersion30, 173 s_dotNetFrameworkRegistryKeyV30, 174 "InstallSuccess", 175 dotNetFrameworkVersionFolderPrefixV30, 176 null, 177 null, 178 hasMSBuild: false), 179 180 // v3.5 181 new DotNetFrameworkSpecV3( 182 dotNetFrameworkVersion35, 183 s_dotNetFrameworkRegistryKeyV35, 184 dotNetFrameworkSetupRegistryInstalledName, 185 dotNetFrameworkVersionFolderPrefixV35, 186 "WinSDK-NetFx35Tools-x86", 187 "InstallationFolder", 188 hasMSBuild: true), 189 190 // v4.0 191 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion40, visualStudioVersion100), 192 193 // v4.5 194 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion45, visualStudioVersion110), 195 196 // v4.5.1 197 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion451, visualStudioVersion120), 198 199 // v4.5.2 200 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion452, visualStudioVersion120), 201 202 // v4.6 203 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion46, visualStudioVersion140), 204 205 // v4.6.1 206 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion461, visualStudioVersion140), 207 208 // v4.6.2 209 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion462, visualStudioVersion150), 210 211 // v4.7 212 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion47, visualStudioVersion150), 213 214 // v4.7.1 215 CreateDotNetFrameworkSpecForV4(dotNetFrameworkVersion471, visualStudioVersion150), 216 }; 217 218 /// <summary> 219 /// List the supported visual studio versions. 220 /// </summary> 221 /// <remarks> 222 /// The items must be ordered by the version, because some methods depend on that fact to find the previous visual studio version. 223 /// </remarks> 224 private static readonly VisualStudioSpec[] s_visualStudioSpecs = 225 { 226 // VS10 227 new VisualStudioSpec(visualStudioVersion100, "Windows\\v7.0A", null, null, new [] 228 { 229 dotNetFrameworkVersion11, 230 dotNetFrameworkVersion20, 231 dotNetFrameworkVersion35, 232 dotNetFrameworkVersion40, 233 }), 234 235 // VS11 236 new VisualStudioSpec(visualStudioVersion110, "Windows\\v8.0A", "v8.0", "InstallationFolder", new [] 237 { 238 dotNetFrameworkVersion11, 239 dotNetFrameworkVersion20, 240 dotNetFrameworkVersion35, 241 dotNetFrameworkVersion40, 242 dotNetFrameworkVersion45, 243 }), 244 245 // VS12 246 new VisualStudioSpec(visualStudioVersion120, "Windows\\v8.1A", "v8.1", "InstallationFolder", new [] 247 { 248 dotNetFrameworkVersion11, 249 dotNetFrameworkVersion20, 250 dotNetFrameworkVersion35, 251 dotNetFrameworkVersion40, 252 dotNetFrameworkVersion45, 253 dotNetFrameworkVersion451, 254 dotNetFrameworkVersion452 255 }), 256 257 // VS14 258 new VisualStudioSpec(visualStudioVersion140, "NETFXSDK\\{0}", "v8.1", "InstallationFolder", new [] 259 { 260 dotNetFrameworkVersion11, 261 dotNetFrameworkVersion20, 262 dotNetFrameworkVersion35, 263 dotNetFrameworkVersion40, 264 dotNetFrameworkVersion45, 265 dotNetFrameworkVersion451, 266 dotNetFrameworkVersion452, 267 dotNetFrameworkVersion46, 268 dotNetFrameworkVersion461 269 }), 270 271 // VS15 272 new VisualStudioSpec(visualStudioVersion150, "NETFXSDK\\{0}", "v8.1", "InstallationFolder", new [] 273 { 274 dotNetFrameworkVersion11, 275 dotNetFrameworkVersion20, 276 dotNetFrameworkVersion35, 277 dotNetFrameworkVersion40, 278 dotNetFrameworkVersion45, 279 dotNetFrameworkVersion451, 280 dotNetFrameworkVersion452, 281 dotNetFrameworkVersion46, 282 dotNetFrameworkVersion461, 283 dotNetFrameworkVersion462, 284 dotNetFrameworkVersion47, 285 dotNetFrameworkVersion471 286 }), 287 }; 288 289 /// <summary> 290 /// Define explicit fallback rules for the request to get path of .net framework sdk tools folder. 291 /// The default rule is fallback to previous VS. However, there are some special cases that need 292 /// explicit rules, i.e. v4.5.1 on VS12 fallbacks to v4.5 on VS12. 293 /// </summary> 294 /// <remarks> 295 /// The rules are maintained in a 2-dimensions array. Each row defines a rule. The first column 296 /// defines the trigger condition. The second column defines the fallback .net and VS versions. 297 /// </remarks> 298 private static readonly Tuple<Version, Version>[,] s_explicitFallbackRulesForPathToDotNetFrameworkSdkTools = 299 { 300 // VS12 301 { Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion120), Tuple.Create(dotNetFrameworkVersion45, visualStudioVersion120) }, 302 { Tuple.Create(dotNetFrameworkVersion452, visualStudioVersion120), Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion120) }, 303 304 // VS14 305 { Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion140), Tuple.Create(dotNetFrameworkVersion45, visualStudioVersion140) }, 306 { Tuple.Create(dotNetFrameworkVersion452, visualStudioVersion140), Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion140) }, 307 { Tuple.Create(dotNetFrameworkVersion46, visualStudioVersion140), Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion140) }, 308 { Tuple.Create(dotNetFrameworkVersion461, visualStudioVersion140), Tuple.Create(dotNetFrameworkVersion46, visualStudioVersion140) }, 309 310 // VS15 311 { Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion150), Tuple.Create(dotNetFrameworkVersion45, visualStudioVersion150) }, 312 { Tuple.Create(dotNetFrameworkVersion452, visualStudioVersion150), Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion150) }, 313 { Tuple.Create(dotNetFrameworkVersion46, visualStudioVersion150), Tuple.Create(dotNetFrameworkVersion451, visualStudioVersion150) }, 314 { Tuple.Create(dotNetFrameworkVersion461, visualStudioVersion150), Tuple.Create(dotNetFrameworkVersion46, visualStudioVersion150) }, 315 { Tuple.Create(dotNetFrameworkVersion462, visualStudioVersion150), Tuple.Create(dotNetFrameworkVersion461, visualStudioVersion150) }, 316 { Tuple.Create(dotNetFrameworkVersion47, visualStudioVersion150), Tuple.Create(dotNetFrameworkVersion462, visualStudioVersion150) }, 317 { Tuple.Create(dotNetFrameworkVersion471, visualStudioVersion150), Tuple.Create(dotNetFrameworkVersion47, visualStudioVersion150) }, 318 }; 319 320 private static readonly IReadOnlyDictionary<Version, DotNetFrameworkSpec> s_dotNetFrameworkSpecDict; 321 private static readonly IReadOnlyDictionary<Version, VisualStudioSpec> s_visualStudioSpecDict; 322 323 #endregion // Static member variables 324 FrameworkLocationHelper()325 static FrameworkLocationHelper() 326 { 327 s_dotNetFrameworkSpecDict = s_dotNetFrameworkSpecs.ToDictionary(spec => spec.Version); 328 s_visualStudioSpecDict = s_visualStudioSpecs.ToDictionary(spec => spec.Version); 329 } 330 331 #region Static properties 332 333 internal static string PathToDotNetFrameworkV11 334 { 335 get 336 { 337 return NativeMethodsShared.IsUnixLike 338 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV11) 339 : GetPathToDotNetFrameworkV11(DotNetFrameworkArchitecture.Current); 340 } 341 } 342 343 internal static string PathToDotNetFrameworkV20 344 { 345 get 346 { 347 return NativeMethodsShared.IsUnixLike 348 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV20) 349 : GetPathToDotNetFrameworkV20(DotNetFrameworkArchitecture.Current); 350 } 351 } 352 353 internal static string PathToDotNetFrameworkV30 354 { 355 get 356 { 357 return NativeMethodsShared.IsUnixLike 358 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV30) 359 : GetPathToDotNetFrameworkV30(DotNetFrameworkArchitecture.Current); 360 } 361 } 362 363 internal static string PathToDotNetFrameworkV35 364 { 365 get 366 { 367 return NativeMethodsShared.IsUnixLike 368 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV35) 369 : GetPathToDotNetFrameworkV35(DotNetFrameworkArchitecture.Current); 370 } 371 } 372 373 internal static string PathToDotNetFrameworkV40 374 { 375 get 376 { 377 return NativeMethodsShared.IsUnixLike 378 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV40) 379 : GetPathToDotNetFrameworkV40(DotNetFrameworkArchitecture.Current); 380 } 381 } 382 383 internal static string PathToDotNetFrameworkV45 384 { 385 get 386 { 387 return NativeMethodsShared.IsUnixLike 388 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV45) 389 : GetPathToDotNetFrameworkV45(DotNetFrameworkArchitecture.Current); 390 } 391 } 392 393 internal static string PathToDotNetFrameworkSdkV11 394 { 395 get 396 { 397 return NativeMethodsShared.IsUnixLike 398 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV11) 399 : GetPathToDotNetFrameworkSdkTools(dotNetFrameworkVersion11, visualStudioVersionLatest); 400 } 401 } 402 403 internal static string PathToDotNetFrameworkSdkV20 404 { 405 get 406 { 407 return NativeMethodsShared.IsUnixLike 408 ? Path.Combine(NativeMethodsShared.FrameworkBasePath, dotNetFrameworkVersionFolderPrefixV20) 409 : GetPathToDotNetFrameworkSdkTools(dotNetFrameworkVersion20, visualStudioVersionLatest); 410 } 411 } 412 413 /// <summary> 414 /// Because there is no longer a strong 1:1 mapping between FX versions and SDK 415 /// versions, if we're unable to locate the desired SDK version, we will try to 416 /// use whichever SDK version is installed by looking at the key pointing to the 417 /// "latest" version. 418 /// 419 /// This isn't ideal, but it will allow our tasks to function on any of several 420 /// related SDKs even if they don't have exactly the same versions. 421 /// 422 /// NOTE: This returns the path to the root of the fallback SDK 423 /// </summary> 424 private static string FallbackDotNetFrameworkSdkInstallPath 425 { 426 get 427 { 428 if (s_fallbackDotNetFrameworkSdkInstallPath == null) 429 { 430 // For non-Windows just get the current Framework path 431 if (!NativeMethodsShared.IsWindows) 432 { 433 s_fallbackDotNetFrameworkSdkInstallPath = NativeMethodsShared.FrameworkCurrentPath; 434 } 435 #if FEATURE_WIN32_REGISTRY 436 else 437 { 438 s_fallbackDotNetFrameworkSdkInstallPath = 439 FindRegistryValueUnderKey( 440 fallbackDotNetFrameworkSdkRegistryInstallPath, 441 fallbackDotNetFrameworkSdkInstallKeyValue); 442 443 if (EnvironmentUtilities.Is64BitProcess && s_fallbackDotNetFrameworkSdkInstallPath == null) 444 { 445 // Since we're 64-bit, what we just checked was the 64-bit fallback key -- so now let's 446 // check the 32-bit one too, just in case. 447 s_fallbackDotNetFrameworkSdkInstallPath = 448 FindRegistryValueUnderKey( 449 fallbackDotNetFrameworkSdkRegistryInstallPath, 450 fallbackDotNetFrameworkSdkInstallKeyValue, 451 RegistryView.Registry32); 452 } 453 } 454 #endif 455 } 456 457 return s_fallbackDotNetFrameworkSdkInstallPath; 458 } 459 } 460 461 /// <summary> 462 /// Because there is no longer a strong 1:1 mapping between FX versions and SDK 463 /// versions, if we're unable to locate the desired SDK version, we will try to 464 /// use whichever SDK version is installed by looking at the key pointing to the 465 /// "latest" version. 466 /// 467 /// This isn't ideal, but it will allow our tasks to function on any of several 468 /// related SDKs even if they don't have exactly the same versions. 469 /// 470 /// NOTE: This explicitly returns the path to the 3.5 tools (bin) under the fallback 471 /// SDK, to match the data we're pulling from the registry now. 472 /// </summary> 473 private static string PathToV35ToolsInFallbackDotNetFrameworkSdk 474 { 475 get 476 { 477 if (s_pathToV35ToolsInFallbackDotNetFrameworkSdk == null) 478 { 479 if (FallbackDotNetFrameworkSdkInstallPath != null) 480 { 481 if (NativeMethodsShared.IsWindows) 482 { 483 var endsWithASlash = 484 FallbackDotNetFrameworkSdkInstallPath.EndsWith( 485 Path.DirectorySeparatorChar.ToString(), 486 StringComparison.Ordinal); 487 488 s_pathToV35ToolsInFallbackDotNetFrameworkSdk = 489 Path.Combine(FallbackDotNetFrameworkSdkInstallPath, "bin"); 490 491 // Path.Combine leaves no trailing slash, so if we had one before, be sure to add it back in 492 if (endsWithASlash) 493 { 494 s_pathToV35ToolsInFallbackDotNetFrameworkSdk = s_pathToV35ToolsInFallbackDotNetFrameworkSdk 495 + Path.DirectorySeparatorChar; 496 } 497 } 498 else 499 { 500 s_pathToV35ToolsInFallbackDotNetFrameworkSdk = FallbackDotNetFrameworkSdkInstallPath; 501 } 502 } 503 } 504 505 return s_pathToV35ToolsInFallbackDotNetFrameworkSdk; 506 } 507 } 508 509 /// <summary> 510 /// Because there is no longer a strong 1:1 mapping between FX versions and SDK 511 /// versions, if we're unable to locate the desired SDK version, we will try to 512 /// use whichever SDK version is installed by looking at the key pointing to the 513 /// "latest" version. 514 /// 515 /// This isn't ideal, but it will allow our tasks to function on any of several 516 /// related SDKs even if they don't have exactly the same versions. 517 /// 518 /// NOTE: This explicitly returns the path to the 4.X tools (bin\NetFX 4.0 Tools) 519 /// under the fallback SDK, to match the data we're pulling from the registry now. 520 /// </summary> 521 private static string PathToV4ToolsInFallbackDotNetFrameworkSdk 522 { 523 get 524 { 525 if (s_pathToV4ToolsInFallbackDotNetFrameworkSdk == null) 526 { 527 if (FallbackDotNetFrameworkSdkInstallPath != null) 528 { 529 if (NativeMethodsShared.IsWindows) 530 { 531 bool endsWithASlash = FallbackDotNetFrameworkSdkInstallPath.EndsWith( 532 "\\", 533 StringComparison.Ordinal); 534 535 s_pathToV4ToolsInFallbackDotNetFrameworkSdk = Path.Combine(FallbackDotNetFrameworkSdkInstallPath, "bin", "NetFX 4.0 Tools"); 536 537 // Path.Combine leaves no trailing slash, so if we had one before, be sure to add it back in 538 if (endsWithASlash) 539 { 540 s_pathToV4ToolsInFallbackDotNetFrameworkSdk = s_pathToV4ToolsInFallbackDotNetFrameworkSdk + "\\"; 541 } 542 } 543 else 544 { 545 s_pathToV4ToolsInFallbackDotNetFrameworkSdk = FallbackDotNetFrameworkSdkInstallPath; 546 } 547 } 548 } 549 550 return s_pathToV4ToolsInFallbackDotNetFrameworkSdk; 551 } 552 } 553 554 #endregion // Static properties 555 556 #region Internal methods 557 GetDotNetFrameworkSdkRootRegistryKey(Version dotNetFrameworkVersion, Version visualStudioVersion)558 internal static string GetDotNetFrameworkSdkRootRegistryKey(Version dotNetFrameworkVersion, Version visualStudioVersion) 559 { 560 RedirectVersionsIfNecessary(ref dotNetFrameworkVersion, ref visualStudioVersion); 561 562 var dotNetFrameworkSpec = GetDotNetFrameworkSpec(dotNetFrameworkVersion); 563 var visualStudioSpec = GetVisualStudioSpec(visualStudioVersion); 564 ErrorUtilities.VerifyThrowArgument(visualStudioSpec.SupportedDotNetFrameworkVersions.Contains(dotNetFrameworkVersion), "FrameworkLocationHelper.UnsupportedFrameworkVersion", dotNetFrameworkVersion); 565 return dotNetFrameworkSpec.GetDotNetFrameworkSdkRootRegistryKey(visualStudioSpec); 566 } 567 GetDotNetFrameworkSdkInstallKeyValue(Version dotNetFrameworkVersion, Version visualStudioVersion)568 internal static string GetDotNetFrameworkSdkInstallKeyValue(Version dotNetFrameworkVersion, Version visualStudioVersion) 569 { 570 RedirectVersionsIfNecessary(ref dotNetFrameworkVersion, ref visualStudioVersion); 571 572 var dotNetFrameworkSpec = GetDotNetFrameworkSpec(dotNetFrameworkVersion); 573 var visualStudioSpec = GetVisualStudioSpec(visualStudioVersion); 574 ErrorUtilities.VerifyThrowArgument(visualStudioSpec.SupportedDotNetFrameworkVersions.Contains(dotNetFrameworkVersion), "FrameworkLocationHelper.UnsupportedFrameworkVersion", dotNetFrameworkVersion); 575 return dotNetFrameworkSpec.DotNetFrameworkSdkRegistryInstallationFolderName; 576 } 577 GetDotNetFrameworkVersionFolderPrefix(Version dotNetFrameworkVersion)578 internal static string GetDotNetFrameworkVersionFolderPrefix(Version dotNetFrameworkVersion) 579 { 580 return GetDotNetFrameworkSpec(dotNetFrameworkVersion).DotNetFrameworkFolderPrefix; 581 } 582 GetPathToWindowsSdk(Version dotNetFrameworkVersion)583 internal static string GetPathToWindowsSdk(Version dotNetFrameworkVersion) 584 { 585 return GetDotNetFrameworkSpec(dotNetFrameworkVersion).GetPathToWindowsSdk(); 586 } 587 GetPathToDotNetFrameworkReferenceAssemblies(Version dotNetFrameworkVersion)588 internal static string GetPathToDotNetFrameworkReferenceAssemblies(Version dotNetFrameworkVersion) 589 { 590 return GetDotNetFrameworkSpec(dotNetFrameworkVersion).GetPathToDotNetFrameworkReferenceAssemblies(); 591 } 592 GetPathToDotNetFrameworkSdkTools(Version dotNetFrameworkVersion, Version visualStudioVersion)593 internal static string GetPathToDotNetFrameworkSdkTools(Version dotNetFrameworkVersion, Version visualStudioVersion) 594 { 595 RedirectVersionsIfNecessary(ref dotNetFrameworkVersion, ref visualStudioVersion); 596 597 var dotNetFrameworkSpec = GetDotNetFrameworkSpec(dotNetFrameworkVersion); 598 var visualStudioSpec = GetVisualStudioSpec(visualStudioVersion); 599 ErrorUtilities.VerifyThrowArgument(visualStudioSpec.SupportedDotNetFrameworkVersions.Contains(dotNetFrameworkVersion), "FrameworkLocationHelper.UnsupportedFrameworkVersion", dotNetFrameworkVersion); 600 return dotNetFrameworkSpec.GetPathToDotNetFrameworkSdkTools(visualStudioSpec); 601 } 602 GetPathToDotNetFrameworkSdk(Version dotNetFrameworkVersion, Version visualStudioVersion)603 internal static string GetPathToDotNetFrameworkSdk(Version dotNetFrameworkVersion, Version visualStudioVersion) 604 { 605 RedirectVersionsIfNecessary(ref dotNetFrameworkVersion, ref visualStudioVersion); 606 607 var dotNetFrameworkSpec = GetDotNetFrameworkSpec(dotNetFrameworkVersion); 608 var visualStudioSpec = GetVisualStudioSpec(visualStudioVersion); 609 ErrorUtilities.VerifyThrowArgument(visualStudioSpec.SupportedDotNetFrameworkVersions.Contains(dotNetFrameworkVersion), "FrameworkLocationHelper.UnsupportedFrameworkVersion", dotNetFrameworkVersion); 610 return dotNetFrameworkSpec.GetPathToDotNetFrameworkSdk(visualStudioSpec); 611 } 612 GetPathToDotNetFrameworkV11(DotNetFrameworkArchitecture architecture)613 internal static string GetPathToDotNetFrameworkV11(DotNetFrameworkArchitecture architecture) 614 { 615 return GetPathToDotNetFramework(dotNetFrameworkVersion11, architecture); 616 } 617 GetPathToDotNetFrameworkV20(DotNetFrameworkArchitecture architecture)618 internal static string GetPathToDotNetFrameworkV20(DotNetFrameworkArchitecture architecture) 619 { 620 return GetPathToDotNetFramework(dotNetFrameworkVersion20, architecture); 621 } 622 GetPathToDotNetFrameworkV30(DotNetFrameworkArchitecture architecture)623 internal static string GetPathToDotNetFrameworkV30(DotNetFrameworkArchitecture architecture) 624 { 625 return GetPathToDotNetFramework(dotNetFrameworkVersion30, architecture); 626 } 627 GetPathToDotNetFrameworkV35(DotNetFrameworkArchitecture architecture)628 internal static string GetPathToDotNetFrameworkV35(DotNetFrameworkArchitecture architecture) 629 { 630 return GetPathToDotNetFramework(dotNetFrameworkVersion35, architecture); 631 } 632 GetPathToDotNetFrameworkV40(DotNetFrameworkArchitecture architecture)633 internal static string GetPathToDotNetFrameworkV40(DotNetFrameworkArchitecture architecture) 634 { 635 return GetPathToDotNetFramework(dotNetFrameworkVersion40, architecture); 636 } 637 GetPathToDotNetFrameworkV45(DotNetFrameworkArchitecture architecture)638 internal static string GetPathToDotNetFrameworkV45(DotNetFrameworkArchitecture architecture) 639 { 640 return GetPathToDotNetFramework(dotNetFrameworkVersion45, architecture); 641 } 642 GetPathToDotNetFramework(Version version)643 internal static string GetPathToDotNetFramework(Version version) 644 { 645 return GetPathToDotNetFramework(version, DotNetFrameworkArchitecture.Current); 646 } 647 GetPathToDotNetFramework(Version version, DotNetFrameworkArchitecture architecture)648 internal static string GetPathToDotNetFramework(Version version, DotNetFrameworkArchitecture architecture) 649 { 650 return GetDotNetFrameworkSpec(version).GetPathToDotNetFramework(architecture); 651 } 652 653 #if FEATURE_INSTALLED_MSBUILD 654 /// <summary> 655 /// Check the registry key and value to see if the .net Framework is installed on the machine. 656 /// </summary> 657 /// <param name="registryEntryToCheckInstall">Registry path to look for the value</param> 658 /// <param name="registryValueToCheckInstall">Key to retrieve the value from</param> 659 /// <returns>True if the registry key is 1 false if it is not there. This method also return true if the complus enviornment variables are set.</returns> CheckForFrameworkInstallation(string registryEntryToCheckInstall, string registryValueToCheckInstall)660 private static bool CheckForFrameworkInstallation(string registryEntryToCheckInstall, string registryValueToCheckInstall) 661 { 662 // Get the complus install root and version 663 string complusInstallRoot = Environment.GetEnvironmentVariable("COMPLUS_INSTALLROOT"); 664 string complusVersion = Environment.GetEnvironmentVariable("COMPLUS_VERSION"); 665 666 // Complus is not set we need to make sure the framework we are targeting is installed. Check the registry key before trying to find the directory. 667 // If complus is set then we will return that directory as the framework directory, there is no need to check the registry value for the framework and it may not even be installed. 668 if (String.IsNullOrEmpty(complusInstallRoot) && String.IsNullOrEmpty(complusVersion)) 669 { 670 if (NativeMethodsShared.IsWindows) 671 { 672 // If the registry entry is 1 then the framework is installed. Go ahead and find the directory. If it is not 1 then the framework is not installed, return null. 673 return String.Compare("1", FindRegistryValueUnderKey(registryEntryToCheckInstall, registryValueToCheckInstall), StringComparison.OrdinalIgnoreCase) == 0; 674 } 675 // False for non-windows since there is nothing in the registry 676 else 677 { 678 return false; 679 } 680 } 681 682 return true; 683 } 684 #endif 685 686 /// <summary> 687 /// Heuristic that first considers the current runtime path and then searches the base of that path for the given 688 /// frameworks version. 689 /// </summary> 690 /// <param name="currentRuntimePath">The path to the runtime that is currently executing.</param> 691 /// <param name="prefix">Should be something like 'v1.2' that indicates the runtime version we want.</param> 692 /// <param name="directoryExists">Function that checks if directory exists.</param> 693 /// <param name="getDirectories">Delegate to method that can return filesystem entries.</param> 694 /// <param name="architecture">.NET framework architecture</param> 695 /// <returns>Will return 'null' if there is no target frameworks on this machine.</returns> FindDotNetFrameworkPath( string currentRuntimePath, string prefix, DirectoryExists directoryExists, GetDirectories getDirectories, DotNetFrameworkArchitecture architecture )696 internal static string FindDotNetFrameworkPath 697 ( 698 string currentRuntimePath, 699 string prefix, 700 DirectoryExists directoryExists, 701 GetDirectories getDirectories, 702 DotNetFrameworkArchitecture architecture 703 ) 704 { 705 if (!NativeMethodsShared.IsWindows) 706 { 707 if (!string.IsNullOrEmpty(prefix) 708 && prefix.Substring(0, 1).Equals("v", StringComparison.OrdinalIgnoreCase)) 709 { 710 prefix = prefix.Substring(1); 711 } 712 713 var frameworkPath = Path.Combine(NativeMethodsShared.FrameworkBasePath, prefix ?? string.Empty); 714 return directoryExists(frameworkPath) ? frameworkPath : null; 715 } 716 717 // If the COMPLUS variables are set, they override everything -- that's the directory we want. 718 string complusInstallRoot = Environment.GetEnvironmentVariable("COMPLUS_INSTALLROOT"); 719 string complusVersion = Environment.GetEnvironmentVariable("COMPLUS_VERSION"); 720 721 if (!String.IsNullOrEmpty(complusInstallRoot) && !String.IsNullOrEmpty(complusVersion)) 722 { 723 return Path.Combine(complusInstallRoot, complusVersion); 724 } 725 726 // If the current runtime starts with correct prefix, then this is the runtime we want to use. 727 // However, only if we're requesting current architecture -- otherwise, the base path may be different, so we'll need to look it up. 728 string leaf = Path.GetFileName(currentRuntimePath); 729 if (leaf.StartsWith(prefix, StringComparison.Ordinal) && architecture == DotNetFrameworkArchitecture.Current) 730 { 731 return currentRuntimePath; 732 } 733 734 // We haven't managed to use exact methods to locate the FX, so 735 // search for the correct path with a heuristic. 736 string baseLocation = Path.GetDirectoryName(currentRuntimePath); 737 string searchPattern = prefix + "*"; 738 739 int indexOfFramework64 = baseLocation.IndexOf("Framework64", StringComparison.OrdinalIgnoreCase); 740 741 if (indexOfFramework64 != -1 && architecture == DotNetFrameworkArchitecture.Bitness32) 742 { 743 // need to get rid of just the 64, but want to look up 'Framework64' rather than '64' to avoid the case where 744 // the path is something like 'C:\MyPath\64\Framework64'. 9 = length of 'Framework', to make the index match 745 // the location of the '64'. 746 int indexOf64 = indexOfFramework64 + 9; 747 string tempLocation = baseLocation; 748 baseLocation = tempLocation.Substring(0, indexOf64) + tempLocation.Substring(indexOf64 + 2, tempLocation.Length - indexOf64 - 2); 749 } 750 else if (indexOfFramework64 == -1 && architecture == DotNetFrameworkArchitecture.Bitness64) 751 { 752 // need to add 64 -- since this is a heuristic, we assume that we just need to append. 753 baseLocation = baseLocation + "64"; 754 } 755 // we don't need to do anything if it's DotNetFrameworkArchitecture.Current. 756 757 string[] directories; 758 759 if (directoryExists(baseLocation)) 760 { 761 directories = getDirectories(baseLocation, searchPattern); 762 } 763 else 764 { 765 // If we can't even find the base path, might as well give up now. 766 return null; 767 } 768 769 if (directories.Length == 0) 770 { 771 // Couldn't find the path, return a null. 772 return null; 773 } 774 775 // We don't care which one we choose, but we want to be predictible. 776 // The intention here is to choose the alphabetical maximum. 777 string max = directories[0]; 778 779 // the max.EndsWith condition: pre beta 2 versions of v3.5 have build number like v3.5.20111. 780 // This was removed in beta2 781 // We should favor \v3.5 over \v3.5.xxxxx 782 // versions previous to 2.0 have .xxxx version numbers. 3.0 and 3.5 do not. 783 if (!max.EndsWith(prefix, StringComparison.OrdinalIgnoreCase)) 784 { 785 for (int i = 1; i < directories.Length; ++i) 786 { 787 if (directories[i].EndsWith(prefix, StringComparison.OrdinalIgnoreCase)) 788 { 789 max = directories[i]; 790 break; 791 } 792 else if (String.Compare(directories[i], max, StringComparison.OrdinalIgnoreCase) > 0) 793 { 794 max = directories[i]; 795 } 796 } 797 } 798 799 return max; 800 } 801 802 /// <summary> 803 /// Determine the 32 bit program files directory, this is used for finding where the reference assemblies live. 804 /// </summary> GenerateProgramFiles32()805 internal static string GenerateProgramFiles32() 806 { 807 // With Mono, all we look for in Files32 should be found in the below location 808 if (!NativeMethodsShared.IsWindows && NativeMethodsShared.IsMono) 809 { 810 return Path.Combine(NativeMethodsShared.FrameworkBasePath, "xbuild"); 811 } 812 813 // On a 64 bit machine we always want to use the program files x86. If we are running as a 64 bit process then this variable will be set correctly 814 // If we are on a 32 bit machine or running as a 32 bit process then this variable will be null and the programFiles variable will be correct. 815 #if FEATURE_SPECIAL_FOLDERS 816 string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); 817 #else 818 string programFilesX86 = FileUtilities.GetFolderPath(FileUtilities.SpecialFolder.ProgramFilesX86); 819 #endif 820 if (String.IsNullOrEmpty(programFilesX86)) 821 { 822 // 32 bit box 823 programFilesX86 = programFiles; 824 } 825 826 return programFilesX86; 827 } 828 829 /// <summary> 830 /// Determine the 64-bit program files directory, used as the basis for MSBuildExtensionsPath64. 831 /// Returns null if we're not on a 64-bit machine 832 /// </summary> GenerateProgramFiles64()833 internal static string GenerateProgramFiles64() 834 { 835 // With Mono, all we look for in Files32 should be found in the below location 836 if (!NativeMethodsShared.IsWindows && NativeMethodsShared.IsMono) 837 { 838 return Path.Combine(NativeMethodsShared.FrameworkBasePath, "xbuild"); 839 } 840 841 string programFilesX64 = null; 842 if (string.Equals(programFiles, programFiles32)) 843 { 844 // either we're in a 32-bit window, or we're on a 32-bit machine. 845 // if we're on a 32-bit machine, ProgramW6432 won't exist 846 // if we're on a 64-bit machine, ProgramW6432 will point to the correct Program Files. 847 programFilesX64 = Environment.GetEnvironmentVariable("ProgramW6432"); 848 } 849 else 850 { 851 // 64-bit window on a 64-bit machine; %ProgramFiles% points to the 64-bit 852 // Program Files already. 853 programFilesX64 = programFiles; 854 } 855 856 return programFilesX64; 857 } 858 859 /// <summary> 860 /// Generate the path to the program files reference assembly location by taking in the program files special folder and then 861 /// using that path to generate the path to the reference assemblies location. 862 /// </summary> GenerateProgramFilesReferenceAssemblyRoot()863 internal static string GenerateProgramFilesReferenceAssemblyRoot() 864 { 865 string combinedPath = Environment.GetEnvironmentVariable("ReferenceAssemblyRoot"); 866 if (!String.IsNullOrEmpty(combinedPath)) 867 { 868 combinedPath = Path.GetFullPath(combinedPath); 869 if (Directory.Exists(combinedPath)) 870 { 871 return combinedPath; 872 } 873 } 874 875 combinedPath = NativeMethodsShared.IsWindows 876 ? Path.Combine(programFiles32, "Reference Assemblies\\Microsoft\\Framework") 877 : Path.Combine(NativeMethodsShared.FrameworkBasePath, "xbuild-frameworks"); 878 879 return Path.GetFullPath(combinedPath); 880 } 881 882 /// <summary> 883 /// Given a ToolsVersion, find the path to the build tools folder for that ToolsVersion. 884 /// </summary> 885 /// <param name="toolsVersion">The ToolsVersion to look up</param> 886 /// <param name="architecture">Target build tools architecture.</param> 887 /// <returns>The path to the build tools folder for that ToolsVersion, if it exists, or 888 /// null otherwise</returns> GeneratePathToBuildToolsForToolsVersion(string toolsVersion, DotNetFrameworkArchitecture architecture)889 internal static string GeneratePathToBuildToolsForToolsVersion(string toolsVersion, DotNetFrameworkArchitecture architecture) 890 { 891 if (string.Compare(toolsVersion, MSBuildConstants.CurrentToolsVersion, StringComparison.Ordinal) == 0) 892 { 893 return GetPathToBuildToolsFromEnvironment(architecture); 894 } 895 896 string toolsPath = null; 897 #if FEATURE_WIN32_REGISTRY 898 // If we're not looking for the current tools version, try the registry. 899 toolsPath = GetPathToBuildToolsFromRegistry(toolsVersion, architecture); 900 #endif 901 902 // If all else fails, always use the current environment. 903 return toolsPath ?? GetPathToBuildToolsFromEnvironment(architecture); 904 } 905 906 /// <summary> 907 /// Take the parts of the Target framework moniker and formulate the reference assembly path based on the the following pattern: 908 /// For a framework and version: 909 /// $(TargetFrameworkRootPath)\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion) 910 /// For a subtype: 911 /// $(TargetFrameworkRootPath)\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\SubType\$(TargetFrameworkSubType) 912 /// e.g.NET Framework v4.0 would locate its reference assemblies in: 913 /// \Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0 914 /// e.g.Silverlight v2.0 would locate its reference assemblies in: 915 /// \Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v2.0 916 /// e.g.NET Compact Framework v3.5, subtype PocketPC would locate its reference assemblies in: 917 /// \Program Files\Reference Assemblies\Microsoft\Framework\.NETCompactFramework\v3.5\SubType\PocketPC 918 /// </summary> 919 /// <returns>The path to the reference assembly location</returns> GenerateReferenceAssemblyPath(string targetFrameworkRootPath, FrameworkName frameworkName)920 internal static string GenerateReferenceAssemblyPath(string targetFrameworkRootPath, FrameworkName frameworkName) 921 { 922 ErrorUtilities.VerifyThrowArgumentNull(targetFrameworkRootPath, "targetFrameworkRootPath"); 923 ErrorUtilities.VerifyThrowArgumentNull(frameworkName, "frameworkName"); 924 925 try 926 { 927 string path = targetFrameworkRootPath; 928 path = Path.Combine(path, frameworkName.Identifier); 929 path = Path.Combine(path, "v" + frameworkName.Version.ToString()); 930 if (!String.IsNullOrEmpty(frameworkName.Profile)) 931 { 932 path = Path.Combine(path, "Profile"); 933 path = Path.Combine(path, frameworkName.Profile); 934 } 935 936 path = Path.GetFullPath(path); 937 return path; 938 } 939 catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) 940 { 941 ErrorUtilities.ThrowInvalidOperation("FrameworkLocationHelper.CouldNotGenerateReferenceAssemblyDirectory", targetFrameworkRootPath, frameworkName.ToString(), e.Message); 942 // The compiler does not see the massage above an as exception; 943 return null; 944 } 945 } 946 947 /// <summary> 948 /// Given a path, subtracts the requested number of directories and returns the result. 949 /// </summary> 950 /// <comments> 951 /// Internal only so that I can have the unit tests use it too, instead of duplicating the same code 952 /// </comments> RemoveDirectories(string path, int numberOfLevelsToRemove)953 internal static string RemoveDirectories(string path, int numberOfLevelsToRemove) 954 { 955 ErrorUtilities.VerifyThrowArgumentOutOfRange(numberOfLevelsToRemove > 0, "what are you doing passing a negative number to this function??"); 956 957 string fixedPath = null; 958 if (path != null) 959 { 960 // Record whether we had a slash or not so that we can tack it back on if necessary 961 var endedWithASlash = path.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) 962 || path.EndsWith( 963 Path.AltDirectorySeparatorChar.ToString(), 964 StringComparison.Ordinal); 965 966 DirectoryInfo fixedPathInfo = new DirectoryInfo(path); 967 for (int i = 0; i < numberOfLevelsToRemove; i++) 968 { 969 if (fixedPathInfo != null && fixedPathInfo.Parent != null) 970 { 971 fixedPathInfo = fixedPathInfo.Parent; 972 } 973 } 974 975 if (fixedPathInfo != null) 976 { 977 fixedPath = fixedPathInfo.FullName; 978 } 979 980 if (fixedPath != null && endedWithASlash) 981 { 982 fixedPath = fixedPath + Path.DirectorySeparatorChar; 983 } 984 } 985 986 return fixedPath; 987 } 988 989 /// <summary> 990 /// Look up the path to the build tools directory for the requested ToolsVersion in the .exe.config file of this executable 991 /// </summary> GetPathToBuildToolsFromEnvironment(DotNetFrameworkArchitecture architecture)992 private static string GetPathToBuildToolsFromEnvironment(DotNetFrameworkArchitecture architecture) 993 { 994 switch (architecture) 995 { 996 case DotNetFrameworkArchitecture.Bitness64: 997 return BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64; 998 case DotNetFrameworkArchitecture.Bitness32: 999 return BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32; 1000 default: 1001 return BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory; 1002 } 1003 } 1004 GetTargetFrameworkRootFallbackPaths(string toolsVersion)1005 internal static IList<string> GetTargetFrameworkRootFallbackPaths(string toolsVersion) 1006 { 1007 #if FEATURE_SYSTEM_CONFIGURATION 1008 return GetTargetFrameworkRootFallbackPathsFromConfigFor(toolsVersion); 1009 #else 1010 return new List<string>(); 1011 #endif 1012 } 1013 1014 #if FEATURE_SYSTEM_CONFIGURATION 1015 /// <summary> 1016 /// Returns the list of fallback search paths for looking up Target frameworks for the current OS, 1017 /// specified in app.config like: 1018 /// 1019 /// 1020 /// </summary> GetTargetFrameworkRootFallbackPathsFromConfigFor(string toolsVersion)1021 internal static IList<string> GetTargetFrameworkRootFallbackPathsFromConfigFor(string toolsVersion) 1022 { 1023 try 1024 { 1025 ToolsetElement toolset = GetToolsetElementFromConfigFor(toolsVersion); 1026 PropertyElement searchPathsfromConfiguration = toolset?.PropertyElements.GetElement("TargetFrameworkRootPathSearchPaths" + GetOSNameForTargetFrameworkRoot()); 1027 var searchPaths = searchPathsfromConfiguration?.Value; 1028 1029 if (searchPaths != null) 1030 { 1031 //FIXME: Split on unix 1032 var pathsList = searchPaths.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries); 1033 return pathsList; 1034 } 1035 } 1036 catch (ConfigurationException) 1037 { 1038 // may happen if the .exe.config contains bad data. Shouldn't ever happen in 1039 // practice since we'll long since have loaded all toolsets in the toolset loading 1040 // code and thrown errors to the user at that point if anything was invalid, but just 1041 // in case, just eat the exception here, so that we can go on to look in the registry 1042 // to see if there is any valid data there. 1043 } 1044 return new List<string>(); 1045 } 1046 #endif 1047 1048 #if FEATURE_SYSTEM_CONFIGURATION GetToolsetElementFromConfigFor(string toolsVersion)1049 private static ToolsetElement GetToolsetElementFromConfigFor(string toolsVersion) 1050 { 1051 ToolsetConfigurationSection configurationSection = null; 1052 1053 if (ToolsetConfigurationReaderHelpers.ConfigurationFileMayHaveToolsets()) 1054 { 1055 Configuration configuration = ConfigurationManager.OpenExeConfiguration( 1056 BuildEnvironmentHelper.Instance.CurrentMSBuildExePath); 1057 1058 configurationSection = ToolsetConfigurationReaderHelpers.ReadToolsetConfigurationSection(configuration); 1059 } 1060 1061 return configurationSection?.Toolsets.GetElement(toolsVersion); 1062 } 1063 #endif 1064 1065 /// <summary> 1066 /// OS name that can be used as the suffix for `TargetFrameworkRootPathSearchPaths` property name 1067 /// in app.config 1068 /// </summary> GetOSNameForTargetFrameworkRoot()1069 private static string GetOSNameForTargetFrameworkRoot() 1070 { 1071 if (NativeMethodsShared.IsWindows) 1072 { 1073 return "Windows"; 1074 } 1075 1076 if (NativeMethodsShared.IsOSX) 1077 { 1078 return "OSX"; 1079 } 1080 1081 return "Unix"; 1082 } 1083 1084 #if FEATURE_WIN32_REGISTRY 1085 /// <summary> 1086 /// Look up the path to the build tools directory in the registry for the requested ToolsVersion and requested architecture 1087 /// </summary> GetPathToBuildToolsFromRegistry(string toolsVersion, DotNetFrameworkArchitecture architecture)1088 private static string GetPathToBuildToolsFromRegistry(string toolsVersion, DotNetFrameworkArchitecture architecture) 1089 { 1090 string toolsVersionSpecificKey = ToolsVersionsRegistryPath + "\\" + toolsVersion; 1091 1092 RegistryView view = RegistryView.Default; 1093 1094 switch (architecture) 1095 { 1096 case DotNetFrameworkArchitecture.Bitness32: 1097 view = RegistryView.Registry32; 1098 break; 1099 case DotNetFrameworkArchitecture.Bitness64: 1100 view = RegistryView.Registry64; 1101 break; 1102 case DotNetFrameworkArchitecture.Current: 1103 view = RegistryView.Default; 1104 break; 1105 } 1106 1107 string toolsPath = FindRegistryValueUnderKey(toolsVersionSpecificKey, MSBuildConstants.ToolsPath, view); 1108 return toolsPath; 1109 } 1110 #endif 1111 1112 #endregion // Internal methods 1113 1114 #region Private methods 1115 1116 /// <summary> 1117 /// Will return the path to the dot net framework reference assemblies if they exist under the program files\reference assembies\microsoft\framework directory 1118 /// or null if the directory does not exist. 1119 /// </summary> GenerateReferenceAssemblyDirectory(string versionPrefix)1120 private static string GenerateReferenceAssemblyDirectory(string versionPrefix) 1121 { 1122 string programFilesReferenceAssemblyDirectory = Path.Combine(programFilesReferenceAssemblyLocation, versionPrefix); 1123 string referenceAssemblyDirectory = null; 1124 1125 if (Directory.Exists(programFilesReferenceAssemblyDirectory)) 1126 { 1127 referenceAssemblyDirectory = programFilesReferenceAssemblyDirectory; 1128 } 1129 1130 return referenceAssemblyDirectory; 1131 } 1132 1133 #if FEATURE_WIN32_REGISTRY 1134 /// <summary> 1135 /// Look for the given registry value under the given key. 1136 /// </summary> FindRegistryValueUnderKey( string registryBaseKeyName, string registryKeyName, RegistryView registryView = RegistryView.Default)1137 private static string FindRegistryValueUnderKey 1138 ( 1139 string registryBaseKeyName, 1140 string registryKeyName, 1141 RegistryView registryView = RegistryView.Default) 1142 { 1143 using (RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView)) 1144 using (RegistryKey subKey = baseKey.OpenSubKey(registryBaseKeyName)) 1145 { 1146 return subKey?.GetValue(registryKeyName)?.ToString(); 1147 } 1148 } 1149 #endif 1150 GetVisualStudioSpec(Version version)1151 private static VisualStudioSpec GetVisualStudioSpec(Version version) 1152 { 1153 ErrorUtilities.VerifyThrowArgument(s_visualStudioSpecDict.ContainsKey(version), "FrameworkLocationHelper.UnsupportedVisualStudioVersion", version); 1154 return s_visualStudioSpecDict[version]; 1155 } 1156 GetDotNetFrameworkSpec(Version version)1157 private static DotNetFrameworkSpec GetDotNetFrameworkSpec(Version version) 1158 { 1159 ErrorUtilities.VerifyThrowArgument(s_dotNetFrameworkSpecDict.ContainsKey(version), "FrameworkLocationHelper.UnsupportedFrameworkVersion", version); 1160 return s_dotNetFrameworkSpecDict[version]; 1161 } 1162 1163 /// <summary> 1164 /// Helper method to create an instance of <see cref="DotNetFrameworkSpec"/> for .net v4.x, 1165 /// because most of attributes are the same for v4.x versions. 1166 /// </summary> 1167 /// <param name="version">.net framework version.</param> 1168 /// <param name="visualStudioVersion">Version of Visual Studio</param> 1169 /// <returns></returns> CreateDotNetFrameworkSpecForV4(Version version, Version visualStudioVersion)1170 private static DotNetFrameworkSpec CreateDotNetFrameworkSpecForV4(Version version, Version visualStudioVersion) 1171 { 1172 return new DotNetFrameworkSpec( 1173 version, 1174 dotNetFrameworkRegistryKey: dotNetFrameworkSetupRegistryPath + "\\v4\\Full", 1175 dotNetFrameworkSetupRegistryInstalledName: "Install", 1176 dotNetFrameworkVersionFolderPrefix: NativeMethodsShared.IsWindows ? "v4.0" : "v4.5", 1177 dotNetFrameworkSdkRegistryToolsKey: "WinSDK-NetFx40Tools-x86", 1178 dotNetFrameworkSdkRegistryInstallationFolderName: "InstallationFolder", 1179 hasMSBuild: true, 1180 visualStudioVersion: visualStudioVersion); 1181 } 1182 RedirectVersionsIfNecessary(ref Version dotNetFrameworkVersion, ref Version visualStudioVersion)1183 private static void RedirectVersionsIfNecessary(ref Version dotNetFrameworkVersion, ref Version visualStudioVersion) 1184 { 1185 if (dotNetFrameworkVersion == dotNetFrameworkVersion45 && visualStudioVersion == visualStudioVersion100) 1186 { 1187 // There is no VS10 equivalent -- so just return the VS11 version 1188 visualStudioVersion = visualStudioVersion110; 1189 return; 1190 } 1191 1192 if (dotNetFrameworkVersion == dotNetFrameworkVersion35 && visualStudioVersion > visualStudioVersion110) 1193 { 1194 // Fall back to Dev11 location -- 3.5 tools MSI was reshipped unchanged, so there 1195 // essentially are no 12-specific 3.5 tools. 1196 visualStudioVersion = visualStudioVersion110; 1197 return; 1198 } 1199 } 1200 1201 #endregion 1202 1203 private class VisualStudioSpec 1204 { 1205 /// <summary> 1206 /// The key in registry to indicate the corresponding .net framework in this visual studio. 1207 /// i.e. 'v8.0A' for VS11. 1208 /// </summary> 1209 private readonly string _dotNetFrameworkSdkRegistryKey; 1210 VisualStudioSpec( Version version, string dotNetFrameworkSdkRegistryKey, string windowsSdkRegistryKey, string windowsSdkRegistryInstallationFolderName, Version[] supportedDotNetFrameworkVersions)1211 public VisualStudioSpec( 1212 Version version, 1213 string dotNetFrameworkSdkRegistryKey, 1214 string windowsSdkRegistryKey, 1215 string windowsSdkRegistryInstallationFolderName, 1216 Version[] supportedDotNetFrameworkVersions) 1217 { 1218 Version = version; 1219 _dotNetFrameworkSdkRegistryKey = dotNetFrameworkSdkRegistryKey; 1220 WindowsSdkRegistryKey = windowsSdkRegistryKey; 1221 WindowsSdkRegistryInstallationFolderName = windowsSdkRegistryInstallationFolderName; 1222 SupportedDotNetFrameworkVersions = supportedDotNetFrameworkVersions; 1223 } 1224 1225 /// <summary> 1226 /// The version of this visual studio. 1227 /// </summary> 1228 public Version Version { get; } 1229 1230 /// <summary> 1231 /// The list of supported .net framework versions in this visual studio. 1232 /// </summary> 1233 public Version[] SupportedDotNetFrameworkVersions { get; } 1234 1235 /// <summary> 1236 /// The key in registry to indicate the corresponding windows sdk, i.e. "v8.0" for VS11. 1237 /// </summary> 1238 public string WindowsSdkRegistryKey { get; } 1239 1240 /// <summary> 1241 /// The name in registry to indicate the sdk installation folder path, i.e. "InstallationFolder" for windows v8.0. 1242 /// </summary> 1243 public string WindowsSdkRegistryInstallationFolderName { get; } 1244 1245 /// <summary> 1246 /// The key in the registry to indicate the corresponding .net framework in this visual studio. 1247 /// i.e. 'v8.0A' for VS11. 1248 /// </summary> GetDotNetFrameworkSdkRegistryKey(Version dotNetSdkVersion)1249 public string GetDotNetFrameworkSdkRegistryKey(Version dotNetSdkVersion) 1250 { 1251 string sdkVersionFolder = "4.6"; // Default for back-compat 1252 1253 if (dotNetSdkVersion == dotNetFrameworkVersion471) 1254 { 1255 sdkVersionFolder = "4.7.1"; 1256 } 1257 if (dotNetSdkVersion == dotNetFrameworkVersion47) 1258 { 1259 sdkVersionFolder = "4.7"; 1260 } 1261 else if (dotNetSdkVersion == dotNetFrameworkVersion461) 1262 { 1263 sdkVersionFolder = "4.6.1"; 1264 } 1265 else if (dotNetSdkVersion == dotNetFrameworkVersion462) 1266 { 1267 sdkVersionFolder = "4.6.2"; 1268 } 1269 1270 // If the path is formatted to include a version number if we need to include that. 1271 // (e.g. NETFXSDK\{0} should be NETFXSDK\4.6 or NETFXSDK\4.6.1) 1272 // Note: before VS2015 this key was the same per instance of VS and didn't need to change. 1273 // In that case the string will not contain a format item and will not be modified. 1274 return string.Format(_dotNetFrameworkSdkRegistryKey, sdkVersionFolder); 1275 } 1276 } 1277 1278 private class DotNetFrameworkSpec 1279 { 1280 private const string HKLM = "HKEY_LOCAL_MACHINE"; 1281 private const string MicrosoftSDKsRegistryKey = @"SOFTWARE\Microsoft\Microsoft SDKs"; 1282 1283 /// <summary> 1284 /// The registry key of this .net framework, i.e. "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" for .net v4.5. 1285 /// </summary> 1286 private readonly string _dotNetFrameworkRegistryKey; 1287 1288 /// <summary> 1289 /// The name in registry to indicate that this .net framework is installed, i.e. "Install" for .net v4.5. 1290 /// </summary> 1291 private readonly string _dotNetFrameworkSetupRegistryInstalledName; 1292 1293 /// <summary> 1294 /// The key in registry to indicate the sdk tools folder, i.e. "WinSDK-NetFx40Tools-x86" for .net v4.5. 1295 /// </summary> 1296 private readonly string _dotNetFrameworkSdkRegistryToolsKey; 1297 1298 /// <summary> 1299 /// The version of visual studio that shipped with this .net framework. 1300 /// </summary> 1301 private readonly Version _visualStudioVersion; 1302 1303 /// <summary> 1304 /// Does this .net framework include MSBuild? 1305 /// </summary> 1306 private readonly bool _hasMsBuild; 1307 1308 /// <summary> 1309 /// Cached paths of .net framework on different architecture. 1310 /// </summary> 1311 private readonly ConcurrentDictionary<DotNetFrameworkArchitecture, string> _pathsToDotNetFramework; 1312 1313 /// <summary> 1314 /// Cached paths of .net framework sdk tools folder path on different visual studio version. 1315 /// </summary> 1316 private readonly ConcurrentDictionary<Version, string> _pathsToDotNetFrameworkSdkTools; 1317 1318 #if FEATURE_WIN32_REGISTRY 1319 /// <summary> 1320 /// Cached path of the corresponding windows sdk. 1321 /// </summary> 1322 private string _pathToWindowsSdk; 1323 #endif 1324 1325 /// <summary> 1326 /// Cached path of .net framework reference assemblies. 1327 /// </summary> 1328 protected string _pathToDotNetFrameworkReferenceAssemblies; 1329 DotNetFrameworkSpec( Version version, string dotNetFrameworkRegistryKey, string dotNetFrameworkSetupRegistryInstalledName, string dotNetFrameworkVersionFolderPrefix, string dotNetFrameworkSdkRegistryToolsKey, string dotNetFrameworkSdkRegistryInstallationFolderName, bool hasMSBuild = true, Version visualStudioVersion = null)1330 public DotNetFrameworkSpec( 1331 Version version, 1332 string dotNetFrameworkRegistryKey, 1333 string dotNetFrameworkSetupRegistryInstalledName, 1334 string dotNetFrameworkVersionFolderPrefix, 1335 string dotNetFrameworkSdkRegistryToolsKey, 1336 string dotNetFrameworkSdkRegistryInstallationFolderName, 1337 bool hasMSBuild = true, 1338 Version visualStudioVersion = null) 1339 { 1340 this.Version = version; 1341 this._visualStudioVersion = visualStudioVersion; 1342 this._dotNetFrameworkRegistryKey = dotNetFrameworkRegistryKey; 1343 this._dotNetFrameworkSetupRegistryInstalledName = dotNetFrameworkSetupRegistryInstalledName; 1344 this.DotNetFrameworkFolderPrefix = dotNetFrameworkVersionFolderPrefix; 1345 this._dotNetFrameworkSdkRegistryToolsKey = dotNetFrameworkSdkRegistryToolsKey; 1346 this.DotNetFrameworkSdkRegistryInstallationFolderName = dotNetFrameworkSdkRegistryInstallationFolderName; 1347 this._hasMsBuild = hasMSBuild; 1348 this._pathsToDotNetFramework = new ConcurrentDictionary<DotNetFrameworkArchitecture, string>(); 1349 this._pathsToDotNetFrameworkSdkTools = new ConcurrentDictionary<Version, string>(); 1350 } 1351 1352 /// <summary> 1353 /// The version of this .net framework. 1354 /// </summary> 1355 public Version Version { get; } 1356 1357 /// <summary> 1358 /// The name in registry to indicate the sdk installation folder path, i.e. "InstallationFolder" for .net v4.5. 1359 /// </summary> 1360 public string DotNetFrameworkSdkRegistryInstallationFolderName { get; } 1361 1362 /// <summary> 1363 /// Folder prefix, i.e. v4.0 for .net v4.5. 1364 /// </summary> 1365 public string DotNetFrameworkFolderPrefix { get; } 1366 1367 /// <summary> 1368 /// Get the FrameworkName for this version of the .NET Framework. 1369 /// </summary> 1370 private FrameworkName FrameworkName => new FrameworkName(dotNetFrameworkIdentifier, this.Version); 1371 1372 /// <summary> 1373 /// Gets the full registry key of this .net framework Sdk for the given visual studio version. 1374 /// i.e. "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0A\WinSDK-NetFx40Tools-x86" for .net v4.5 on VS11. 1375 /// </summary> GetDotNetFrameworkSdkRootRegistryKey(VisualStudioSpec visualStudioSpec)1376 public virtual string GetDotNetFrameworkSdkRootRegistryKey(VisualStudioSpec visualStudioSpec) 1377 { 1378 return string.Join(@"\", HKLM, MicrosoftSDKsRegistryKey, visualStudioSpec.GetDotNetFrameworkSdkRegistryKey(Version), _dotNetFrameworkSdkRegistryToolsKey); 1379 } 1380 1381 /// <summary> 1382 /// Gets the full path of .net framework for the given architecture. 1383 /// </summary> GetPathToDotNetFramework(DotNetFrameworkArchitecture architecture)1384 public virtual string GetPathToDotNetFramework(DotNetFrameworkArchitecture architecture) 1385 { 1386 #if !FEATURE_INSTALLED_MSBUILD 1387 return null; 1388 #else 1389 string cachedPath; 1390 if (this._pathsToDotNetFramework.TryGetValue(architecture, out cachedPath)) 1391 { 1392 return cachedPath; 1393 } 1394 1395 // Otherwise, check to see if we're even installed. If not, return null -- no point in setting the static 1396 // variables to null when that's what they are already. 1397 if (NativeMethodsShared.IsWindows) 1398 { 1399 if (!CheckForFrameworkInstallation( 1400 this._dotNetFrameworkRegistryKey, 1401 this._dotNetFrameworkSetupRegistryInstalledName)) 1402 { 1403 return null; 1404 } 1405 } 1406 1407 // We're installed and we haven't found this framework path yet -- so find it! 1408 string generatedPathToDotNetFramework = 1409 FindDotNetFrameworkPath( 1410 Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName), 1411 this.DotNetFrameworkFolderPrefix, 1412 Directory.Exists, 1413 Directory.GetDirectories, 1414 architecture); 1415 1416 // .net was improperly uninstalled: msbuild.exe isn't there 1417 if (this._hasMsBuild && 1418 generatedPathToDotNetFramework != null && 1419 !File.Exists(Path.Combine(generatedPathToDotNetFramework, NativeMethodsShared.IsWindows ? "MSBuild.exe" : "mcs.exe"))) 1420 { 1421 return null; 1422 } 1423 1424 if (!string.IsNullOrEmpty(generatedPathToDotNetFramework)) 1425 { 1426 _pathsToDotNetFramework[architecture] = generatedPathToDotNetFramework; 1427 } 1428 1429 return generatedPathToDotNetFramework; 1430 #endif 1431 } 1432 1433 /// <summary> 1434 /// Gets the full path of .net framework sdk tools for the given visual studio version. 1435 /// i.e. "C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\" for .net v4.5 on VS11. 1436 /// </summary> GetPathToDotNetFrameworkSdkTools(VisualStudioSpec visualStudioSpec)1437 public virtual string GetPathToDotNetFrameworkSdkTools(VisualStudioSpec visualStudioSpec) 1438 { 1439 string cachedPath; 1440 if (this._pathsToDotNetFrameworkSdkTools.TryGetValue(visualStudioSpec.Version, out cachedPath)) 1441 { 1442 return cachedPath; 1443 } 1444 1445 string generatedPathToDotNetFrameworkSdkTools = null; 1446 1447 if (NativeMethodsShared.IsUnixLike) 1448 { 1449 string frameworkPath = NativeMethodsShared.FrameworkBasePath; 1450 if (!string.IsNullOrEmpty(frameworkPath)) 1451 { 1452 frameworkPath = Path.Combine(frameworkPath, this.Version.ToString()); 1453 } 1454 1455 if (!string.IsNullOrEmpty(frameworkPath) && Directory.Exists(frameworkPath)) 1456 { 1457 generatedPathToDotNetFrameworkSdkTools = frameworkPath; 1458 } 1459 } 1460 #if FEATURE_WIN32_REGISTRY 1461 else 1462 { 1463 string registryPath = string.Join( 1464 @"\", 1465 MicrosoftSDKsRegistryKey, 1466 visualStudioSpec.GetDotNetFrameworkSdkRegistryKey(Version), 1467 this._dotNetFrameworkSdkRegistryToolsKey); 1468 1469 // For the Dev10 SDK, we check the registry that corresponds to the current process' bitness, rather than 1470 // always the 32-bit one the way we do for Dev11 and onward, since that's what we did in Dev10 as well. 1471 // As of Dev11, the SDK reg keys are installed in the 32-bit registry. 1472 RegistryView registryView = visualStudioSpec.Version == visualStudioVersion100 ? RegistryView.Default : RegistryView.Registry32; 1473 1474 generatedPathToDotNetFrameworkSdkTools = FindRegistryValueUnderKey( 1475 registryPath, 1476 this.DotNetFrameworkSdkRegistryInstallationFolderName, 1477 registryView); 1478 1479 if (string.IsNullOrEmpty(generatedPathToDotNetFrameworkSdkTools)) 1480 { 1481 // Fallback mechanisms. 1482 1483 // Try to find explicit fallback rule. 1484 // i.e. v4.5.1 on VS12 fallbacks to v4.5 on VS12. 1485 bool foundExplicitRule = false; 1486 for (int i = 0; i < s_explicitFallbackRulesForPathToDotNetFrameworkSdkTools.GetLength(0); ++i) 1487 { 1488 var trigger = s_explicitFallbackRulesForPathToDotNetFrameworkSdkTools[i, 0]; 1489 if (trigger.Item1 == this.Version && trigger.Item2 == visualStudioSpec.Version) 1490 { 1491 foundExplicitRule = true; 1492 var fallback = s_explicitFallbackRulesForPathToDotNetFrameworkSdkTools[i, 1]; 1493 generatedPathToDotNetFrameworkSdkTools = FallbackToPathToDotNetFrameworkSdkToolsInPreviousVersion( 1494 fallback.Item1, 1495 fallback.Item2); 1496 break; 1497 } 1498 } 1499 1500 // Otherwise, fallback to previous VS. 1501 // i.e. fallback to v110 if the current visual studio version is v120. 1502 if (!foundExplicitRule) 1503 { 1504 int index = Array.IndexOf(s_visualStudioSpecs, visualStudioSpec); 1505 if (index > 0) 1506 { 1507 // The items in the array "visualStudioSpecs" must be ordered by version. That would allow us to fallback to the previous visual studio version easily. 1508 VisualStudioSpec fallbackVisualStudioSpec = s_visualStudioSpecs[index - 1]; 1509 generatedPathToDotNetFrameworkSdkTools = FallbackToPathToDotNetFrameworkSdkToolsInPreviousVersion( 1510 this.Version, 1511 fallbackVisualStudioSpec.Version); 1512 } 1513 } 1514 } 1515 } 1516 #endif 1517 if (string.IsNullOrEmpty(generatedPathToDotNetFrameworkSdkTools)) 1518 { 1519 // Fallback to "default" ultimately. 1520 generatedPathToDotNetFrameworkSdkTools = FallbackToDefaultPathToDotNetFrameworkSdkTools(this.Version); 1521 } 1522 1523 if (!string.IsNullOrEmpty(generatedPathToDotNetFrameworkSdkTools)) 1524 { 1525 this._pathsToDotNetFrameworkSdkTools[visualStudioSpec.Version] = generatedPathToDotNetFrameworkSdkTools; 1526 } 1527 1528 return generatedPathToDotNetFrameworkSdkTools; 1529 } 1530 1531 /// <summary> 1532 /// Gets the full path of .net framework sdk. 1533 /// i.e. "C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\" for .net v4.5 on VS11. 1534 /// </summary> GetPathToDotNetFrameworkSdk(VisualStudioSpec visualStudioSpec)1535 public virtual string GetPathToDotNetFrameworkSdk(VisualStudioSpec visualStudioSpec) 1536 { 1537 string pathToBinRoot = this.GetPathToDotNetFrameworkSdkTools(visualStudioSpec); 1538 if (NativeMethodsShared.IsWindows) 1539 { 1540 pathToBinRoot = RemoveDirectories(pathToBinRoot, 2); 1541 } 1542 1543 return pathToBinRoot; 1544 } 1545 1546 /// <summary> 1547 /// Gets the full path of reference assemblies folder. 1548 /// i.e. "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\" for .net v4.5. 1549 /// </summary> GetPathToDotNetFrameworkReferenceAssemblies()1550 public virtual string GetPathToDotNetFrameworkReferenceAssemblies() 1551 { 1552 if (this._pathToDotNetFrameworkReferenceAssemblies == null) 1553 { 1554 // when a user requests the 40 reference assembly path we don't need to read the redist list because we will not be chaining so we may as well just 1555 // generate the path and save us some time. 1556 string referencePath = GenerateReferenceAssemblyPath(FrameworkLocationHelper.programFilesReferenceAssemblyLocation, this.FrameworkName); 1557 if (Directory.Exists(referencePath)) 1558 { 1559 this._pathToDotNetFrameworkReferenceAssemblies = FileUtilities.EnsureTrailingSlash(referencePath); 1560 } 1561 } 1562 1563 return this._pathToDotNetFrameworkReferenceAssemblies; 1564 } 1565 1566 /// <summary> 1567 /// Gets the full path of the corresponding windows sdk shipped with this .net framework. 1568 /// i.e. "C:\Program Files (x86)\Windows Kits\8.0\" for v8.0 (shipped with .net v4.5 and VS11). 1569 /// </summary> GetPathToWindowsSdk()1570 public virtual string GetPathToWindowsSdk() 1571 { 1572 #if FEATURE_WIN32_REGISTRY 1573 if (this._pathToWindowsSdk == null) 1574 { 1575 ErrorUtilities.VerifyThrowArgument(this._visualStudioVersion != null, "FrameworkLocationHelper.UnsupportedFrameworkVersionForWindowsSdk", this.Version); 1576 1577 var visualStudioSpec = GetVisualStudioSpec(this._visualStudioVersion); 1578 1579 if (string.IsNullOrEmpty(visualStudioSpec.WindowsSdkRegistryKey) || string.IsNullOrEmpty(visualStudioSpec.WindowsSdkRegistryInstallationFolderName)) 1580 { 1581 ErrorUtilities.ThrowArgument("FrameworkLocationHelper.UnsupportedFrameworkVersionForWindowsSdk", this.Version); 1582 } 1583 1584 string registryPath = string.Join(@"\", MicrosoftSDKsRegistryKey, "Windows", visualStudioSpec.WindowsSdkRegistryKey); 1585 1586 // As of Dev11, the SDK reg keys are installed in the 32-bit registry. 1587 this._pathToWindowsSdk = FindRegistryValueUnderKey( 1588 registryPath, 1589 visualStudioSpec.WindowsSdkRegistryInstallationFolderName, 1590 RegistryView.Registry32); 1591 } 1592 return this._pathToWindowsSdk; 1593 #else 1594 return null; 1595 #endif 1596 } 1597 FallbackToPathToDotNetFrameworkSdkToolsInPreviousVersion(Version dotNetFrameworkVersion, Version visualStudioVersion)1598 private static string FallbackToPathToDotNetFrameworkSdkToolsInPreviousVersion(Version dotNetFrameworkVersion, Version visualStudioVersion) 1599 { 1600 VisualStudioSpec visualStudioSpec; 1601 DotNetFrameworkSpec dotNetFrameworkSpec; 1602 if (s_visualStudioSpecDict.TryGetValue(visualStudioVersion, out visualStudioSpec) 1603 && s_dotNetFrameworkSpecDict.TryGetValue(dotNetFrameworkVersion, out dotNetFrameworkSpec) 1604 && visualStudioSpec.SupportedDotNetFrameworkVersions.Contains(dotNetFrameworkVersion)) 1605 { 1606 return dotNetFrameworkSpec.GetPathToDotNetFrameworkSdkTools(visualStudioSpec); 1607 } 1608 1609 return null; 1610 } 1611 FallbackToDefaultPathToDotNetFrameworkSdkTools(Version dotNetFrameworkVersion)1612 private static string FallbackToDefaultPathToDotNetFrameworkSdkTools(Version dotNetFrameworkVersion) 1613 { 1614 if (dotNetFrameworkVersion.Major == 4) 1615 { 1616 return PathToV4ToolsInFallbackDotNetFrameworkSdk; 1617 } 1618 1619 if (dotNetFrameworkVersion == dotNetFrameworkVersion35) 1620 { 1621 return PathToV35ToolsInFallbackDotNetFrameworkSdk; 1622 } 1623 1624 return null; 1625 } 1626 } 1627 1628 /// <summary> 1629 /// Specialized implementation for legacy .net framework v1.1 and v2.0. 1630 /// </summary> 1631 private class DotNetFrameworkSpecLegacy : DotNetFrameworkSpec 1632 { 1633 #if FEATURE_WIN32_REGISTRY 1634 private string _pathToDotNetFrameworkSdkTools; 1635 #endif 1636 DotNetFrameworkSpecLegacy( Version version, string dotNetFrameworkRegistryKey, string dotNetFrameworkSetupRegistryInstalledName, string dotNetFrameworkVersionFolderPrefix, string dotNetFrameworkSdkRegistryInstallationFolderName, bool hasMSBuild)1637 public DotNetFrameworkSpecLegacy( 1638 Version version, 1639 string dotNetFrameworkRegistryKey, 1640 string dotNetFrameworkSetupRegistryInstalledName, 1641 string dotNetFrameworkVersionFolderPrefix, 1642 string dotNetFrameworkSdkRegistryInstallationFolderName, 1643 bool hasMSBuild) 1644 : base(version, 1645 dotNetFrameworkRegistryKey, 1646 dotNetFrameworkSetupRegistryInstalledName, 1647 dotNetFrameworkVersionFolderPrefix, 1648 null, 1649 dotNetFrameworkSdkRegistryInstallationFolderName, 1650 hasMSBuild) 1651 { 1652 } 1653 1654 /// <summary> 1655 /// Gets the full registry key of this .net framework Sdk for the given visual studio version. 1656 /// i.e. "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework" for v1.1 and v2.0. 1657 /// </summary> GetDotNetFrameworkSdkRootRegistryKey(VisualStudioSpec visualStudioSpec)1658 public override string GetDotNetFrameworkSdkRootRegistryKey(VisualStudioSpec visualStudioSpec) 1659 { 1660 return fullDotNetFrameworkRegistryKey; 1661 } 1662 1663 /// <summary> 1664 /// Gets the full path of .net framework sdk tools for the given visual studio version. 1665 /// </summary> GetPathToDotNetFrameworkSdkTools(VisualStudioSpec visualStudioSpec)1666 public override string GetPathToDotNetFrameworkSdkTools(VisualStudioSpec visualStudioSpec) 1667 { 1668 #if FEATURE_WIN32_REGISTRY 1669 if (_pathToDotNetFrameworkSdkTools == null) 1670 { 1671 _pathToDotNetFrameworkSdkTools = FindRegistryValueUnderKey( 1672 dotNetFrameworkRegistryPath, 1673 this.DotNetFrameworkSdkRegistryInstallationFolderName); 1674 } 1675 1676 return _pathToDotNetFrameworkSdkTools; 1677 #else 1678 return null; 1679 #endif 1680 } 1681 1682 /// <summary> 1683 /// Gets the full path of .net framework sdk, which is the full path of .net framework sdk tools for v1.1 and v2.0. 1684 /// </summary> GetPathToDotNetFrameworkSdk(VisualStudioSpec visualStudioSpec)1685 public override string GetPathToDotNetFrameworkSdk(VisualStudioSpec visualStudioSpec) 1686 { 1687 return this.GetPathToDotNetFrameworkSdkTools(visualStudioSpec); 1688 } 1689 1690 /// <summary> 1691 /// Gets the full path of reference assemblies folder, which is the full path of .net framework for v1.1 and v2.0. 1692 /// </summary> GetPathToDotNetFrameworkReferenceAssemblies()1693 public override string GetPathToDotNetFrameworkReferenceAssemblies() 1694 { 1695 return this.GetPathToDotNetFramework(DotNetFrameworkArchitecture.Current); 1696 } 1697 } 1698 1699 /// <summary> 1700 /// Specialized implementation for legacy .net framework v3.0 and v3.5. 1701 /// </summary> 1702 private class DotNetFrameworkSpecV3 : DotNetFrameworkSpec 1703 { DotNetFrameworkSpecV3( Version version, string dotNetFrameworkRegistryKey, string dotNetFrameworkSetupRegistryInstalledName, string dotNetFrameworkVersionFolderPrefix, string dotNetFrameworkSdkRegistryToolsKey, string dotNetFrameworkSdkRegistryInstallationFolderName, bool hasMSBuild)1704 public DotNetFrameworkSpecV3( 1705 Version version, 1706 string dotNetFrameworkRegistryKey, 1707 string dotNetFrameworkSetupRegistryInstalledName, 1708 string dotNetFrameworkVersionFolderPrefix, 1709 string dotNetFrameworkSdkRegistryToolsKey, 1710 string dotNetFrameworkSdkRegistryInstallationFolderName, 1711 bool hasMSBuild) 1712 : base(version, 1713 dotNetFrameworkRegistryKey, 1714 dotNetFrameworkSetupRegistryInstalledName, 1715 dotNetFrameworkVersionFolderPrefix, 1716 dotNetFrameworkSdkRegistryToolsKey, 1717 dotNetFrameworkSdkRegistryInstallationFolderName, 1718 hasMSBuild) 1719 { 1720 } 1721 1722 /// <summary> 1723 /// Gets the full path of .net framework sdk. 1724 /// i.e. "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\" for .net v3.5 on VS11. 1725 /// </summary> GetPathToDotNetFrameworkSdk(VisualStudioSpec visualStudioSpec)1726 public override string GetPathToDotNetFrameworkSdk(VisualStudioSpec visualStudioSpec) 1727 { 1728 string pathToBinRoot = this.GetPathToDotNetFrameworkSdkTools(visualStudioSpec); 1729 pathToBinRoot = RemoveDirectories(pathToBinRoot, 1); 1730 return pathToBinRoot; 1731 } 1732 1733 /// <summary> 1734 /// Gets the full path of reference assemblies folder. 1735 /// i.e. "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\" for v3.5. 1736 /// </summary> GetPathToDotNetFrameworkReferenceAssemblies()1737 public override string GetPathToDotNetFrameworkReferenceAssemblies() 1738 { 1739 if (this._pathToDotNetFrameworkReferenceAssemblies== null) 1740 { 1741 #if FEATURE_WIN32_REGISTRY 1742 this._pathToDotNetFrameworkReferenceAssemblies = FindRegistryValueUnderKey( 1743 dotNetFrameworkAssemblyFoldersRegistryPath + "\\" + this.DotNetFrameworkFolderPrefix, 1744 referenceAssembliesRegistryValueName); 1745 #endif 1746 1747 if (this._pathToDotNetFrameworkReferenceAssemblies == null) 1748 { 1749 this._pathToDotNetFrameworkReferenceAssemblies = GenerateReferenceAssemblyDirectory(this.DotNetFrameworkFolderPrefix); 1750 } 1751 } 1752 1753 return this._pathToDotNetFrameworkReferenceAssemblies; 1754 } 1755 } 1756 } 1757 } 1758