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 // </copyright> 5 // <summary>Provide a helper class for tasks to find their tools if they are in the SDK</summary> 6 //----------------------------------------------------------------------- 7 8 using System; 9 using System.Collections.Generic; 10 using System.Text; 11 using System.IO; 12 using Microsoft.Build.Utilities; 13 using Microsoft.Build.Shared; 14 using Microsoft.Build.Framework; 15 16 namespace Microsoft.Build.Tasks 17 { 18 /// <summary> 19 /// This class will provide the ability for classes given an SdkToolsPath and their tool name to find that tool. 20 /// The tool will be looked for either under the SDKToolPath passed into the task or as fallback to look for the toolname using the toolslocation helper. 21 /// </summary> 22 internal static class SdkToolsPathUtility 23 { 24 /// <summary> 25 /// Cache the file exists delegate which will determine if a file exists or not but will not eat the CAS exceptions. 26 /// </summary> 27 private static FileExists s_fileInfoExists; 28 29 /// <summary> 30 /// Provide a delegate which will do the correct file exists. 31 /// </summary> 32 internal static FileExists FileInfoExists 33 { 34 get 35 { 36 if (s_fileInfoExists == null) 37 { 38 s_fileInfoExists = new FileExists(FileExists); 39 } 40 41 return s_fileInfoExists; 42 } 43 } 44 45 /// <summary> 46 /// This method will take a sdkToolsPath and a toolName and return the path to the tool if it is found and exists. 47 /// 48 /// First the method will try and find the tool under the sdkToolsPath taking into account the current processor architecture 49 /// If the tool could not be found the method will try and find the tool under the sdkToolsPath (which should point to the x86 sdk directory). 50 /// 51 /// Finally if the method has not found the tool yet it will fallback and use the toolslocation helper method to try and find the tool. 52 /// </summary> 53 /// <returns>Path including the toolName of the tool if found, null if it is not found</returns> GeneratePathToTool(FileExists fileExists, string currentArchitecture, string sdkToolsPath, string toolName, TaskLoggingHelper log, bool logErrorsAndWarnings)54 internal static string GeneratePathToTool(FileExists fileExists, string currentArchitecture, string sdkToolsPath, string toolName, TaskLoggingHelper log, bool logErrorsAndWarnings) 55 { 56 // Null until we combine the toolname with the path. 57 string pathToTool = null; 58 if (!String.IsNullOrEmpty(sdkToolsPath)) 59 { 60 string processorSpecificToolDirectory = String.Empty; 61 try 62 { 63 switch (currentArchitecture) 64 { 65 // There may not be an arm directory so we will fall back to the x86 tool location 66 // but if there is then we should try and use it. 67 case ProcessorArchitecture.ARM: 68 processorSpecificToolDirectory = Path.Combine(sdkToolsPath, "arm"); 69 break; 70 case ProcessorArchitecture.AMD64: 71 processorSpecificToolDirectory = Path.Combine(sdkToolsPath, "x64"); 72 break; 73 case ProcessorArchitecture.IA64: 74 processorSpecificToolDirectory = Path.Combine(sdkToolsPath, "ia64"); 75 break; 76 case ProcessorArchitecture.X86: 77 default: 78 processorSpecificToolDirectory = sdkToolsPath; 79 break; 80 } 81 82 pathToTool = Path.Combine(processorSpecificToolDirectory, toolName); 83 84 if (!fileExists(pathToTool)) 85 { 86 // Try falling back to the x86 location 87 if (currentArchitecture != ProcessorArchitecture.X86) 88 { 89 pathToTool = Path.Combine(sdkToolsPath, toolName); 90 } 91 } 92 else 93 { 94 return pathToTool; 95 } 96 } 97 catch (ArgumentException e) 98 { 99 // Catch exceptions from path.combine 100 log.LogErrorWithCodeFromResources("General.SdkToolsPathError", toolName, e.Message); 101 return null; 102 } 103 104 if (fileExists(pathToTool)) 105 { 106 return pathToTool; 107 } 108 else 109 { 110 if (logErrorsAndWarnings) 111 { 112 // Log an error indicating we could not find it in the processor specific architecture or x86 locations. 113 // We could not find the tool at all, lot a error. 114 log.LogWarningWithCodeFromResources("General.PlatformSDKFileNotFoundSdkToolsPath", toolName, processorSpecificToolDirectory, sdkToolsPath); 115 } 116 } 117 } 118 else 119 { 120 if (logErrorsAndWarnings) 121 { 122 log.LogMessageFromResources(MessageImportance.Low, "General.SdkToolsPathNotSpecifiedOrToolDoesNotExist", toolName, sdkToolsPath); 123 } 124 } 125 126 // Fall back and see if we can find it with the toolsLocation helper methods. This is not optimal because 127 // the location they are looking at is based on when the Microsoft.Build.Utilities.dll was compiled 128 // but it is better than nothing. 129 if (null == pathToTool || !fileExists(pathToTool)) 130 { 131 pathToTool = FindSDKToolUsingToolsLocationHelper(toolName); 132 133 if (pathToTool == null && logErrorsAndWarnings) 134 { 135 log.LogErrorWithCodeFromResources("General.SdkToolsPathToolDoesNotExist", toolName, sdkToolsPath, ToolLocationHelper.GetDotNetFrameworkSdkRootRegistryKey(TargetDotNetFrameworkVersion.Latest, VisualStudioVersion.VersionLatest)); 136 } 137 } 138 139 return pathToTool; 140 } 141 142 /// <summary> 143 /// This method will take the toolName and use the Legacy ToolLocation helper methods to try and find the tool. 144 /// This is a last ditch effort to find the tool when we cannot find it using the passed in SDKToolsPath (in either the x86 or processor specific directories). 145 /// </summary> 146 /// <param name="toolName">Name of the tool to find the sdk path for</param> 147 /// <returns>A path to the tool or null if the path does not exist.</returns> FindSDKToolUsingToolsLocationHelper(string toolName)148 internal static string FindSDKToolUsingToolsLocationHelper(string toolName) 149 { 150 // If it isn't there, we should find it in the SDK based on the version compiled into the utilities 151 string pathToTool = ToolLocationHelper.GetPathToDotNetFrameworkSdkFile(toolName, TargetDotNetFrameworkVersion.Latest, VisualStudioVersion.VersionLatest); 152 return pathToTool; 153 } 154 155 /// <summary> 156 /// Provide a method which can be used with a delegate to provide a specific FileExists behavior. 157 /// 158 /// Use FileInfo instead of File.Exists(...) because the latter fails silently (by design) if CAS 159 /// doesn't grant access. We want the security exception if there is going to be one. 160 /// </summary> 161 /// <returns>True if the file exists. False if it does not</returns> FileExists(string filePath)162 private static bool FileExists(string filePath) 163 { 164 return new FileInfo(filePath).Exists; 165 } 166 } 167 } 168