-
-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathPluginDependencyResolver.cs
More file actions
208 lines (187 loc) · 7.33 KB
/
PluginDependencyResolver.cs
File metadata and controls
208 lines (187 loc) · 7.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
using System.Reflection;
using System.Runtime.Loader;
using Beutl.Logging;
using NuGet.Common;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Packaging.Core;
namespace Beutl.Api.Services;
// https://github.com/dotnet/runtime/blob/9ec7fc21862f3446c6c6f7dcfff275942e3884d3/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs
internal sealed class PluginDependencyResolver
{
private readonly ILogger _logger = new LoggerAdapter(Log.CreateLogger<PluginDependencyResolver>());
private const string NeutralCultureName = "neutral";
private const string ResourceAssemblyExtension = ".dll";
private readonly Dictionary<string, string> _assemblyPaths = new(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _nativeSearchPaths = [];
private readonly HashSet<string> _resourceSearchPaths = [];
private readonly string[] _assemblyDirectorySearchPaths;
public PluginDependencyResolver(string mainDirectory, PackageFolderReader? reader)
{
NuGetFramework framework = Helper.GetFrameworkName();
if (reader != null)
{
var availablePackages = new HashSet<PackageIdentity>();
GetPackageDependencies(
mainDirectory,
reader,
reader.GetIdentity(),
framework,
_logger,
availablePackages);
}
else
{
foreach (string item in Directory.GetFiles(mainDirectory, "*.*", SearchOption.AllDirectories))
{
string relative = Path.GetRelativePath(mainDirectory, item);
if (relative.StartsWith("runtimes")
&& Path.GetDirectoryName(item) is { } fullname)
{
_nativeSearchPaths.Add(fullname);
}
else if (item.EndsWith(".resources.dll"))
{
string? parent = Path.GetDirectoryName(item);
string? culture = Path.GetFileName(parent);
if (parent is { }
&& !_resourceSearchPaths.Contains(parent)
&& culture is { }
&& CultureNameValidation.IsValid(culture))
{
_resourceSearchPaths.Add(parent);
}
}
else if (item.EndsWith(".dll"))
{
_assemblyPaths.TryAdd(Path.GetFileNameWithoutExtension(item), item);
}
}
}
_assemblyDirectorySearchPaths = [mainDirectory];
}
private void GetPackageDependencies(
string path,
PackageFolderReader reader,
PackageIdentity package,
NuGetFramework framework,
ILogger logger,
ISet<PackageIdentity> availablePackages)
{
if (availablePackages.Contains(package)) return;
IEnumerable<PackageDependencyGroup> deps = reader.GetPackageDependencies();
NuGetFramework? nearest = Helper.FrameworkReducer.GetNearest(
framework,
deps.Select(x => x.TargetFramework));
string[] libItems = reader.GetLibItems()
.Where(x => x.TargetFramework == nearest)
.SelectMany(x => x.Items)
.ToArray();
foreach (string item in libItems)
{
if (item.EndsWith(".resources.dll"))
{
string? parent = Path.GetDirectoryName(Path.Combine(path, item));
string? culture = Path.GetFileName(parent);
if (parent is { }
&& !_resourceSearchPaths.Contains(parent)
&& culture is { }
&& CultureNameValidation.IsValid(culture))
{
_resourceSearchPaths.Add(parent);
continue;
}
}
if (item.EndsWith(".dll"))
{
_assemblyPaths.TryAdd(
Path.GetFileNameWithoutExtension(item),
Path.Combine(path, item));
}
}
foreach (string? item in reader.GetItems("runtimes")
.SelectMany(x => x.Items))
{
string add = Path.Combine(path, Path.GetDirectoryName(item)!);
if (Directory.Exists(add))
{
_nativeSearchPaths.Add(add);
}
}
availablePackages.Add(package);
foreach (PackageDependency? dependency in deps.Where(x => x.TargetFramework == nearest)
.SelectMany(x => x.Packages))
{
var dependentPackage = new PackageIdentity(dependency.Id, dependency.VersionRange.MinVersion);
path = Helper.PackagePathResolver.GetInstalledPath(dependentPackage);
if (path != null)
{
reader = new PackageFolderReader(path);
GetPackageDependencies(
path,
reader,
dependentPackage,
framework, logger, availablePackages);
}
}
}
public string? ResolveAssemblyToPath(AssemblyName assemblyName)
{
if (!string.IsNullOrEmpty(assemblyName.CultureName)
&& !string.Equals(assemblyName.CultureName, NeutralCultureName, StringComparison.OrdinalIgnoreCase))
{
foreach (string searchPath in _resourceSearchPaths)
{
string assemblyPath = Path.Combine(
searchPath,
assemblyName.CultureName,
$"{assemblyName.Name}{ResourceAssemblyExtension}");
if (File.Exists(assemblyPath))
{
return assemblyPath;
}
}
}
else if (assemblyName.Name != null)
{
if (_assemblyPaths.TryGetValue(assemblyName.Name, out string? assemblyPath))
{
if (File.Exists(assemblyPath))
{
return assemblyPath;
}
}
}
return null;
}
public string? ResolveUnmanagedDllToPath(string unmanagedDllName)
{
ArgumentNullException.ThrowIfNull(unmanagedDllName);
IEnumerable<string> searchPaths;
if (unmanagedDllName.Contains(Path.DirectorySeparatorChar))
{
// Library names with absolute or relative path can't be resolved
// using the component .deps.json as that defines simple names.
// So instead use the component directory as the lookup path.
searchPaths = _assemblyDirectorySearchPaths;
}
else
{
searchPaths = _nativeSearchPaths;
}
bool isRelativePath = !Path.IsPathFullyQualified(unmanagedDllName);
foreach (LibraryNameVariation libraryNameVariation in LibraryNameVariation.DetermineLibraryNameVariations(unmanagedDllName, isRelativePath))
{
string libraryName = libraryNameVariation.Prefix + unmanagedDllName + libraryNameVariation.Suffix;
foreach (string searchPath in searchPaths)
{
string libraryPath = Path.Combine(searchPath, libraryName);
if (File.Exists(libraryPath))
{
return libraryPath;
}
}
}
return null;
}
}