diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs
index 14308261..5329ade0 100644
--- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs
+++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs
@@ -568,4 +568,70 @@ public void TestSerializeNugetPackageIdentifier(string version)
Assert.That(deserialized.InRange(identifier), Is.True);
}
}
+
+ [Test]
+ [TestCase("jQuery", "3.7.0")]
+ public void TestPostprocessInstall(string packageId, string packageVersion)
+ {
+ var package = new NugetPackageIdentifier(packageId, packageVersion) { IsManuallyInstalled = true };
+ var filepath = NugetHelper.PackagesConfigFilePath;
+
+ var packagesConfigFile = new PackagesConfigFile();
+ packagesConfigFile.AddPackage(package);
+ packagesConfigFile.Save(filepath);
+
+ Assert.IsFalse(NugetHelper.IsInstalled(package), "The package IS installed: {0} {1}", package.Id, package.Version);
+
+ var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal);
+ filepath = filepath.Substring(assetsIndex);
+ NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[] { filepath }, null, null, null);
+
+ Assert.IsTrue(NugetHelper.IsInstalled(package), "The package was NOT installed: {0} {1}", package.Id, package.Version);
+ }
+
+ [Test]
+ [TestCase("jQuery", "3.7.0")]
+ public void TestPostprocessUninstall(string packageId, string packageVersion)
+ {
+ var package = new NugetPackageIdentifier(packageId, packageVersion) { IsManuallyInstalled = true };
+ var filepath = NugetHelper.PackagesConfigFilePath;
+
+ NugetHelper.InstallIdentifier(package);
+ Assert.IsTrue(NugetHelper.IsInstalled(package), "The package was NOT installed: {0} {1}", package.Id, package.Version);
+
+ var packagesConfigFile = new PackagesConfigFile();
+ packagesConfigFile.Save(filepath);
+
+ var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal);
+ filepath = filepath.Substring(assetsIndex);
+ NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[]{filepath},
+ null, null, null);
+
+ Assert.IsFalse(NugetHelper.IsInstalled(package), "The package is STILL installed: {0} {1}", package.Id, package.Version);
+ }
+
+ [Test]
+ [TestCase("jQuery", "3.6.4", "3.7.0")]
+ [TestCase("jQuery", "3.7.0", "3.6.4")]
+ public void TestPostprocessDifferentVersion(string packageId, string packageVersionOld, string packageVersionNew)
+ {
+ var packageOld = new NugetPackageIdentifier(packageId, packageVersionOld) { IsManuallyInstalled = true };
+ var packageNew = new NugetPackageIdentifier(packageId, packageVersionNew) { IsManuallyInstalled = true };
+ var filepath = NugetHelper.PackagesConfigFilePath;
+
+ NugetHelper.InstallIdentifier(packageOld);
+ Assert.IsTrue(NugetHelper.IsInstalled(packageOld), "The package was NOT installed: {0} {1}", packageOld.Id, packageOld.Version);
+
+ var packagesConfigFile = new PackagesConfigFile();
+ packagesConfigFile.AddPackage(packageNew);
+ packagesConfigFile.Save(filepath);
+
+ var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal);
+ filepath = filepath.Substring(assetsIndex);
+ NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[] { filepath }, null, null, null);
+
+ Assert.IsFalse(NugetHelper.IsInstalled(packageOld), "The old package version IS STILL installed: {0} {1}", packageOld.Id, packageOld.Version);
+
+ Assert.IsTrue(NugetHelper.IsInstalled(packageNew), "The new package version was NOT installed: {0} {1}", packageNew.Id, packageNew.Version);
+ }
}
diff --git a/src/NuGetForUnity/Editor/NugetHelper.cs b/src/NuGetForUnity/Editor/NugetHelper.cs
index 6601f876..32c32a49 100644
--- a/src/NuGetForUnity/Editor/NugetHelper.cs
+++ b/src/NuGetForUnity/Editor/NugetHelper.cs
@@ -43,7 +43,7 @@ public static class NugetHelper
///
///
///
- private static readonly string PackagesConfigFilePath = Path.GetFullPath(Path.Combine(Application.dataPath, PackagesConfigFile.FileName));
+ internal static readonly string PackagesConfigFilePath = Path.GetFullPath(Path.Combine(Application.dataPath, PackagesConfigFile.FileName));
///
/// Gets the absolute path to the Unity-Project root directory.
@@ -131,6 +131,14 @@ public static PackagesConfigFile PackagesConfigFile
}
}
+ ///
+ /// Invalidates the currently loaded 'packages.config' so it is reloaded when it is accessed the next time.
+ ///
+ internal static void ReloadPackagesConfig()
+ {
+ packagesConfigFile = null;
+ }
+
///
/// Gets the packages that are actually installed in the project.
///
@@ -369,45 +377,41 @@ private static void CleanInstallationDirectory(NugetPackageIdentifier package)
// go through the library folders in descending order (highest to lowest version)
var libDirectories = new DirectoryInfo(packageLibsDirectory).GetDirectories();
- var isAlreadyImported = IsAlreadyImportedInEngine(package);
- if (!isAlreadyImported)
+ var bestLibDirectory = TargetFrameworkResolver.TryGetBestTargetFramework(libDirectories, directory => directory.Name);
+ if (bestLibDirectory == null)
{
- var bestLibDirectory = TargetFrameworkResolver.TryGetBestTargetFramework(libDirectories, directory => directory.Name);
- if (bestLibDirectory == null)
- {
- Debug.LogWarningFormat("Couldn't find a library folder with a supported target-framework for the package {0}", package);
- }
- else
- {
- LogVerbose(
- "Selecting directory '{0}' with the best target framework {1} for current settings",
- bestLibDirectory,
- bestLibDirectory.Name);
- }
+ Debug.LogWarningFormat("Couldn't find a library folder with a supported target-framework for the package {0}", package);
+ }
+ else
+ {
+ LogVerbose(
+ "Selecting directory '{0}' with the best target framework {1} for current settings",
+ bestLibDirectory,
+ bestLibDirectory.Name);
+ }
- // delete all of the libraries except for the selected one
- foreach (var directory in libDirectories)
+ // delete all of the libraries except for the selected one
+ foreach (var directory in libDirectories)
+ {
+ // we use reference equality as the TargetFrameworkResolver returns the input reference.
+ if (directory != bestLibDirectory)
{
- // we use reference equality as the TargetFrameworkResolver returns the input reference.
- if (directory != bestLibDirectory)
- {
- DeleteDirectory(directory.FullName);
- }
+ DeleteDirectory(directory.FullName);
}
+ }
- if (bestLibDirectory != null)
+ if (bestLibDirectory != null)
+ {
+ // some older packages e.g. Microsoft.CodeAnalysis.Common 2.10.0 have multiple localization resource files
+ // e.g. Microsoft.CodeAnalysis.resources.dll each inside a folder with the language name as a folder name e.g. zh-Hant or fr
+ // unity doesn't support importing multiple assemblies with the same file name.
+ // for now we just delete all folders so the language neutral version is used and Unity is happy.
+ var languageSupFolders = bestLibDirectory.GetDirectories();
+ if (languageSupFolders.All(languageSupFolder => languageSupFolder.Name.Split('-').FirstOrDefault()?.Length == 2))
{
- // some older packages e.g. Microsoft.CodeAnalysis.Common 2.10.0 have multiple localization resource files
- // e.g. Microsoft.CodeAnalysis.resources.dll each inside a folder with the language name as a folder name e.g. zh-Hant or fr
- // unity doesn't support importing multiple assemblies with the same file name.
- // for now we just delete all folders so the language neutral version is used and Unity is happy.
- var languageSupFolders = bestLibDirectory.GetDirectories();
- if (languageSupFolders.All(languageSupFolder => languageSupFolder.Name.Split('-').FirstOrDefault()?.Length == 2))
+ foreach (var languageSupFolder in languageSupFolders)
{
- foreach (var languageSupFolder in languageSupFolders)
- {
- languageSupFolder.Delete(true);
- }
+ languageSupFolder.Delete(true);
}
}
}
@@ -740,13 +744,11 @@ public static void Uninstall(NugetPackageIdentifier package, bool refreshAssets
var foundPackage = package as NugetPackage ?? GetSpecificPackage(package);
// update the package.config file
- if (!PackagesConfigFile.RemovePackage(foundPackage))
+ if (PackagesConfigFile.RemovePackage(foundPackage))
{
- return;
+ PackagesConfigFile.Save(PackagesConfigFilePath);
}
- PackagesConfigFile.Save(PackagesConfigFilePath);
-
var packageInstallDirectory = Path.Combine(NugetConfigFile.RepositoryPath, $"{foundPackage.Id}.{foundPackage.Version}");
DeleteDirectory(packageInstallDirectory);
@@ -797,7 +799,7 @@ public static bool Update(NugetPackageIdentifier currentVersion, NugetPackage ne
LogVerbose("Updating {0} {1} to {2}", currentVersion.Id, currentVersion.Version, newVersion.Version);
Uninstall(currentVersion, false);
newVersion.IsManuallyInstalled = newVersion.IsManuallyInstalled || currentVersion.IsManuallyInstalled;
- return InstallIdentifier(newVersion, refreshAssets);
+ return InstallIdentifier(newVersion, refreshAssets, true);
}
///
@@ -903,13 +905,13 @@ void AddPackageToInstalled(NugetPackage package)
{
// set root packages as manually installed if none are marked as such
foreach (var rootPackage in GetInstalledRootPackages())
- {
- PackagesConfigFile.SetManuallyInstalledFlag(rootPackage);
- }
+ {
+ PackagesConfigFile.SetManuallyInstalledFlag(rootPackage);
+ }
- PackagesConfigFile.Save(PackagesConfigFilePath);
+ PackagesConfigFile.Save(PackagesConfigFilePath);
+ }
}
- }
stopwatch.Stop();
LogVerbose("Getting installed packages took {0} ms", stopwatch.ElapsedMilliseconds);
@@ -1029,11 +1031,24 @@ private static NugetPackage GetInstalledPackage(NugetPackageIdentifier packageId
{
if (packageId.InRange(installedPackage))
{
- LogVerbose(
- "Requested {0} {1}, but {2} is already installed, so using that.",
- packageId.Id,
- packageId.Version,
- installedPackage.Version);
+ var configPackage = PackagesConfigFile.Packages.Find(p => p.Id == packageId.Id);
+ if (configPackage != null && configPackage < installedPackage)
+ {
+ LogVerbose(
+ "Requested {0} {1}. {2} is already installed, but config demands lower version.",
+ packageId.Id,
+ packageId.Version,
+ installedPackage.Version);
+ installedPackage = null;
+ }
+ else
+ {
+ LogVerbose(
+ "Requested {0} {1}, but {2} is already installed, so using that.",
+ packageId.Id,
+ packageId.Version,
+ installedPackage.Version);
+ }
}
else
{
@@ -1147,9 +1162,10 @@ private static void CopyStream(Stream input, Stream output)
///
/// The identifier of the package to install.
/// True to refresh the Unity asset database. False to ignore the changes (temporarily).
- internal static bool InstallIdentifier(NugetPackageIdentifier package, bool refreshAssets = true)
+ /// True to indicate we're calling method as result of Update and don't want to go through IsAlreadyImportedInEngine
+ internal static bool InstallIdentifier(NugetPackageIdentifier package, bool refreshAssets = true, bool isUpdate = false)
{
- if (IsAlreadyImportedInEngine(package, false))
+ if (!isUpdate && IsAlreadyImportedInEngine(package, false))
{
LogVerbose("Package {0} is already imported in engine, skipping install.", package);
return true;
@@ -1160,7 +1176,7 @@ internal static bool InstallIdentifier(NugetPackageIdentifier package, bool refr
if (foundPackage != null)
{
foundPackage.IsManuallyInstalled = package.IsManuallyInstalled;
- return Install(foundPackage, refreshAssets);
+ return Install(foundPackage, refreshAssets, isUpdate);
}
Debug.LogErrorFormat("Could not find {0} {1} or greater.", package.Id, package.Version);
@@ -1188,9 +1204,10 @@ public static void LogVerbose(string format, params object[] args)
///
/// The package to install.
/// True to refresh the Unity asset database. False to ignore the changes (temporarily).
- public static bool Install(NugetPackage package, bool refreshAssets = true)
+ /// True to indicate we're calling method as result of Update and don't want to go through IsAlreadyImportedInEngine
+ public static bool Install(NugetPackage package, bool refreshAssets = true, bool isUpdate = false)
{
- if (IsAlreadyImportedInEngine(package, false))
+ if (!isUpdate && IsAlreadyImportedInEngine(package, false))
{
LogVerbose("Package {0} is already imported in engine, skipping install.", package);
return true;
@@ -1211,6 +1228,16 @@ public static bool Install(NugetPackage package, bool refreshAssets = true)
if (installedPackage > package)
{
+ var configPackage = PackagesConfigFile.Packages.Find(identifier => identifier.Id == package.Id);
+ if (configPackage != null && configPackage < installedPackage)
+ {
+ LogVerbose(
+ "{0} {1} is installed but config needs {2} so downgrading.",
+ installedPackage.Id,
+ installedPackage.Version,
+ package.Version);
+ return Update(installedPackage, package, false);
+ }
LogVerbose(
"{0} {1} is installed. {2} or greater is needed, so using installed version.",
installedPackage.Id,
@@ -1225,7 +1252,6 @@ public static bool Install(NugetPackage package, bool refreshAssets = true)
return true;
}
- var installSuccess = false;
try
{
LogVerbose("Installing: {0} {1}", package.Id, package.Version);
@@ -1360,13 +1386,13 @@ public static bool Install(NugetPackage package, bool refreshAssets = true)
// update the installed packages list
InstalledPackagesDictionary.Add(package.Id, package);
- installSuccess = true;
+ return true;
}
catch (Exception e)
{
WarnIfDotNetAuthenticationIssue(e);
Debug.LogErrorFormat("Unable to install package {0} {1}\n{2}", package.Id, package.Version, e);
- installSuccess = false;
+ return false;
}
finally
{
@@ -1377,8 +1403,6 @@ public static bool Install(NugetPackage package, bool refreshAssets = true)
EditorUtility.ClearProgressBar();
}
}
-
- return installSuccess;
}
private static void WarnIfDotNetAuthenticationIssue(Exception e)
@@ -1454,40 +1478,39 @@ public static void Restore()
var stopwatch = new Stopwatch();
stopwatch.Start();
+ var somethingChanged = false;
try
{
- var progressStep = 1.0f / PackagesConfigFile.Packages.Count;
- float currentProgress = 0;
-
- // copy the list since the InstallIdentifier operation below changes the actual installed packages list
- var packagesToInstall = new List(PackagesConfigFile.Packages);
+ var packagesToInstall = PackagesConfigFile.Packages.FindAll(package => !IsInstalled(package));
+ if (packagesToInstall.Count > 0)
+ {
+ var progressStep = 1.0f / packagesToInstall.Count;
+ float currentProgress = 0;
- LogVerbose("Restoring {0} packages.", packagesToInstall.Count);
+ LogVerbose("Restoring {0} packages.", packagesToInstall.Count);
- foreach (var package in packagesToInstall)
- {
- if (package != null)
+ foreach (var package in packagesToInstall)
{
- EditorUtility.DisplayProgressBar(
- "Restoring NuGet Packages",
- string.Format("Restoring {0} {1}", package.Id, package.Version),
- currentProgress);
-
- if (!IsInstalled(package))
+ if (package != null)
{
+ EditorUtility.DisplayProgressBar(
+ "Restoring NuGet Packages",
+ string.Format("Restoring {0} {1}", package.Id, package.Version),
+ currentProgress);
LogVerbose("---Restoring {0} {1}", package.Id, package.Version);
InstallIdentifier(package);
+ somethingChanged = true;
}
- else
- {
- LogVerbose("---Already installed: {0} {1}", package.Id, package.Version);
- }
- }
- currentProgress += progressStep;
+ currentProgress += progressStep;
+ }
+ }
+ else
+ {
+ LogVerbose("No packages need restoring.");
}
- CheckForUnnecessaryPackages();
+ somethingChanged = somethingChanged || CheckForUnnecessaryPackages();
}
catch (Exception e)
{
@@ -1498,19 +1521,24 @@ public static void Restore()
stopwatch.Stop();
LogVerbose("Restoring packages took {0} ms", stopwatch.ElapsedMilliseconds);
- AssetDatabase.Refresh();
+ if (somethingChanged)
+ {
+ AssetDatabase.Refresh();
+ }
+
EditorUtility.ClearProgressBar();
}
}
- internal static void CheckForUnnecessaryPackages()
+ internal static bool CheckForUnnecessaryPackages()
{
if (!Directory.Exists(NugetConfigFile.RepositoryPath))
{
- return;
+ return false;
}
var directories = Directory.GetDirectories(NugetConfigFile.RepositoryPath, "*", SearchOption.TopDirectoryOnly);
+ var somethingDeleted = false;
foreach (var folder in directories)
{
var folderName = Path.GetFileName(folder);
@@ -1541,12 +1569,20 @@ internal static void CheckForUnnecessaryPackages()
if (!installed)
{
+ somethingDeleted = true;
LogVerbose("---DELETE unnecessary package {0}", folder);
DeleteDirectory(folder);
DeleteFile(folder + ".meta");
}
}
+
+ if (somethingDeleted)
+ {
+ UpdateInstalledPackages();
+ }
+
+ return somethingDeleted;
}
///
diff --git a/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs b/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs
index 0675fc26..974e595a 100644
--- a/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs
+++ b/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs
@@ -67,6 +67,28 @@ private void OnPreprocessAsset()
LogResults(results);
}
+ ///
+ /// Called when the asset database finishes importing assets.
+ /// We use it to check if packages.config has been changed and if so, we want to restore packages.
+ ///
+ internal static void OnPostprocessAllAssets(string[] importedAssets,
+ string[] deletedAssets,
+ string[] movedAssets,
+ string[] movedFromAssetPaths)
+ {
+ var packagesConfigFilePath = Path.GetFullPath(NugetHelper.PackagesConfigFilePath);
+ var foundPackagesConfigAsset = importedAssets.Any(
+ importedAsset => Path.GetFullPath(importedAsset).Equals(packagesConfigFilePath, StringComparison.Ordinal));
+
+ if (!foundPackagesConfigAsset)
+ {
+ return;
+ }
+
+ NugetHelper.ReloadPackagesConfig();
+ NugetHelper.Restore();
+ }
+
private static IEnumerable<(string AssetType, string AssetPath, ResultStatus Status)> HandleAsset(string projectRelativeAssetPath,
string absoluteRepositoryPath,
bool reimport)
diff --git a/src/NuGetForUnity/Editor/PackagesConfigFile.cs b/src/NuGetForUnity/Editor/PackagesConfigFile.cs
index 3bf2aba8..8e7fa640 100644
--- a/src/NuGetForUnity/Editor/PackagesConfigFile.cs
+++ b/src/NuGetForUnity/Editor/PackagesConfigFile.cs
@@ -24,7 +24,7 @@ public class PackagesConfigFile
///
/// Gets the s contained in the package.config file.
///
- public List Packages { get; private set; }
+ public List Packages { get; private set; } = new List();
///
/// Adds a package to the packages.config file.
diff --git a/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs b/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs
index bbbb3120..297909ba 100644
--- a/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs
+++ b/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs
@@ -39,17 +39,17 @@ internal static HashSet GetAlreadyImportedLibs()
// Search the all project assemblies that are not editor only.
// We only use player assemblies as we don't need to collect UnityEditor assemblies, we don't support installing NuGet packages with reference to UnityEditor.
#if UNITY_2019_3_OR_NEWER
- const AssembliesType assemblieType = AssembliesType.PlayerWithoutTestAssemblies;
+ const AssembliesType assemblyType = AssembliesType.PlayerWithoutTestAssemblies;
#else
- const AssembliesType assemblieType = AssembliesType.Player;
+ const AssembliesType assemblyType = AssembliesType.Player;
#endif
- var projectAssemblies = CompilationPipeline.GetAssemblies(assemblieType)
+ var projectAssemblies = CompilationPipeline.GetAssemblies(assemblyType)
.Where(playerAssembly => playerAssembly.flags != AssemblyFlags.EditorAssembly);
// Collect all referenced assemblies but exclude all assemblies installed by NuGetForUnity.
- var porojectReferences = projectAssemblies.SelectMany(playerAssembly => playerAssembly.allReferences);
+ var projectReferences = projectAssemblies.SelectMany(playerAssembly => playerAssembly.allReferences);
alreadyImportedLibs = new HashSet(
- porojectReferences.Select(compiledAssemblyReference => Path.GetFileNameWithoutExtension(compiledAssemblyReference))
+ projectReferences.Select(Path.GetFileNameWithoutExtension)
.Where(assemblyName => !alreadyInstalledDllFileNames.Contains(assemblyName)));
if (PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup) == ApiCompatibilityLevel.NET_Standard_2_0)