Skip to content

Commit

Permalink
Resolve dependencies from GAC (#950)
Browse files Browse the repository at this point in the history
* Resolve dependencies from GAC
* Use ISet

Co-authored-by: Sanan Yuzbashiyev <Sanan07@users.noreply.github.com>
  • Loading branch information
nohwnd and Sanan07 authored Aug 30, 2021
1 parent d79a604 commit 0f25394
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,24 @@ public string[] GetFullPathToDependentAssemblies(string assemblyPath, out IList<
Assembly assembly = null;
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Reflection loading {assemblyPath}.");

// First time we load in LoadFromContext to avoid issues.
assembly = this.assemblyUtility.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (Exception ex)
{
EqtTrace.Error($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Reflection loading of {assemblyPath} failed:");
EqtTrace.Error(ex);

warnings.Add(ex.Message);
return new string[0]; // Otherwise just return no dependencies.
}

Debug.Assert(assembly != null, "assembly");

List<string> result = new List<string>();
List<string> visitedAssemblies = new List<string>();
HashSet<string> visitedAssemblies = new HashSet<string>();

visitedAssemblies.Add(assembly.FullName);

Expand Down Expand Up @@ -149,9 +154,11 @@ private string GetTargetFrameworkStringFromAssembly(Assembly assembly)
/// <param name="result"> The result. </param>
/// <param name="visitedAssemblies"> The visited Assemblies. </param>
/// <param name="warnings"> The warnings. </param>
private void ProcessChildren(Assembly assembly, IList<string> result, IList<string> visitedAssemblies, IList<string> warnings)
private void ProcessChildren(Assembly assembly, IList<string> result, ISet<string> visitedAssemblies, IList<string> warnings)
{
Debug.Assert(assembly != null, "assembly");

EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Processing assembly {assembly.FullName}.");
foreach (AssemblyName reference in assembly.GetReferencedAssemblies())
{
this.GetDependentAssembliesInternal(reference.FullName, result, visitedAssemblies, warnings);
Expand All @@ -161,6 +168,7 @@ private void ProcessChildren(Assembly assembly, IList<string> result, IList<stri
var modules = new Module[0];
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Getting modules of {assembly.FullName}.");
modules = assembly.GetModules();
}
catch (FileNotFoundException e)
Expand Down Expand Up @@ -192,13 +200,12 @@ private void ProcessChildren(Assembly assembly, IList<string> result, IList<stri
continue;
}

if (visitedAssemblies.Contains(m.Name))
if (!visitedAssemblies.Add(m.Name))
{
// The assembly was already in the set, meaning that we already visited it.
continue;
}

visitedAssemblies.Add(m.Name);

if (!File.Exists(m.FullyQualifiedName))
{
string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependencyWithoutReason, m.FullyQualifiedName);
Expand All @@ -219,20 +226,21 @@ private void ProcessChildren(Assembly assembly, IList<string> result, IList<stri
/// <param name="visitedAssemblies"> The visited Assemblies. </param>
/// <param name="warnings"> The warnings. </param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
private void GetDependentAssembliesInternal(string assemblyString, IList<string> result, IList<string> visitedAssemblies, IList<string> warnings)
private void GetDependentAssembliesInternal(string assemblyString, IList<string> result, ISet<string> visitedAssemblies, IList<string> warnings)
{
Debug.Assert(!string.IsNullOrEmpty(assemblyString), "assemblyString");

if (visitedAssemblies.Contains(assemblyString))
if (!visitedAssemblies.Add(assemblyString))
{
// The assembly was already in the hashset, so we already visited it.
return;
}

visitedAssemblies.Add(assemblyString);

Assembly assembly = null;
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetDependentAssembliesInternal: Reflection loading {assemblyString}.");

string postPolicyAssembly = AppDomain.CurrentDomain.ApplyPolicy(assemblyString);
Debug.Assert(!string.IsNullOrEmpty(postPolicyAssembly), "postPolicyAssembly");

Expand All @@ -241,17 +249,15 @@ private void GetDependentAssembliesInternal(string assemblyString, IList<string>
}
catch (Exception ex)
{
EqtTrace.Error($"AssemblyLoadWorker.GetDependentAssembliesInternal: Reflection loading {assemblyString} failed:.");
EqtTrace.Error(ex);

string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependency, assemblyString, ex.Message);
warnings.Add(warning);
return;
}

// As soon as we find GAC or internal assembly we do not look further.
if (assembly.GlobalAssemblyCache)
{
return;
}

EqtTrace.Verbose($"AssemblyLoadWorker.GetDependentAssembliesInternal: Assembly {assemblyString} was added as dependency.");
result.Add(assembly.Location);

this.ProcessChildren(assembly, result, visitedAssemblies, warnings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ internal virtual string[] GetFullPathToDependentAssemblies(string assemblyPath,
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: start.");

AppDomainSetup setupInfo = new AppDomainSetup();
setupInfo.ApplicationBase = Path.GetDirectoryName(Path.GetFullPath(assemblyPath));
var dllDirectory = Path.GetDirectoryName(Path.GetFullPath(assemblyPath));
setupInfo.ApplicationBase = dllDirectory;

Debug.Assert(string.IsNullOrEmpty(configFile) || File.Exists(configFile), "Config file is specified but does not exist: {0}", configFile);

Expand Down Expand Up @@ -227,7 +228,18 @@ internal virtual string[] GetFullPathToDependentAssemblies(string assemblyPath,

EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: loaded the worker.");

return worker.GetFullPathToDependentAssemblies(assemblyPath, out warnings);
var allDependencies = worker.GetFullPathToDependentAssemblies(assemblyPath, out warnings);
var dependenciesFromDllDirectory = new List<string>();
var dllDirectoryUppercase = dllDirectory.ToUpperInvariant();
foreach (var dependency in allDependencies)
{
if (dependency.ToUpperInvariant().Contains(dllDirectoryUppercase))
{
dependenciesFromDllDirectory.Add(dependency);
}
}

return dependenciesFromDllDirectory.ToArray();
}
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ private void AddDependencies(string testSource, string configFile, IList<Deploym
Debug.Assert(deploymentItems != null, "deploymentItems should not be null.");
Debug.Assert(Path.IsPathRooted(testSource), "path should be rooted.");

var sw = Stopwatch.StartNew();

// Note: if this is not an assembly we simply return empty array, also:
// we do recursive search and report missing.
string[] references = this.AssemblyUtility.GetFullPathToDependentAssemblies(testSource, configFile, out var warningList);
Expand All @@ -230,6 +232,7 @@ private void AddDependencies(string testSource, string configFile, IList<Deploym
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("DeploymentManager: Source:{0} has following references", testSource);
EqtTrace.Info("DeploymentManager: Resolving dependencies took {0} ms", sw.ElapsedMilliseconds);
}

foreach (string reference in references)
Expand Down

0 comments on commit 0f25394

Please sign in to comment.