|
// 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.Collections.Generic;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Tasks.AssemblyFoldersFromConfig;
using Microsoft.Build.Utilities;
#nullable disable
namespace Microsoft.Build.Tasks
{
internal readonly record struct DirectoryWithParentAssembly(string Directory, string ParentAssembly);
/// <summary>
/// Utility class encapsulates steps to resolve assembly references.
/// For example, this class has the code that will take:
///
/// System.Xml
///
/// and turn it into:
///
/// [path-to-frameworks]\System.Xml.dll
///
///
/// </summary>
internal static class AssemblyResolution
{
/// <summary>
/// Implementation guts for ResolveReference.
/// </summary>
/// <param name="jaggedResolvers">The array of resolvers to search with.</param>
/// <param name="assemblyName">The assembly name to look up.</param>
/// <param name="sdkName"></param>
/// <param name="rawFileNameCandidate">The file name to match if {RawFileName} is seen. (May be null).</param>
/// <param name="isPrimaryProjectReference">True if this is a primary reference directly from the project file.</param>
/// <param name="isImmutableFrameworkReference">True if <paramref name="rawFileNameCandidate"/> is immutable and guaranteed to exist.</param>
/// <param name="wantSpecificVersion"></param>
/// <param name="executableExtensions">The filename extension of the assembly. Must be this or its no match.</param>
/// <param name="hintPath">This reference's hintpath</param>
/// <param name="assemblyFolderKey">Like "hklm\Vendor RegKey" as provided to a reference by the <AssemblyFolderKey> on the reference in the project.</param>
/// <param name="assembliesConsideredAndRejected">Receives the list of locations that this function tried to find the assembly. May be "null".</param>
/// <param name="resolvedSearchPath">Receives the searchPath that the reference was resolved at. Empty if not resolved.</param>
/// <param name="userRequestedSpecificFile"> This will be true if the user requested a specific file.</param>
/// <returns>The resolved path</returns>
internal static string ResolveReference(
IEnumerable<Resolver[]> jaggedResolvers,
AssemblyNameExtension assemblyName,
string sdkName,
string rawFileNameCandidate,
bool isPrimaryProjectReference,
bool isImmutableFrameworkReference,
bool wantSpecificVersion,
string[] executableExtensions,
string hintPath,
string assemblyFolderKey,
List<ResolutionSearchLocation> assembliesConsideredAndRejected,
out string resolvedSearchPath,
out bool userRequestedSpecificFile)
{
// Initialize outs.
userRequestedSpecificFile = false;
resolvedSearchPath = String.Empty;
// Search each group of resolvers
foreach (Resolver[] resolvers in jaggedResolvers)
{
// Tolerate null resolvers.
if (resolvers == null)
{
break;
}
// Search each searchpath.
foreach (Resolver resolver in resolvers)
{
if
(
resolver.Resolve(
assemblyName,
sdkName,
rawFileNameCandidate,
isPrimaryProjectReference,
isImmutableFrameworkReference,
wantSpecificVersion,
executableExtensions,
hintPath,
assemblyFolderKey,
assembliesConsideredAndRejected,
out string fileLocation,
out userRequestedSpecificFile))
{
resolvedSearchPath = resolver.SearchPath;
return fileLocation;
}
}
}
return null;
}
#if FEATURE_WIN32_REGISTRY
/// <summary>
/// Compile search paths into an array of resolvers.
/// </summary>
/// <param name="buildEngine"></param>
/// <param name="searchPaths"></param>
/// <param name="candidateAssemblyFiles">Paths to assembly files mentioned in the project.</param>
/// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64, the processor architecture being targetted.</param>
/// <param name="frameworkPaths">Paths to FX folders.</param>
/// <param name="fileExists"></param>
/// <param name="getAssemblyName"></param>
/// <param name="getRegistrySubKeyNames"></param>
/// <param name="getRegistrySubKeyDefaultValue"></param>
/// <param name="openBaseKey"></param>
/// <param name="installedAssemblies"></param>
/// <param name="getRuntimeVersion"></param>
/// <param name="targetedRuntimeVersion"></param>
/// <param name="getAssemblyPathInGac"></param>
/// <param name="log"></param>
/// <returns></returns>
#else
/// <summary>
/// Compile search paths into an array of resolvers.
/// </summary>
/// <param name="buildEngine"></param>
/// <param name="searchPaths"></param>
/// <param name="candidateAssemblyFiles">Paths to assembly files mentioned in the project.</param>
/// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64, the processor architecture being targetted.</param>
/// <param name="frameworkPaths">Paths to FX folders.</param>
/// <param name="fileExists"></param>
/// <param name="getAssemblyName"></param>
/// <param name="installedAssemblies"></param>
/// <param name="getRuntimeVersion"></param>
/// <param name="targetedRuntimeVersion"></param>
/// <param name="getAssemblyPathInGac"></param>
/// <param name="log"></param>
/// <returns></returns>
#endif
public static Resolver[] CompileSearchPaths(
IBuildEngine buildEngine,
string[] searchPaths,
string[] candidateAssemblyFiles,
System.Reflection.ProcessorArchitecture targetProcessorArchitecture,
string[] frameworkPaths,
FileExists fileExists,
GetAssemblyName getAssemblyName,
#if FEATURE_WIN32_REGISTRY
GetRegistrySubKeyNames getRegistrySubKeyNames,
GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue,
OpenBaseKey openBaseKey,
#endif
InstalledAssemblies installedAssemblies,
GetAssemblyRuntimeVersion getRuntimeVersion,
Version targetedRuntimeVersion,
GetAssemblyPathInGac getAssemblyPathInGac,
TaskLoggingHelper log)
{
var resolvers = new Resolver[searchPaths.Length];
for (int p = 0; p < searchPaths.Length; ++p)
{
string basePath = searchPaths[p];
// Was {HintPathFromItem} specified? If so, take the Item's
// HintPath property.
if (String.Equals(basePath, AssemblyResolutionConstants.hintPathSentinel, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new HintPathResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
}
else if (String.Equals(basePath, AssemblyResolutionConstants.frameworkPathSentinel, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new FrameworkPathResolver(frameworkPaths, installedAssemblies, searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
}
else if (String.Equals(basePath, AssemblyResolutionConstants.rawFileNameSentinel, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new RawFilenameResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
}
else if (String.Equals(basePath, AssemblyResolutionConstants.candidateAssemblyFilesSentinel, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new CandidateAssemblyFilesResolver(candidateAssemblyFiles, searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
}
#if FEATURE_GAC
else if (String.Equals(basePath, AssemblyResolutionConstants.gacSentinel, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new GacResolver(targetProcessorArchitecture, searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion, getAssemblyPathInGac);
}
#endif
else if (String.Equals(basePath, AssemblyResolutionConstants.assemblyFoldersSentinel, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new AssemblyFoldersResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
}
#if FEATURE_WIN32_REGISTRY
// Check for AssemblyFoldersEx sentinel.
else if (0 == String.Compare(basePath, 0, AssemblyResolutionConstants.assemblyFoldersExSentinel, 0, AssemblyResolutionConstants.assemblyFoldersExSentinel.Length, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new AssemblyFoldersExResolver(searchPaths[p], getAssemblyName, fileExists, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, getRuntimeVersion, openBaseKey, targetedRuntimeVersion, targetProcessorArchitecture, true, buildEngine);
}
#endif
else if (0 == String.Compare(basePath, 0, AssemblyResolutionConstants.assemblyFoldersFromConfigSentinel, 0, AssemblyResolutionConstants.assemblyFoldersFromConfigSentinel.Length, StringComparison.OrdinalIgnoreCase))
{
resolvers[p] = new AssemblyFoldersFromConfigResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion, targetProcessorArchitecture, true, buildEngine, log);
}
else
{
resolvers[p] = new DirectoryResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion, null);
}
}
return resolvers;
}
/// <summary>
/// Build a resolver array from a set of directories to resolve directly from.
/// </summary>
internal static Resolver[] CompileDirectories(
List<DirectoryWithParentAssembly> parentReferenceDirectories,
FileExists fileExists,
GetAssemblyName getAssemblyName,
GetAssemblyRuntimeVersion getRuntimeVersion,
Version targetedRuntimeVersion)
{
var resolvers = new Resolver[parentReferenceDirectories.Count];
for (int i = 0; i < parentReferenceDirectories.Count; i++)
{
resolvers[i] = new DirectoryResolver(parentReferenceDirectories[i].Directory, getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion, parentReferenceDirectories[i].ParentAssembly);
}
return resolvers;
}
}
}
|