| File: SdkToolsPathUtility.cs | Web Access |
| Project: src\msbuild\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.IO; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; #nullable disable namespace Microsoft.Build.Tasks { /// <summary> /// This class will provide the ability for classes given an SdkToolsPath and their tool name to find that tool. /// 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. /// </summary> internal static class SdkToolsPathUtility { /// <summary> /// Cache the file exists delegate which will determine if a file exists or not but will not eat the CAS exceptions. /// </summary> private static FileExists s_fileInfoExists; /// <summary> /// Provide a delegate which will do the correct file exists. /// </summary> internal static FileExists FileInfoExists { get { if (s_fileInfoExists == null) { s_fileInfoExists = FileExists; } return s_fileInfoExists; } } /// <summary> /// This method will take a sdkToolsPath and a toolName and return the path to the tool if it is found and exists. /// /// First the method will try and find the tool under the sdkToolsPath taking into account the current processor architecture /// 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). /// /// 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. /// </summary> /// <returns>Path including the toolName of the tool if found, null if it is not found</returns> internal static string GeneratePathToTool(FileExists fileExists, string currentArchitecture, string sdkToolsPath, string toolName, TaskLoggingHelper log, bool logErrorsAndWarnings) { // Null until we combine the toolname with the path. string pathToTool = null; if (!String.IsNullOrEmpty(sdkToolsPath)) { string processorSpecificToolDirectory; try { processorSpecificToolDirectory = GetProcessorSpecificToolDirectory(currentArchitecture, sdkToolsPath); pathToTool = Path.Combine(processorSpecificToolDirectory, toolName); if (!fileExists(pathToTool)) { // Try falling back to the x86 location if (currentArchitecture != ProcessorArchitecture.X86) { pathToTool = Path.Combine(sdkToolsPath, toolName); } } else { return pathToTool; } } catch (ArgumentException e) { // Catch exceptions from path.combine log.LogErrorWithCodeFromResources("General.SdkToolsPathError", toolName, e.Message); return null; } if (fileExists(pathToTool)) { return pathToTool; } else { if (logErrorsAndWarnings) { // Log an error indicating we could not find it in the processor specific architecture or x86 locations. // We could not find the tool at all, lot a error. log.LogWarningWithCodeFromResources("General.PlatformSDKFileNotFoundSdkToolsPath", toolName, processorSpecificToolDirectory, sdkToolsPath); } } } else { if (logErrorsAndWarnings) { log.LogMessageFromResources(MessageImportance.Low, "General.SdkToolsPathNotSpecifiedOrToolDoesNotExist", toolName, sdkToolsPath); } } // Fall back and see if we can find it with the toolsLocation helper methods. This is not optimal because // the location they are looking at is based on when the Microsoft.Build.Utilities.dll was compiled // but it is better than nothing. if (pathToTool == null || !fileExists(pathToTool)) { pathToTool = FindSDKToolUsingToolsLocationHelper(toolName); if (pathToTool == null && logErrorsAndWarnings) { log.LogErrorWithCodeFromResources("General.SdkToolsPathToolDoesNotExist", toolName, sdkToolsPath, ToolLocationHelper.GetDotNetFrameworkSdkRootRegistryKey(TargetDotNetFrameworkVersion.Latest, VisualStudioVersion.VersionLatest)); } } return pathToTool; } internal static string GetProcessorSpecificToolDirectory(string currentArchitecture, string sdkToolsPath) { return currentArchitecture switch { // There may not be an arm directory so we will fall back to the x86 tool location // but if there is then we should try and use it. ProcessorArchitecture.ARM => Path.Combine(sdkToolsPath, "arm"), ProcessorArchitecture.AMD64 => Path.Combine(sdkToolsPath, "x64"), ProcessorArchitecture.IA64 => Path.Combine(sdkToolsPath, "ia64"), _ => sdkToolsPath, }; } /// <summary> /// This method will take the toolName and use the Legacy ToolLocation helper methods to try and find the tool. /// 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). /// </summary> /// <param name="toolName">Name of the tool to find the sdk path for</param> /// <returns>A path to the tool or null if the path does not exist.</returns> internal static string FindSDKToolUsingToolsLocationHelper(string toolName) { // If it isn't there, we should find it in the SDK based on the version compiled into the utilities string pathToTool = ToolLocationHelper.GetPathToDotNetFrameworkSdkFile(toolName, TargetDotNetFrameworkVersion.Latest, VisualStudioVersion.VersionLatest); return pathToTool; } /// <summary> /// Provide a method which can be used with a delegate to provide a specific FileExists behavior. /// /// Use FileInfo instead of FileSystems.Default.FileExists(...) because the latter fails silently (by design) if CAS /// doesn't grant access. We want the security exception if there is going to be one. /// </summary> /// <returns>True if the file exists. False if it does not</returns> private static bool FileExists(string filePath) { return new FileInfo(filePath).Exists; } } }