Skip to content

Commit

Permalink
Fixed editor hanging when symbolic link path is no longer valid (#794)
Browse files Browse the repository at this point in the history
* Fixed editor hanging when symbolic link path is no longer valid

* removed unused debug

* Delay call the sync if there were any invalid links
  • Loading branch information
StephenHodgson committed Mar 4, 2021
1 parent f747e78 commit b6f959f
Showing 1 changed file with 169 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
Expand All @@ -22,12 +23,55 @@ public static class SymbolicLinker
/// </summary>
static SymbolicLinker()
{
RunSync(Application.isBatchMode);
EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGui;
EditorApplication.projectChanged += OnProjectChanged;
RunSync(Application.isBatchMode);
}

private static bool hasProjectUpdated = false;

private static void OnProjectChanged()
{
if (hasProjectUpdated) { return; }
hasProjectUpdated = true;

void VerifyLinks(object state)
{
while (hasProjectUpdated)
{
Task.Delay(2500);

var anyInvalid = false;

foreach (var link in Settings.SymbolicLinks)
{
var path = $"{ProjectRoot}{link.TargetRelativePath}";
var attributes = File.GetAttributes(path);
var isValid = VerifySymbolicLink(path);

if (DebugEnabled)
{
Debug.Log($"Checking {path}\nIsValid?{isValid} | {attributes}");
}

if (!isValid)
{
DeleteSymbolicLink(path);
anyInvalid = true;
}
}

if (anyInvalid)
{
EditorApplication.delayCall += () => RunSync(true);
}
}
}

ThreadPool.QueueUserWorkItem(VerifyLinks);
}

private const string LINK_ICON_TEXT = "<=link=>";
private const FileAttributes FOLDER_SYMLINK_ATTRIBUTES = FileAttributes.ReparsePoint;

// Style used to draw the symlink indicator in the project view.
private static GUIStyle SymlinkMarkerStyle => symbolicLinkMarkerStyle ?? (symbolicLinkMarkerStyle = new GUIStyle(EditorStyles.label)
Expand Down Expand Up @@ -64,6 +108,11 @@ public static SymbolicLinkSettings Settings
if (settings == null &&
!string.IsNullOrEmpty(MixedRealityPreferences.SymbolicLinkSettingsPath))
{
if (DebugEnabled)
{
Debug.Log($"Loading symlink settings from :{MixedRealityPreferences.SymbolicLinkSettingsPath}");
}

settings = AssetDatabase.LoadAssetAtPath<SymbolicLinkSettings>(MixedRealityPreferences.SymbolicLinkSettingsPath);
}

Expand All @@ -83,6 +132,11 @@ public static SymbolicLinkSettings Settings
/// <param name="forceUpdate">Bypass the auto load check and force the packages to be updated, even if they're up to date.</param>
public static void RunSync(bool forceUpdate = false)
{
if (DebugEnabled)
{
Debug.Log($"{nameof(RunSync)}({nameof(forceUpdate)} = {forceUpdate})");
}

if (IsSyncing || EditorApplication.isPlayingOrWillChangePlaymode)
{
return;
Expand Down Expand Up @@ -114,6 +168,7 @@ public static void RunSync(bool forceUpdate = false)
}

IsSyncing = true;
AssetDatabase.ReleaseCachedFileHandles();
EditorApplication.LockReloadAssemblies();

if (DebugEnabled)
Expand All @@ -133,6 +188,11 @@ public static void RunSync(bool forceUpdate = false)

if (VerifySymbolicLink(targetAbsolutePath))
{
if (DebugEnabled)
{
Debug.Log($"Is Link Active? {link.IsActive} : {link.TargetRelativePath}");
}

// If we already have the directory in our project, then skip.
if (link.IsActive) { continue; }

Expand Down Expand Up @@ -302,26 +362,123 @@ private static void OnProjectWindowItemGui(string guid, Rect rect)
{
var path = AssetDatabase.GUIDToAssetPath(guid);

if (string.IsNullOrEmpty(path)) { return; }
if (!string.IsNullOrEmpty(path) &&
IsSymbolicPath(path) &&
VerifySymbolicLink(path))
{
GUI.Label(rect, LINK_ICON_TEXT, SymlinkMarkerStyle);
}
}
catch (Exception e)
{
if (DebugEnabled)
{
Debug.LogError(e);
}
}
}

private static bool IsSymbolicPath(string path)
{
try
{
if (!Directory.Exists(path))
{
if (File.Exists(path))
{
return false;
}

Debug.LogError($"Invalid path: {path}");
return false;
}

var attributes = File.GetAttributes(path);

if ((attributes & FOLDER_SYMLINK_ATTRIBUTES) != FOLDER_SYMLINK_ATTRIBUTES) { return; }
if (attributes == (FileAttributes)(-1))
{
Debug.LogError($"Invalid file attributes found for {path}!");

GUI.Label(rect, LINK_ICON_TEXT, SymlinkMarkerStyle);
if (Directory.Exists(path))
{
Directory.Delete(path);
}

if (Settings.SymbolicLinks.Any(link => link.TargetRelativePath.Contains(path))) { return; }
return false;
}

var fullPath = Path.GetFullPath(path);
if ((attributes & FileAttributes.Directory) != FileAttributes.Directory)
{
return false;
}

if (DeleteSymbolicLink(fullPath))
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
}

private static bool VerifySymbolicLink(string targetAbsolutePath)
{
try
{
if (DebugEnabled)
{
Debug.Log($"Removed \"{fullPath}\" symbolic link from project.");
Debug.Log($"Attempting to validate {targetAbsolutePath}");
}

if (!Directory.Exists(targetAbsolutePath))
{
if (DebugEnabled)
{
Debug.LogWarning($"Validated disabled link: {targetAbsolutePath}");
}

return false;
}

var isValid = IsSymbolicPath(targetAbsolutePath);

if (!isValid)
{
if (DebugEnabled)
{
Debug.LogWarning($"Removing invalid link for {targetAbsolutePath}");
}

DeleteSymbolicLink(targetAbsolutePath);
}
else
{
var tempFile = $"{targetAbsolutePath}/temp.txt";

try
{
if (!File.Exists(tempFile))
{
File.CreateText(tempFile).Dispose();
}

if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
catch (DirectoryNotFoundException)
{
return false;
}
}

return isValid && Directory.Exists(targetAbsolutePath);
}
catch
catch (Exception e)
{
// ignored
Debug.LogError(e);
return false;
}
}

Expand Down Expand Up @@ -431,35 +588,6 @@ private static bool DeleteSymbolicLink(string path)
return true;
}

private static bool VerifySymbolicLink(string targetAbsolutePath)
{
if (DebugEnabled)
{
Debug.Log($"Attempting to validate {targetAbsolutePath}");
}

var isValid = IsSymbolicPath(targetAbsolutePath);

if (!isValid &&
Directory.Exists(targetAbsolutePath))
{
if (DebugEnabled)
{
Debug.LogWarning($"Removing invalid link for {targetAbsolutePath}");
}

DeleteSymbolicLink(targetAbsolutePath);
}

return isValid && Directory.Exists(targetAbsolutePath);
}

private static bool IsSymbolicPath(string path)
{
var pathInfo = new FileInfo(path);
return pathInfo.Attributes.HasFlag(FileAttributes.ReparsePoint);
}

private static string AddSubfolderPathToTarget(string sourcePath, string targetPath)
{
var subFolder = sourcePath.Substring(sourcePath.LastIndexOf("/", StringComparison.Ordinal) + 1).Replace("~", string.Empty);
Expand Down

0 comments on commit b6f959f

Please sign in to comment.