Skip to content

Commit

Permalink
Annotate RAR SearchPaths added because they're "next to a reference" (#…
Browse files Browse the repository at this point in the history
…9700)

* annotate that searchpath was added by a parent assembly
  • Loading branch information
surayya-MS committed Feb 7, 2024
1 parent 5768954 commit bc11c1b
Show file tree
Hide file tree
Showing 21 changed files with 165 additions and 17 deletions.
27 changes: 25 additions & 2 deletions src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3237,7 +3237,7 @@ public void ResolveToGACSpecificVersion()
[Fact]
public void ParentAssemblyResolvedFromAForGac()
{
var parentReferenceFolders = new List<string>();
var parentReferenceFolders = new List<DirectoryWithParentAssembly>();
var referenceList = new List<Reference>();

var taskItem = new TaskItem("Microsoft.VisualStudio.Interopt, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
Expand Down Expand Up @@ -3266,7 +3266,7 @@ public void ParentAssemblyResolvedFromAForGac()
}

Assert.Single(parentReferenceFolders);
Assert.Equal(reference2.ResolvedSearchPath, parentReferenceFolders[0]);
Assert.Equal(reference2.ResolvedSearchPath, parentReferenceFolders[0].Directory);
}

/// <summary>
Expand Down Expand Up @@ -8623,6 +8623,29 @@ public void SDKReferencesAreResolvedWithoutIO()
rar._cache.IsDirty.ShouldBeFalse();
}

[Fact]
public void LogsParentAssemblyForEveryConsideredAndRejectedSearchPath()
{
InitializeRARwithMockEngine(_output, out MockEngine mockEngine, out ResolveAssemblyReference rar);

rar.Assemblies = new ITaskItem[]
{
new TaskItem(@"C:\DirectoryTest\A.dll"),
new TaskItem("B"),
};

rar.SearchPaths = new string[]
{
"{RawFileName}",
};

Execute(rar).ShouldBeTrue();

mockEngine.AssertLogContains(rar.Log.FormatResourceString("ResolveAssemblyReference.SearchPathAddedByParentAssembly",
@"C:\DirectoryTest",
@"C:\DirectoryTest\A.dll"));
}

[Fact]
public void ManagedRuntimeVersionReaderSupportsWindowsRuntime()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,9 @@ internal void StopIOMonitoring()
s_netstandardDllPath,
@"C:\SystemRuntime\Regular.dll",
s_dependsOnNuGet_ADllPath,
s_nugetCache_N_Lib_NDllPath
s_nugetCache_N_Lib_NDllPath,
@"C:\DirectoryTest\A.dll",
@"C:\DirectoryTest\B.dll",
};

/// <summary>
Expand Down Expand Up @@ -2482,6 +2484,19 @@ internal static AssemblyNameExtension[] GetDependencies(string path)
};
}

if (String.Equals(path, @"C:\DirectoryTest\A.dll", StringComparison.OrdinalIgnoreCase))
{
return new AssemblyNameExtension[]
{
GetAssemblyName(@"C:\DirectoryTest\B.dll")
};
}

if (String.Equals(path, @"C:\DirectoryTest\B.dll", StringComparison.OrdinalIgnoreCase))
{
return Array.Empty<AssemblyNameExtension>();
}

