204 lines
8.7 KiB
C#
204 lines
8.7 KiB
C#
#pragma warning disable SA1512,SA1124 // Single-line comments should not be followed by blank line
|
|
|
|
using System;
|
|
using System.IO;
|
|
using JetBrains.Annotations;
|
|
using NugetForUnity.Configuration;
|
|
using NugetForUnity.Models;
|
|
using NugetForUnity.PackageSource;
|
|
|
|
#region No ReShaper
|
|
|
|
// ReSharper disable All
|
|
// needed because 'JetBrains.Annotations.NotNull' and 'System.Diagnostics.CodeAnalysis.NotNull' collide if this file is compiled with a never version of Unity / C#
|
|
using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
|
|
|
|
// ReSharper restore All
|
|
|
|
#endregion
|
|
|
|
namespace NugetForUnity
|
|
{
|
|
/// <summary>
|
|
/// Manages the NuGet package cache, which is where .nupkg files are stored after they are downloaded from the server, but before they are installed.
|
|
/// </summary>
|
|
internal static class PackageCacheManager
|
|
{
|
|
private const string CacheEnvironmentVariableName = "NuGetCachePath";
|
|
|
|
/// <summary>
|
|
/// The path where to put created (packed) and downloaded (not installed yet) .nupkg files.
|
|
/// </summary>
|
|
[SuppressMessage(
|
|
"StyleCop.CSharp.OrderingRules",
|
|
"SA1202:Elements should be ordered by access",
|
|
Justification = "Conflicts with other warnings.")]
|
|
internal static readonly string CacheOutputDirectory = DetermineCacheOutputDirectory();
|
|
|
|
/// <summary>
|
|
/// Initializes static members of the <see cref="PackageCacheManager" /> class.
|
|
/// Static constructor called only once.
|
|
/// </summary>
|
|
[SuppressMessage("SonarQube", "S3877:Exceptions should not be thrown from unexpected methods", Justification = "Need to fail here.")]
|
|
static PackageCacheManager()
|
|
{
|
|
try
|
|
{
|
|
// create the cache directory, if it doesn't exist
|
|
Directory.CreateDirectory(CacheOutputDirectory);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Failed to create the cache output directory '{CacheOutputDirectory}'. The cache directory can be changed by specifying the '{CacheEnvironmentVariableName}' environment variable.",
|
|
exception);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a NugetPackage from the NuGet already installed list, the cache or the currently active package source.
|
|
/// </summary>
|
|
/// <param name="packageId">The <see cref="INugetPackageIdentifier" /> containing the ID and Version of the package to get.</param>
|
|
/// <returns>The retrieved package, if there is one. Null if no matching package was found.</returns>
|
|
[CanBeNull]
|
|
internal static INugetPackage GetPackageFromCacheOrSource([NotNull] INugetPackageIdentifier packageId)
|
|
{
|
|
// First look to see if the package is already installed
|
|
var package = GetInstalledPackage(packageId);
|
|
|
|
if (package != null)
|
|
{
|
|
return package;
|
|
}
|
|
|
|
// That package isn't installed yet, so look in the cache next
|
|
package = GetCachedPackage(packageId);
|
|
|
|
if (package != null)
|
|
{
|
|
return package;
|
|
}
|
|
|
|
// It's not in the cache, so we need to look in the active sources
|
|
return GetOnlinePackage(packageId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to find an already cached package that matches the given package ID.
|
|
/// </summary>
|
|
/// <param name="packageId">The <see cref="INugetPackageIdentifier" /> of the <see cref="NugetPackageLocal" /> to find.</param>
|
|
/// <returns>The best <see cref="NugetPackageLocal" /> match, if there is one, otherwise null.</returns>
|
|
[CanBeNull]
|
|
private static NugetPackageLocal GetCachedPackage([NotNull] INugetPackageIdentifier packageId)
|
|
{
|
|
if (!ConfigurationManager.NugetConfigFile.InstallFromCache || packageId.HasVersionRange)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var cachedPackagePath = Path.Combine(CacheOutputDirectory, packageId.PackageFileName);
|
|
|
|
if (!File.Exists(cachedPackagePath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
NugetLogger.LogVerbose("Found exact package in the cache: {0}", cachedPackagePath);
|
|
return NugetPackageLocal.FromNupkgFile(
|
|
cachedPackagePath,
|
|
new NugetPackageSourceLocal(
|
|
"Nupkg file from cache",
|
|
Path.GetDirectoryName(cachedPackagePath) ??
|
|
throw new InvalidOperationException($"Failed to get directory from '{cachedPackagePath}'")));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to find an "online" (in the package sources - which could be local) package that matches (or is in the range of) the given package ID.
|
|
/// </summary>
|
|
/// <param name="packageId">The <see cref="INugetPackageIdentifier" /> of the <see cref="INugetPackage" /> to find.</param>
|
|
/// <returns>The best <see cref="INugetPackage" /> match, if there is one, otherwise null.</returns>
|
|
[CanBeNull]
|
|
private static INugetPackage GetOnlinePackage([NotNull] INugetPackageIdentifier packageId)
|
|
{
|
|
var package = ConfigurationManager.GetSpecificPackage(packageId);
|
|
|
|
if (package != null)
|
|
{
|
|
NugetLogger.LogVerbose("{0} {1} not found, using {2}", packageId.Id, packageId.Version, package.Version);
|
|
}
|
|
else
|
|
{
|
|
NugetLogger.LogVerbose("Failed to find {0} {1}", packageId.Id, packageId.Version);
|
|
}
|
|
|
|
return package;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to find an already installed package that matches (or is in the range of) the given package ID.
|
|
/// </summary>
|
|
/// <param name="packageId">The <see cref="INugetPackageIdentifier" /> of the <see cref="INugetPackage" /> to find.</param>
|
|
/// <returns>The best <see cref="INugetPackage" /> match, if there is one, otherwise null.</returns>
|
|
[CanBeNull]
|
|
private static INugetPackage GetInstalledPackage([NotNull] INugetPackageIdentifier packageId)
|
|
{
|
|
if (!InstalledPackagesManager.TryGetById(packageId.Id, out var installedPackage))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (packageId.PackageVersion == installedPackage.PackageVersion)
|
|
{
|
|
NugetLogger.LogVerbose("Found exact package already installed: {0} {1}", installedPackage.Id, installedPackage.Version);
|
|
return installedPackage;
|
|
}
|
|
|
|
if (!packageId.InRange(installedPackage))
|
|
{
|
|
NugetLogger.LogVerbose(
|
|
"Requested {0} {1}. {2} is already installed, but it is out of range.",
|
|
packageId.Id,
|
|
packageId.Version,
|
|
installedPackage.Version);
|
|
return null;
|
|
}
|
|
|
|
var configPackage = InstalledPackagesManager.GetPackageConfigurationById(packageId.Id);
|
|
if (configPackage != null && configPackage.PackageVersion < installedPackage.PackageVersion)
|
|
{
|
|
NugetLogger.LogVerbose(
|
|
"Requested {0} {1}. {2} is already installed, but config demands lower version.",
|
|
packageId.Id,
|
|
packageId.Version,
|
|
installedPackage.Version);
|
|
return null;
|
|
}
|
|
|
|
NugetLogger.LogVerbose(
|
|
"Requested {0} {1}, but {2} is already installed, so using that.",
|
|
packageId.Id,
|
|
packageId.Version,
|
|
installedPackage.Version);
|
|
return installedPackage;
|
|
}
|
|
|
|
private static string DetermineCacheOutputDirectory()
|
|
{
|
|
var cacheFromEnvironment = Environment.GetEnvironmentVariable(CacheEnvironmentVariableName);
|
|
if (!string.IsNullOrEmpty(cacheFromEnvironment))
|
|
{
|
|
return Path.GetFullPath(cacheFromEnvironment);
|
|
}
|
|
|
|
var localApplicationDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
|
if (string.IsNullOrEmpty(localApplicationDataFolder))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"There is no system folder specified for '{Environment.SpecialFolder.LocalApplicationData}' you need to either configure it or set the NuGet cache directory by specifying the '{CacheEnvironmentVariableName}' environment variable.");
|
|
}
|
|
|
|
return Path.Combine(Path.GetFullPath(localApplicationDataFolder), "NuGet", "Cache");
|
|
}
|
|
}
|
|
}
|