Files
tixl3d--tixl/Core/Compilation/TixlAssemblyLoadContext.NuGet.cs
2026-07-13 13:15:05 +08:00

138 lines
6.3 KiB
C#

#nullable enable
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading;
using NuGet.Configuration;
using NuGet.Frameworks;
using T3.Core.Logging;
using Microsoft.Extensions.DependencyModel;
using T3.Core.IO;
namespace T3.Core.Compilation;
internal sealed partial class TixlAssemblyLoadContext
{
private static readonly Lock _nugetLock = new();
private static readonly AssemblyLoadContext _nugetContext = new("NuGet", true);
private static readonly List<AssemblyTreeNode> _loadedNuGetAssemblies = [];
// todo - per-project directory, since this walks up the directory provided to find nuget config files
private static readonly string _nugetDirectory = SettingsUtility.GetGlobalPackagesFolder(NuGet.Configuration.Settings.LoadDefaultSettings(null, null, null));
private readonly record struct NugetFrameworkDirectory(DirectoryInfo Directory, NuGetFramework Framework) : IFrameworkSpecific
{
NuGetFramework IFrameworkSpecific.TargetFramework => Framework;
}
private Assembly? SearchNugetForAssemblies(AssemblyName asmName, string name)
{
lock (_nugetLock)
{
foreach (var node in _loadedNuGetAssemblies)
{
if (node.TryFindExisting(name, out var asmNode))
{
// add the dependency to our context
AddDependency(asmNode);
return asmNode.Assembly;
}
}
foreach (var node in _loadedNuGetAssemblies)
{
if (node.TryFindUnreferenced(name, out var asmNode))
{
// add the dependency to our context
AddDependency(asmNode);
return asmNode.Assembly;
}
}
if (_dependencyContext != null)
{
// check if the assembly is in the dependency context
var runtimeAssemblies = _dependencyContext.RuntimeLibraries;
var assemblyVersion = asmName.Version;
var assemblyVersionStr = "";
var assemblyVersionRevisionStr = "";
if (assemblyVersion != null)
{
assemblyVersionStr = $"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}.{assemblyVersion.Revision}";
assemblyVersionRevisionStr =
$"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}.{assemblyVersion.MajorRevision}/{assemblyVersion.MinorRevision}";
}
foreach (var library in runtimeAssemblies)
{
if (library.Path == null)
continue;
var assemblyNames = library.GetDefaultAssemblyNames(_dependencyContext);
foreach (var libName in assemblyNames)
{
if (libName == asmName || (libName.Version == null && asmName.Name == libName.Name))
{
// this is the one we are looking for
// var assemblyPath = Path.Combine(_dependencyContext.RuntimeGraph.First().Runtime, library.Path, libName.Name + ".dll");
var assemblyRelativePath = library.Path;
var assemblyBasePath = Path.Combine(_nugetDirectory, assemblyRelativePath);
if (!Directory.Exists(assemblyBasePath))
{
Log.Debug("Failed to find nuget assembly directory: " + assemblyBasePath);
continue;
}
// throw runtime info to the end of the path to find our dll
var runtimes = _dependencyContext.RuntimeGraph;
foreach (var group in library.RuntimeAssemblyGroups)
{
if (group.Runtime is { Length: > 0 } && runtimes.All(x => x.Runtime != group.Runtime))
{
// we cannot support the specified runtime
continue;
}
foreach (var file in group.RuntimeFiles)
{
var fileVersion = file.AssemblyVersion;
if (fileVersion != null && assemblyVersion != null)
{
// we check the version of the assembly
if (fileVersion != assemblyVersionStr && fileVersion != assemblyVersionRevisionStr)
{
continue;
}
}
// todo - probably dont want or need a separate nuget context? but in the case of transitive dependencies this could be simplest..
var fullPath = Path.Combine(assemblyBasePath, file.Path);
if (!File.Exists(fullPath))
{
Log.Error($"NuGet assembly {asmName} not found at {fullPath}");
continue;
}
var assembly = LoadAssembly(fullPath, _nugetContext);
var newNode = new AssemblyTreeNode(assembly, _nugetContext, true, true, _dllImportResolver);
AddDependency(newNode);
_loadedNuGetAssemblies.Add(newNode);
return newNode.Assembly;
}
}
}
}
}
}
}
if(CoreSettings.Config.LogAssemblyLoadingDetails)
Log.Error($"{Name}: Could not find nuget assembly '{asmName}'.");
return null;
}
private readonly DependencyContext? _dependencyContext;
}