diff --git a/src/NuGetForUnity/Editor/Configuration/ConfigurationManager.cs b/src/NuGetForUnity/Editor/Configuration/ConfigurationManager.cs index bebc8e09..bbbdbe2b 100644 --- a/src/NuGetForUnity/Editor/Configuration/ConfigurationManager.cs +++ b/src/NuGetForUnity/Editor/Configuration/ConfigurationManager.cs @@ -34,8 +34,16 @@ public static class ConfigurationManager static ConfigurationManager() { - NugetConfigFileDirectoryPath = UnityPathHelper.AbsoluteAssetsPath; - NugetConfigFilePath = Path.Combine(NugetConfigFileDirectoryPath, NugetConfigFile.FileName); + NugetConfigFilePath = Path.Combine(UnityPathHelper.AbsoluteUnityPackagesNugetPath, NugetConfigFile.FileName); + if (File.Exists(NugetConfigFilePath)) + { + NugetConfigFileDirectoryPath = UnityPathHelper.AbsoluteUnityPackagesNugetPath; + } + else + { + NugetConfigFilePath = Path.Combine(UnityPathHelper.AbsoluteAssetsPath, NugetConfigFile.FileName); + NugetConfigFileDirectoryPath = UnityPathHelper.AbsoluteAssetsPath; + } } /// @@ -45,7 +53,7 @@ static ConfigurationManager() /// . /// [NotNull] - public static string NugetConfigFilePath { get; } + public static string NugetConfigFilePath { get; private set; } /// /// Gets the loaded NuGet.config file that holds the settings for NuGet. @@ -69,7 +77,7 @@ public static NugetConfigFile NugetConfigFile /// Gets the path to the directory containing the NuGet.config file. /// [NotNull] - internal static string NugetConfigFileDirectoryPath { get; } + internal static string NugetConfigFileDirectoryPath { get; private set; } /// /// Gets a value indicating whether verbose logging is enabled. @@ -207,5 +215,28 @@ public static INugetPackage GetSpecificPackage([NotNull] INugetPackageIdentifier { return ActivePackageSource.GetSpecificPackage(nugetPackageIdentifier); } + + /// + /// Moves the Nuget.config under newPlacement and updated local properties to point to it. + /// + /// New placement for configs. + internal static void MoveConfig(PackageInstallLocation newInstallLocation) + { + NugetConfigFile.ChangeInstallLocation(newInstallLocation); + var newConfigsPath = newInstallLocation == PackageInstallLocation.InPackagesFolder ? + UnityPathHelper.AbsoluteUnityPackagesNugetPath : + UnityPathHelper.AbsoluteAssetsPath; + var newConfigFilePath = Path.Combine(newConfigsPath, NugetConfigFile.FileName); + + File.Move(NugetConfigFilePath, newConfigFilePath); + var configMeta = NugetConfigFilePath + ".meta"; + if (File.Exists(configMeta)) + { + File.Move(configMeta, newConfigFilePath + ".meta"); + } + + NugetConfigFilePath = newConfigFilePath; + NugetConfigFileDirectoryPath = newConfigsPath; + } } } diff --git a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs index e0f451a3..1db2d7b1 100644 --- a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs @@ -60,11 +60,17 @@ public class NugetConfigFile private const string SupportsPackageIdSearchFilterAttributeName = "supportsPackageIdSearchFilter"; - /// - /// The incomplete path that is saved. The path is expanded and made public via the property above. - /// - [CanBeNull] - private string savedRepositoryPath; + [NotNull] + private readonly string unityPackagesNugetInstallPath = Path.Combine(UnityPathHelper.AbsoluteUnityPackagesNugetPath, "InstalledPackages"); + + [NotNull] + private string configuredRepositoryPath = "Packages"; + + [NotNull] + private string packagesConfigDirectoryPath = Application.dataPath; + + [NotNull] + private string repositoryPath = Path.GetFullPath(Path.Combine(Application.dataPath, "Packages")); /// /// Gets the list of package sources that are defined in the NuGet.config file. @@ -87,7 +93,35 @@ public class NugetConfigFile /// Gets the absolute path where packages are to be installed. /// [NotNull] - public string RepositoryPath { get; private set; } = Path.GetFullPath(Path.Combine(Application.dataPath, "Packages")); + public string RepositoryPath + { + get => InstallLocation == PackageInstallLocation.InPackagesFolder ? unityPackagesNugetInstallPath : repositoryPath; + + private set => repositoryPath = value; + } + + /// + /// Gets or sets the incomplete path that is saved. The path is expanded and made public via the property above. + /// + [NotNull] + public string ConfiguredRepositoryPath + { + get => configuredRepositoryPath; + + set + { + configuredRepositoryPath = value; + + var expandedPath = Environment.ExpandEnvironmentVariables(value); + + if (!Path.IsPathRooted(expandedPath)) + { + expandedPath = Path.Combine(Application.dataPath, expandedPath); + } + + RepositoryPath = Path.GetFullPath(expandedPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + } + } /// /// Gets the default package source to push NuGet packages to. @@ -121,7 +155,15 @@ public class NugetConfigFile /// Gets or sets absolute path to directory containing packages.config file. /// [NotNull] - public string PackagesConfigDirectoryPath { get; set; } = Application.dataPath; + public string PackagesConfigDirectoryPath + { + get => + InstallLocation == PackageInstallLocation.InPackagesFolder ? + UnityPathHelper.AbsoluteUnityPackagesNugetPath : + packagesConfigDirectoryPath; + + set => packagesConfigDirectoryPath = value; + } /// /// Gets the relative path to directory containing packages.config file. The path is relative to the folder containing the 'NuGet.config' file. @@ -144,6 +186,11 @@ public string RelativePackagesConfigDirectoryPath /// public int RequestTimeoutSeconds { get; set; } = DefaultRequestTimeout; + /// + /// Gets the value that tells the system how to determine where the packages are to be installed and configurations are to be stored. + /// + internal PackageInstallLocation InstallLocation { get; private set; } + /// /// Gets the list of enabled plugins. /// @@ -297,18 +344,13 @@ public static NugetConfigFile Load([NotNull] string filePath) var key = add.Attribute("key")?.Value; var value = add.Attribute("value")?.Value ?? throw new InvalidOperationException($"config misses 'value' attribute. Element:\n{add}"); - if (string.Equals(key, "repositoryPath", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(key, "packageInstallLocation", StringComparison.OrdinalIgnoreCase)) { - configFile.savedRepositoryPath = value; - configFile.RepositoryPath = Environment.ExpandEnvironmentVariables(value); - - if (!Path.IsPathRooted(configFile.RepositoryPath)) - { - configFile.RepositoryPath = Path.Combine(Application.dataPath, configFile.RepositoryPath); - } - - configFile.RepositoryPath = Path.GetFullPath( - configFile.RepositoryPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + configFile.InstallLocation = (PackageInstallLocation)Enum.Parse(typeof(PackageInstallLocation), value); + } + else if (string.Equals(key, "repositoryPath", StringComparison.OrdinalIgnoreCase)) + { + configFile.ConfiguredRepositoryPath = value; } else if (string.Equals(key, "DefaultPushSource", StringComparison.OrdinalIgnoreCase)) { @@ -362,6 +404,7 @@ public static NugetConfigFile CreateDefaultFile([NotNull] string filePath) + @@ -454,18 +497,23 @@ public void Save([NotNull] string filePath) var config = new XElement("config"); - if (!string.IsNullOrEmpty(savedRepositoryPath)) + addElement = new XElement("add"); + addElement.Add(new XAttribute("key", "packageInstallLocation")); + addElement.Add(new XAttribute("value", InstallLocation.ToString())); + config.Add(addElement); + + if (!string.IsNullOrEmpty(ConfiguredRepositoryPath)) { // save the un-expanded repository path addElement = new XElement("add"); addElement.Add(new XAttribute("key", "repositoryPath")); - addElement.Add(new XAttribute("value", savedRepositoryPath)); + addElement.Add(new XAttribute("value", ConfiguredRepositoryPath)); config.Add(addElement); } addElement = new XElement("add"); addElement.Add(new XAttribute("key", PackagesConfigDirectoryPathConfigKey)); - addElement.Add(new XAttribute("value", RelativePackagesConfigDirectoryPath)); + addElement.Add(new XAttribute("value", PathHelper.GetRelativePath(Application.dataPath, packagesConfigDirectoryPath))); config.Add(addElement); // save the default push source @@ -553,5 +601,28 @@ public void Save([NotNull] string filePath) configFile.Save(filePath); } + + /// + /// Changes the package install location config and also moves the packages.config to the new location. + /// + /// New install location to set. + internal void ChangeInstallLocation(PackageInstallLocation newInstallLocation) + { + if (newInstallLocation == InstallLocation) + { + return; + } + + var oldPackagesConfigPath = PackagesConfigFilePath; + InstallLocation = newInstallLocation; + UnityPathHelper.EnsurePackageInstallDirectoryIsSetup(); + var newConfigPath = PackagesConfigFilePath; + File.Move(oldPackagesConfigPath, newConfigPath); + var configMeta = oldPackagesConfigPath + ".meta"; + if (File.Exists(configMeta)) + { + File.Move(configMeta, newConfigPath + ".meta"); + } + } } } diff --git a/src/NuGetForUnity/Editor/Configuration/PackageInstallLocation.cs b/src/NuGetForUnity/Editor/Configuration/PackageInstallLocation.cs new file mode 100644 index 00000000..bab1c548 --- /dev/null +++ b/src/NuGetForUnity/Editor/Configuration/PackageInstallLocation.cs @@ -0,0 +1,22 @@ +namespace NugetForUnity.Configuration +{ + /// + /// Tells the system how to determine where the packages are to be installed and configurations are to be stored. + /// + internal enum PackageInstallLocation + { + /// + /// This option will place Nuget.config into the Assets folder and will allow the user to + /// specify custom location within Assets folder for packages.config and package installation + /// folder. + /// + CustomWithinAssets, + + /// + /// This options will place the Nuget.config and packages.config under Packages/nuget-packages + /// and will install the packages under Packages/nuget-packages/InstalledPackages. + /// + /// . + InPackagesFolder, + } +} diff --git a/src/NuGetForUnity/Editor/Configuration/PackageInstallLocation.cs.meta b/src/NuGetForUnity/Editor/Configuration/PackageInstallLocation.cs.meta new file mode 100644 index 00000000..d47762b3 --- /dev/null +++ b/src/NuGetForUnity/Editor/Configuration/PackageInstallLocation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52ddf392568655f4dbb92a118f9c7f4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/NuGetForUnity/Editor/Configuration/PackagesConfigFile.cs b/src/NuGetForUnity/Editor/Configuration/PackagesConfigFile.cs index d626978e..5ce05aec 100644 --- a/src/NuGetForUnity/Editor/Configuration/PackagesConfigFile.cs +++ b/src/NuGetForUnity/Editor/Configuration/PackagesConfigFile.cs @@ -181,7 +181,9 @@ internal static void Move([NotNull] string newPath) var nugetConfig = ConfigurationManager.NugetConfigFile; var oldFilePath = nugetConfig.PackagesConfigFilePath; var oldPath = nugetConfig.PackagesConfigDirectoryPath; - nugetConfig.PackagesConfigDirectoryPath = newPath; + + // We need to make sure saved path is using forward slashes so it works on all systems + nugetConfig.PackagesConfigDirectoryPath = newPath.Replace("\\", "/"); var newFilePath = Path.GetFullPath(Path.Combine(newPath, FileName)); try { diff --git a/src/NuGetForUnity/Editor/Helper/UnityPathHelper.cs b/src/NuGetForUnity/Editor/Helper/UnityPathHelper.cs index 4ab6ba4c..ff0224e4 100644 --- a/src/NuGetForUnity/Editor/Helper/UnityPathHelper.cs +++ b/src/NuGetForUnity/Editor/Helper/UnityPathHelper.cs @@ -3,6 +3,7 @@ using System; using System.IO; using JetBrains.Annotations; +using NugetForUnity.Configuration; using UnityEngine; #region No ReShaper @@ -28,8 +29,15 @@ static UnityPathHelper() { AbsoluteAssetsPath = Path.GetFullPath(Application.dataPath); AbsoluteProjectPath = Path.GetDirectoryName(AbsoluteAssetsPath) ?? throw new InvalidOperationException("Can't detect project root."); + AbsoluteUnityPackagesNugetPath = Path.GetFullPath(Path.Combine(AbsoluteAssetsPath, "../Packages/nuget-packages")); } + /// + /// Gets the absolute path to 'project root'/Packages/nuget-packages. + /// + [NotNull] + internal static string AbsoluteUnityPackagesNugetPath { get; } + /// /// Gets the absolute path to the Unity-Project 'Assets' directory. /// @@ -53,10 +61,42 @@ internal static bool IsPathInAssets([NotNull] string path) return !Path.IsPathRooted(assetsRelativePath) && !assetsRelativePath.StartsWith("..", StringComparison.Ordinal); } + /// + /// Checks if given relative path is a valid for packages installations. + /// + /// Relative path to check. + /// True if path is within Assets folder or Packages subfolder, false otherwise. + internal static bool IsValidInstallPath([NotNull] string path) + { + return !Path.IsPathRooted(path) && !path.StartsWith("..", StringComparison.Ordinal); + } + + /// + /// Ensures that the package install directory exists and in case it is under Unity's + /// Packages folder that it contains a dummy package.json file so that Unity can see it. + /// + internal static void EnsurePackageInstallDirectoryIsSetup() + { + Directory.CreateDirectory(ConfigurationManager.NugetConfigFile.RepositoryPath); + + if (ConfigurationManager.NugetConfigFile.InstallLocation == PackageInstallLocation.CustomWithinAssets) + { + return; + } + + var jsonPath = Path.Combine(AbsoluteUnityPackagesNugetPath, "package.json"); + if (!File.Exists(jsonPath)) + { + File.WriteAllText( + jsonPath, + @"{ ""name"": ""nuget-packages"",""version"": ""1.0.0"",""displayName"": ""NuGetPackages"", ""description"": ""NuGetPackages"", ""dependencies"": {}}"); + } + } + /// /// Returns the path relative to Assets directory, or "." if it is the Assets directory. /// - /// The path of witch we calculate the relative path of. + /// The path of which we calculate the relative path of. /// The path relative to Assets directory, or "." if it is the Assets directory. [NotNull] private static string GetAssetsRelativePath([NotNull] string path) diff --git a/src/NuGetForUnity/Editor/NugetPackageInstaller.cs b/src/NuGetForUnity/Editor/NugetPackageInstaller.cs index 35fa8f6f..9fd20529 100644 --- a/src/NuGetForUnity/Editor/NugetPackageInstaller.cs +++ b/src/NuGetForUnity/Editor/NugetPackageInstaller.cs @@ -161,7 +161,7 @@ private static bool Install([NotNull] INugetPackage package, bool refreshAssets, if (File.Exists(cachedPackagePath)) { - var baseDirectory = Path.Combine(ConfigurationManager.NugetConfigFile.RepositoryPath, $"{package.Id}.{package.Version}"); + var baseDirectory = package.GetPackageInstallPath(); // unzip the package using (var zip = ZipFile.OpenRead(cachedPackagePath)) diff --git a/src/NuGetForUnity/Editor/PackageContentManager.cs b/src/NuGetForUnity/Editor/PackageContentManager.cs index cb41e71e..56c460a2 100644 --- a/src/NuGetForUnity/Editor/PackageContentManager.cs +++ b/src/NuGetForUnity/Editor/PackageContentManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using JetBrains.Annotations; using NugetForUnity.Configuration; using NugetForUnity.Helper; @@ -21,7 +22,7 @@ internal static class PackageContentManager /// The package to remove all its content of. internal static void DeletePackageContentPackage([NotNull] INugetPackageIdentifier package) { - var packageInstallDirectory = GetPackageInstallDirectory(package); + var packageInstallDirectory = package.GetPackageInstallPath(); FileSystemHelper.DeleteDirectory(packageInstallDirectory, true); var metaFile = $"{packageInstallDirectory}.meta"; @@ -38,7 +39,7 @@ internal static void DeletePackageContentPackage([NotNull] INugetPackageIdentifi /// The NugetPackage to clean. internal static void CleanInstallationDirectory([NotNull] INugetPackageIdentifier package) { - var packageInstallDirectory = GetPackageInstallDirectory(package); + var packageInstallDirectory = package.GetPackageInstallPath(); NugetLogger.LogVerbose("Cleaning {0}", packageInstallDirectory); @@ -289,10 +290,60 @@ internal static void ExtractPackageEntry([NotNull] ZipArchiveEntry entry, [NotNu } } - [NotNull] - private static string GetPackageInstallDirectory([NotNull] INugetPackageIdentifier package) + /// + /// Moves already installed packages from the old to a new path. + /// + /// Old package installation absolute path. + /// New package installation absolute path. + internal static void MoveInstalledPackages(string oldPath, string newPath) { - return Path.Combine(ConfigurationManager.NugetConfigFile.RepositoryPath, $"{package.Id}.{package.Version}"); + if (!Directory.Exists(oldPath)) + { + return; + } + + Directory.CreateDirectory(newPath); + + // We only move the package folders because users might have other things in that folder + foreach (var package in InstalledPackagesManager.InstalledPackages) + { + var packageFolderName = $"{package.Id}.{package.Version}"; + var oldPackagePath = Path.Combine(oldPath, packageFolderName); + var newPackagePath = Path.Combine(newPath, packageFolderName); + if (Directory.Exists(oldPackagePath)) + { + Directory.Move(oldPackagePath, newPackagePath); + var oldMetaPath = $"{oldPackagePath}.meta"; + if (File.Exists(oldMetaPath)) + { + File.Move(oldMetaPath, $"{newPackagePath}.meta"); + } + } + } + + var oldPackageJsonPath = Path.Combine(oldPath, "package.json"); + if (File.Exists(oldPackageJsonPath)) + { + File.Delete(oldPackageJsonPath); + var oldPackageJsonMetaPath = $"{oldPackageJsonPath}.meta"; + if (File.Exists(oldPackageJsonMetaPath)) + { + File.Delete(oldPackageJsonMetaPath); + } + } + + if (Directory.EnumerateFileSystemEntries(oldPath).Any()) + { + return; + } + + // If there is nothing left in the oldPath we remove the whole directory + Directory.Delete(oldPath); + var metaPath = oldPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + ".meta"; + if (File.Exists(metaPath)) + { + File.Delete(metaPath); + } } [NotNull] diff --git a/src/NuGetForUnity/Editor/PackageRestorer.cs b/src/NuGetForUnity/Editor/PackageRestorer.cs index 1033ae7c..8961af9e 100644 --- a/src/NuGetForUnity/Editor/PackageRestorer.cs +++ b/src/NuGetForUnity/Editor/PackageRestorer.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using NugetForUnity.Helper; using UnityEditor; using Debug = UnityEngine.Debug; @@ -16,6 +17,7 @@ public static class PackageRestorer /// True if we want to skip installing dependencies and checking if the lib is imported in Unity. public static void Restore(bool slimRestore) { + UnityPathHelper.EnsurePackageInstallDirectoryIsSetup(); InstalledPackagesManager.UpdateInstalledPackages(); var stopwatch = Stopwatch.StartNew(); diff --git a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs index f5b0417c..bcb49229 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using JetBrains.Annotations; @@ -75,7 +76,8 @@ public class NugetPreferences : SettingsProvider private NugetPreferences() : base("Preferences/NuGet For Unity", SettingsScope.User) { - shouldShowPackagesConfigPathWarning = !UnityPathHelper.IsPathInAssets(ConfigurationManager.NugetConfigFile.PackagesConfigDirectoryPath); + shouldShowPackagesConfigPathWarning = ConfigurationManager.NugetConfigFile.InstallLocation == PackageInstallLocation.CustomWithinAssets && + !UnityPathHelper.IsPathInAssets(ConfigurationManager.NugetConfigFile.PackagesConfigDirectoryPath); var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var enabledPlugins = new HashSet(ConfigurationManager.NugetConfigFile.EnabledPlugins); plugins = assemblies.Where(assembly => assembly.FullName.IndexOf("NugetForUnityPlugin", StringComparison.OrdinalIgnoreCase) >= 0) @@ -108,6 +110,7 @@ public override void OnGUI([CanBeNull] string searchContext) var preferencesChangedThisFrame = false; var sourcePathChangedThisFrame = false; + var needsAssetRefresh = false; var biggestLabelSize = EditorStyles.label.CalcSize(new GUIContent("Request Timeout in seconds")).x; EditorGUIUtility.labelWidth = biggestLabelSize; @@ -149,35 +152,96 @@ public override void OnGUI([CanBeNull] string searchContext) ConfigurationManager.NugetConfigFile.SlimRestore = slimRestore; } - using (new EditorGUILayout.HorizontalScope()) + var newInstallLocation = (PackageInstallLocation)EditorGUILayout.EnumPopup( + "Placement:", + ConfigurationManager.NugetConfigFile.InstallLocation); + if (newInstallLocation != ConfigurationManager.NugetConfigFile.InstallLocation) { - var packagesConfigPath = ConfigurationManager.NugetConfigFile.PackagesConfigDirectoryPath; + var oldRepoPath = ConfigurationManager.NugetConfigFile.RepositoryPath; + InstalledPackagesManager.UpdateInstalledPackages(); // Make sure it is initialized before we move files around + ConfigurationManager.MoveConfig(newInstallLocation); + var newRepoPath = ConfigurationManager.NugetConfigFile.RepositoryPath; + PackageContentManager.MoveInstalledPackages(oldRepoPath, newRepoPath); + + if (newInstallLocation == PackageInstallLocation.CustomWithinAssets && + Directory.Exists(UnityPathHelper.AbsoluteUnityPackagesNugetPath)) + { + Directory.Delete(UnityPathHelper.AbsoluteUnityPackagesNugetPath, true); + } - GUILayout.Label( - new GUIContent("Packages Config path:", $"Absolute path: {packagesConfigPath}"), - GUILayout.Width(EditorGUIUtility.labelWidth)); - GUILayout.Label(ConfigurationManager.NugetConfigFile.RelativePackagesConfigDirectoryPath); + preferencesChangedThisFrame = true; + needsAssetRefresh = true; + } - if (GUILayout.Button("Browse", GUILayout.Width(100))) + if (newInstallLocation == PackageInstallLocation.CustomWithinAssets) + { + using (new EditorGUILayout.HorizontalScope()) { - var newPath = EditorUtility.OpenFolderPanel("Select Folder", packagesConfigPath, string.Empty); + var repositoryPath = ConfigurationManager.NugetConfigFile.RepositoryPath; - if (!string.IsNullOrEmpty(newPath) && newPath != packagesConfigPath) + GUILayout.Label( + new GUIContent("Packages Install path:", $"Absolute path: {repositoryPath}"), + GUILayout.Width(EditorGUIUtility.labelWidth)); + GUILayout.Label(ConfigurationManager.NugetConfigFile.ConfiguredRepositoryPath); + + if (GUILayout.Button("Browse", GUILayout.Width(100))) { - // if the path root is different or it is not under Assets folder, we want to show a warning message - shouldShowPackagesConfigPathWarning = !UnityPathHelper.IsPathInAssets(newPath); + var newPath = EditorUtility.OpenFolderPanel("Select folder where packages will be installed", repositoryPath, string.Empty); + if (!string.IsNullOrEmpty(newPath)) + { + var newRelativePath = PathHelper.GetRelativePath(Application.dataPath, newPath); - PackagesConfigFile.Move(newPath); - preferencesChangedThisFrame = true; + // We need to make sure saved path is using forward slashes so it works on all systems + newRelativePath = newRelativePath.Replace('\\', '/'); + + if (newPath != repositoryPath && UnityPathHelper.IsValidInstallPath(newRelativePath)) + { + PackageContentManager.MoveInstalledPackages(repositoryPath, newPath); + ConfigurationManager.NugetConfigFile.ConfiguredRepositoryPath = newRelativePath; + UnityPathHelper.EnsurePackageInstallDirectoryIsSetup(); + preferencesChangedThisFrame = true; + needsAssetRefresh = true; + } + else if (newPath != repositoryPath) + { + Debug.LogError( + $"Packages install path {newPath} {newRelativePath} is not valid. It must be somewhere under Assets or Packages folder."); + } + } } } - } - if (shouldShowPackagesConfigPathWarning) - { - EditorGUILayout.HelpBox( - "The packages.config is placed outside of Assets folder, this disables the functionality of automatically restoring packages if the file is changed on the disk.", - MessageType.Warning); + using (new EditorGUILayout.HorizontalScope()) + { + var packagesConfigPath = ConfigurationManager.NugetConfigFile.PackagesConfigDirectoryPath; + + GUILayout.Label( + new GUIContent("Packages Config path:", $"Absolute path: {packagesConfigPath}"), + GUILayout.Width(EditorGUIUtility.labelWidth)); + GUILayout.Label(ConfigurationManager.NugetConfigFile.RelativePackagesConfigDirectoryPath); + + if (GUILayout.Button("Browse", GUILayout.Width(100))) + { + var newPath = EditorUtility.OpenFolderPanel("Select Folder", packagesConfigPath, string.Empty); + + if (!string.IsNullOrEmpty(newPath) && newPath != packagesConfigPath) + { + // if the path root is different or it is not under Assets folder, we want to show a warning message + shouldShowPackagesConfigPathWarning = !UnityPathHelper.IsPathInAssets(newPath); + + PackagesConfigFile.Move(newPath); + preferencesChangedThisFrame = true; + needsAssetRefresh = true; + } + } + } + + if (shouldShowPackagesConfigPathWarning) + { + EditorGUILayout.HelpBox( + "The packages.config is placed outside of Assets folder, this disables the functionality of automatically restoring packages if the file is changed on the disk.", + MessageType.Warning); + } } var requestTimeout = EditorGUILayout.IntField( @@ -489,6 +553,12 @@ public override void OnGUI([CanBeNull] string searchContext) // e.g. the 'url' can be changed to a V3 nuget API url so we need to create a V3 package source. ConfigurationManager.LoadNugetConfigFile(); } + + if (needsAssetRefresh) + { + // AssetDatabase.Refresh(); doesn't work when we move the files from Assets to Packages so we use this instead: + EditorApplication.ExecuteMenuItem("Assets/Refresh"); + } } private sealed class NugetPlugin diff --git a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs index aa2057cf..dbed7bfb 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs @@ -385,6 +385,7 @@ private void OnEnable() name = "NuGetForUnity"; titleContent = new GUIContent("NuGet For Unity"); Refresh(false); + UnityPathHelper.EnsurePackageInstallDirectoryIsSetup(); } private void ClearViewCache()