// Use a default list.
return new AssemblyNameExtension[]
{
Expand Down
12 changes: 7 additions & 5 deletions src/Tasks/AssemblyDependency/AssemblyResolution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

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:
Expand Down Expand Up @@ -203,7 +205,7 @@ internal static class AssemblyResolution
}
else
{
resolvers[p] = new DirectoryResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
resolvers[p] = new DirectoryResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion, null);
}
}
return resolvers;
Expand All @@ -213,16 +215,16 @@ internal static class AssemblyResolution
/// Build a resolver array from a set of directories to resolve directly from.
/// </summary>
internal static Resolver[] CompileDirectories(
List<string> directories,
List<DirectoryWithParentAssembly> parentReferenceDirectories,
FileExists fileExists,
GetAssemblyName getAssemblyName,
GetAssemblyRuntimeVersion getRuntimeVersion,
Version targetedRuntimeVersion)
{
var resolvers = new Resolver[directories.Count];
for (int i = 0; i < directories.Count; i++)
var resolvers = new Resolver[parentReferenceDirectories.Count];
for (int i = 0; i < parentReferenceDirectories.Count; i++)
{
resolvers[i] = new DirectoryResolver(directories[i], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
resolvers[i] = new DirectoryResolver(parentReferenceDirectories[i].Directory, getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion, parentReferenceDirectories[i].ParentAssembly);
}

return resolvers;
Expand Down
31 changes: 28 additions & 3 deletions src/Tasks/AssemblyDependency/DirectoryResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ namespace Microsoft.Build.Tasks
/// </summary>
internal class DirectoryResolver : Resolver
{
/// <summary>
/// The parent assembly that was used for the SearchPath.
/// </summary>
public readonly string parentAssembly;

/// <summary>
/// Construct.
/// </summary>
public DirectoryResolver(string searchPathElement, GetAssemblyName getAssemblyName, FileExists fileExists, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVesion)
public DirectoryResolver(string searchPathElement, GetAssemblyName getAssemblyName, FileExists fileExists, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVesion, string parentAssembly)
: base(searchPathElement, getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVesion, System.Reflection.ProcessorArchitecture.None, false)
{
this.parentAssembly = parentAssembly;
}

/// <inheritdoc/>
Expand All @@ -40,8 +46,27 @@ public DirectoryResolver(string searchPathElement, GetAssemblyName getAssemblyNa
foundPath = null;
userRequestedSpecificFile = false;

// Resolve to the given path.
string resolvedPath = ResolveFromDirectory(assemblyName, isPrimaryProjectReference, wantSpecificVersion, executableExtensions, searchPathElement, assembliesConsideredAndRejected);
string resolvedPath;

if (parentAssembly != null)
{
var searchLocationsWithParentAssembly = new List<ResolutionSearchLocation>();

// Resolve to the given path.
resolvedPath = ResolveFromDirectory(assemblyName, isPrimaryProjectReference, wantSpecificVersion, executableExtensions, searchPathElement, searchLocationsWithParentAssembly);

foreach (var searchLocation in searchLocationsWithParentAssembly)
{
searchLocation.ParentAssembly = parentAssembly;
}

assembliesConsideredAndRejected.AddRange(searchLocationsWithParentAssembly);
}
else
{
resolvedPath = ResolveFromDirectory(assemblyName, isPrimaryProjectReference, wantSpecificVersion, executableExtensions, searchPathElement, assembliesConsideredAndRejected);
}

if (resolvedPath != null)
{
foundPath = resolvedPath;
Expand Down
8 changes: 4 additions & 4 deletions src/Tasks/AssemblyDependency/ReferenceTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,7 @@ private static bool IsPseudoAssembly(string name)
/// The only time we do not want to do this is if the parent assembly came from the GAC or AssemblyFoldersEx then we want the assembly
/// to be found using those resolvers so that our GAC and AssemblyFolders checks later on will work on those assemblies.
/// </summary>
internal static void CalculateParentAssemblyDirectories(List<string> parentReferenceFolders, Reference parentReference)
internal static void CalculateParentAssemblyDirectories(List<DirectoryWithParentAssembly> parentReferenceFolders, Reference parentReference)
{
string parentReferenceFolder = parentReference.DirectoryName;
string parentReferenceResolvedSearchPath = parentReference.ResolvedSearchPath;
Expand All @@ -1240,7 +1240,7 @@ internal static void CalculateParentAssemblyDirectories(List<string> parentRefer
if (!parentReferencesAdded.Contains(parentReferenceFolder) && !parentReferenceResolvedFromGAC && !parentReferenceResolvedFromAssemblyFolders)
{
parentReferencesAdded.Add(parentReferenceFolder);
parentReferenceFolders.Add(parentReferenceFolder);
parentReferenceFolders.Add(new (Directory: parentReferenceFolder, ParentAssembly: parentReference.FullPath));
}
}

Expand Down Expand Up @@ -1279,7 +1279,7 @@ internal static void CalculateParentAssemblyDirectories(List<string> parentRefer
// First, look for the dependency in the parents' directories. Unless they are resolved from the GAC or assemblyFoldersEx then
// we should make sure we use the GAC and assemblyFolders resolvers themserves rather than a directory resolver to find the reference.
// This way we dont get assemblies pulled from the GAC or AssemblyFolders but dont have the marking that they were pulled form there.
var parentReferenceFolders = new List<string>();
var parentReferenceFolders = new List<DirectoryWithParentAssembly>();
foreach (Reference parentReference in reference.GetDependees())
{
CalculateParentAssemblyDirectories(parentReferenceFolders, parentReference);
Expand All @@ -1298,7 +1298,7 @@ internal static void CalculateParentAssemblyDirectories(List<string> parentRefer
else
{
// Do not probe near dependees if the reference is primary and resolved externally. If resolved externally, the search paths should have been specified in such a way to point to the assembly file.
if (assemblyName == null || !_externallyResolvedPrimaryReferences.Contains(assemblyName.Name))
if (parentReferenceFolders.Count > 0 && (assemblyName == null || !_externallyResolvedPrimaryReferences.Contains(assemblyName.Name)))
{
jaggedResolvers.Add(AssemblyResolution.CompileDirectories(parentReferenceFolders, _fileExists, _getAssemblyName, _getRuntimeVersion, _targetedRuntimeVersion));
}
Expand Down
5 changes: 5 additions & 0 deletions src/Tasks/AssemblyDependency/ResolutionSearchLocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ internal class ResolutionSearchLocation
/// </summary>
internal string SearchPath { get; set; }

/// <summary>
/// The parent assembly that was used for the SearchPath.
/// </summary>
internal string ParentAssembly { get; set; }

/// <summary>
/// The name of the assembly found at that location. Will be null if there was no assembly there.
/// </summary>
Expand Down
13 changes: 11 additions & 2 deletions src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private static class Strings
public static string ResolvedFrom;
public static string SearchedAssemblyFoldersEx;
public static string SearchPath;
public static string SearchPathAddedByParentAssembly;
public static string TargetedProcessorArchitectureDoesNotMatch;
public static string UnificationByAppConfig;
public static string UnificationByAutoUnify;
Expand Down Expand Up @@ -152,7 +153,8 @@ internal static void Initialize(TaskLoggingHelper log)
Resolved = GetResourceFourSpaces("ResolveAssemblyReference.Resolved");
ResolvedFrom = GetResourceFourSpaces("ResolveAssemblyReference.ResolvedFrom");
SearchedAssemblyFoldersEx = GetResourceEightSpaces("ResolveAssemblyReference.SearchedAssemblyFoldersEx");
SearchPath = EightSpaces + GetResource("ResolveAssemblyReference.SearchPath");
SearchPath = GetResourceEightSpaces("ResolveAssemblyReference.SearchPath");
SearchPathAddedByParentAssembly = GetResourceEightSpaces("ResolveAssemblyReference.SearchPathAddedByParentAssembly");
TargetedProcessorArchitectureDoesNotMatch = GetResourceEightSpaces("ResolveAssemblyReference.TargetedProcessorArchitectureDoesNotMatch");
UnificationByAppConfig = GetResourceFourSpaces("ResolveAssemblyReference.UnificationByAppConfig");
UnificationByAutoUnify = GetResourceFourSpaces("ResolveAssemblyReference.UnificationByAutoUnify");
Expand Down Expand Up @@ -1791,7 +1793,14 @@ private void LogAssembliesConsideredAndRejected(Reference reference, string fusi
if (lastSearchPath != location.SearchPath)
{
lastSearchPath = location.SearchPath;
Log.LogMessage(importance, Strings.SearchPath, lastSearchPath);
if (location.ParentAssembly != null)
{
Log.LogMessage(importance, Strings.SearchPathAddedByParentAssembly, lastSearchPath, location.ParentAssembly);
}
else
{
Log.LogMessage(importance, Strings.SearchPath, lastSearchPath);
}
if (logAssemblyFoldersMinimal)
{
Log.LogMessage(importance, Strings.SearchedAssemblyFoldersEx);
Expand Down
4 changes: 4 additions & 0 deletions src/Tasks/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1713,6 +1713,10 @@
<data name="ResolveAssemblyReference.SearchPath">
<value>For SearchPath "{0}".</value>
</data>
<data name="ResolveAssemblyReference.SearchPathAddedByParentAssembly">
<value>For SearchPath "{0}" (added by referencing assembly "{1}").</value>
<comment> {1} is the name of the parent assembly for which SearchPath was used.</comment>
</data>
<data name="ResolveAssemblyReference.UnificationByAppConfig">
<value>Using this version instead of original version "{0}" in "{2}" because of a binding redirect entry in the file "{1}".</value>
</data>
Expand Down
5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Tasks/Resources/xlf/Strings.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit bc11c1b

Please sign in to comment.