352 lines
11 KiB
C#
352 lines
11 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using UnityEditor;
|
|
|
|
namespace AssetDependencyGraph
|
|
{
|
|
public static class FileExtensionHelper
|
|
{
|
|
public static string GetTypeByExtension(string ext)
|
|
{
|
|
switch (ext.ToLowerInvariant())
|
|
{
|
|
case ".a":
|
|
case ".dll":
|
|
case ".so":
|
|
case ".exe":
|
|
case ".dynlib":
|
|
return "Executable";
|
|
case ".asmdef":
|
|
case ".asmref":
|
|
return "UnityAssembly";
|
|
case ".cs":
|
|
case ".lua":
|
|
case ".js":
|
|
case ".ts":
|
|
case ".java":
|
|
case ".h":
|
|
case ".cpp":
|
|
case ".cxx":
|
|
case ".mm":
|
|
case ".py":
|
|
case ".bat":
|
|
case ".jar":
|
|
case ".arr":
|
|
case ".jslib":
|
|
return "SourceFile";
|
|
case ".gradle":
|
|
return "MakeFile";
|
|
case ".dat":
|
|
case ".data":
|
|
return "DatFile";
|
|
case ".mp3":
|
|
case ".ogg":
|
|
case ".wav":
|
|
return "AudioClip";
|
|
case ".mp4":
|
|
case ".webm":
|
|
return "VideoClip";
|
|
case ".mat":
|
|
return "Material";
|
|
case ".rendertexture":
|
|
case ".dds":
|
|
case ".exr":
|
|
case ".hdr":
|
|
case ".png":
|
|
case ".jpg":
|
|
case ".gif":
|
|
case ".psd":
|
|
case ".bmp":
|
|
case ".tiff":
|
|
case ".tga":
|
|
case ".gradient":
|
|
case ".spriteatlas":
|
|
return "Texture";
|
|
case ".obj":
|
|
case ".fbx":
|
|
case ".mesh":
|
|
return "Mesh";
|
|
case ".shader":
|
|
case ".surfshader":
|
|
case ".shadergraph":
|
|
return "Shader";
|
|
case ".compute":
|
|
return "ComputeShader";
|
|
case ".hlsl":
|
|
case ".cginc":
|
|
case ".shadersubgraph":
|
|
return "ShaderHeader";
|
|
case ".otf":
|
|
case ".ttf":
|
|
return "Font";
|
|
case ".byte":
|
|
case ".bytes":
|
|
case ".bin":
|
|
return "Binary";
|
|
case ".txt":
|
|
case ".md":
|
|
case ".chm":
|
|
case ".yml":
|
|
case ".url":
|
|
case ".json":
|
|
case ".json5":
|
|
case ".xml":
|
|
case ".uxml":
|
|
case ".nson":
|
|
case ".config":
|
|
case ".pdf":
|
|
return "TextFile";
|
|
case ".xlsx":
|
|
case ".xls":
|
|
return "Excel";
|
|
default:
|
|
return "UnknowFileType";
|
|
}
|
|
}
|
|
|
|
|
|
public static bool IsPackage(string ext)
|
|
{
|
|
switch (ext.ToLowerInvariant())
|
|
{
|
|
case ".prefab":
|
|
case ".unity":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool NeedAnalyzeDepend(string ext)
|
|
{
|
|
switch (ext.ToLowerInvariant())
|
|
{
|
|
case ".prefab":
|
|
case ".unity":
|
|
case ".asset":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
public static bool Exclude(string path) => path.EndsWith(".meta")
|
|
|| path.EndsWith(".unitypackage")
|
|
|| path.EndsWith(".preset")
|
|
|| path.EndsWith(".backup")
|
|
|| path.EndsWith(".tmp")
|
|
|| path.EndsWith(".editor")
|
|
|| path.EndsWith(".zip")
|
|
|| path.EndsWith(".scenetemplate");
|
|
}
|
|
|
|
public interface IDependencyAnalysis
|
|
{
|
|
void Analyze(string path, ConcurrentDictionary<AssetIdentify, AssetNode> result);
|
|
|
|
public (AssetIdentify id, AssetNode node) GetOrCreateFolderNode(string path, ConcurrentDictionary<AssetIdentify, AssetNode> result)
|
|
{
|
|
AssetIdentify k = null;
|
|
|
|
foreach (var item in result.Keys)
|
|
{
|
|
if (item.Path == path)
|
|
{
|
|
k = item;
|
|
}
|
|
}
|
|
|
|
if (k == null)
|
|
{
|
|
k = new AssetIdentify()
|
|
{
|
|
Path = path,
|
|
AssetType = "Folder",
|
|
Guid = null,
|
|
Md5 = null
|
|
};
|
|
result[k] = new FolderNode()
|
|
{
|
|
Self = k,
|
|
AssetType = "Folder",
|
|
};
|
|
}
|
|
|
|
return (k, result[k]);
|
|
}
|
|
|
|
public (AssetIdentify id, AssetNode node) GetOrCreateAssetNode(string path, ConcurrentDictionary<AssetIdentify, AssetNode> result)
|
|
{
|
|
AssetIdentify k = null;
|
|
|
|
foreach (var item in result.Keys)
|
|
{
|
|
if (item.Path == path)
|
|
{
|
|
k = item;
|
|
}
|
|
}
|
|
|
|
if (k == null)
|
|
{
|
|
k = new AssetIdentify()
|
|
{
|
|
Path = path,
|
|
Guid = null,
|
|
//Md5 = Utils.Md5(path)
|
|
};
|
|
if (FileExtensionHelper.IsPackage(Path.GetExtension(path)))
|
|
{
|
|
result[k] = new PackageNode()
|
|
{
|
|
Self = k,
|
|
};
|
|
}
|
|
else
|
|
{
|
|
result[k] = new AssetNode()
|
|
{
|
|
Self = k,
|
|
};
|
|
}
|
|
}
|
|
|
|
return (k, result[k]);
|
|
}
|
|
}
|
|
|
|
public class FolderDependencyAnalysis : IDependencyAnalysis
|
|
{
|
|
public void Analyze(string path, ConcurrentDictionary<AssetIdentify, AssetNode> result)
|
|
{
|
|
var dependencyAnalysis = (this as IDependencyAnalysis);
|
|
var k = dependencyAnalysis.GetOrCreateFolderNode(path, result);
|
|
foreach (string file in Directory.EnumerateFiles(path))
|
|
{
|
|
if (FileExtensionHelper.Exclude(file))
|
|
{
|
|
continue;
|
|
}
|
|
var p = file.ToUnityRelatePath().ToUniversalPath();
|
|
var selfNode = result[k.id];
|
|
var kv = dependencyAnalysis.GetOrCreateAssetNode(p, result);
|
|
|
|
kv.node.Dependent.Add(selfNode.Self);
|
|
selfNode.Dependencies.Add(kv.id);
|
|
}
|
|
|
|
foreach (string directory in Directory.EnumerateDirectories(path))
|
|
{
|
|
var p = directory.ToUnityRelatePath().ToUniversalPath();
|
|
var selfNode = result[k.id];
|
|
var kv = dependencyAnalysis.GetOrCreateFolderNode(p, result);
|
|
kv.node.Dependent.Add(selfNode.Self);
|
|
selfNode.Dependencies.Add(kv.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class UnityDependencyAnalysis2 : IDependencyAnalysis
|
|
{
|
|
UnityLmdb unityLmdb;
|
|
HashSet<string> processed = new();
|
|
public UnityDependencyAnalysis2()
|
|
{
|
|
unityLmdb = new UnityLmdb();
|
|
unityLmdb.ResolveGuidPath();
|
|
}
|
|
|
|
public void Analyze(string path, ConcurrentDictionary<AssetIdentify, AssetNode> result)
|
|
{
|
|
if (!processed.Add(path))
|
|
{
|
|
return;
|
|
}
|
|
var dependencyAnalysis = (this as IDependencyAnalysis);
|
|
|
|
var kv = dependencyAnalysis.GetOrCreateAssetNode(path, result);
|
|
var relatePath = path.ToUnityRelatePath();
|
|
kv.id.Guid = unityLmdb.GetGuidByPath(relatePath);
|
|
var selfNode = kv.node;
|
|
var ext = Path.GetExtension(path);
|
|
var assetType = FileExtensionHelper.GetTypeByExtension(ext);
|
|
|
|
selfNode.AssetType = assetType;
|
|
kv.id.AssetType = assetType;
|
|
if (FileExtensionHelper.NeedAnalyzeDepend(ext))
|
|
{
|
|
var dependencies = UnityFileApi.DependencyTool.GetDependencies(path);
|
|
for (int i = 0; i < dependencies.Count; i++)
|
|
{
|
|
var dep = dependencies[i].ToUnityRelatePath();
|
|
var depkv = dependencyAnalysis.GetOrCreateAssetNode(dep, result);
|
|
depkv.node.Dependent.Add(selfNode.Self);
|
|
selfNode.Dependencies.Add(depkv.id);
|
|
Analyze(dep, result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class DependencyAnalyzer
|
|
{
|
|
private Dictionary<Predicate<string>, IDependencyAnalysis> dependencyAnalysisDic = new();
|
|
private ConcurrentDictionary<AssetIdentify, AssetNode> assetIdentify2AssetNodeDic = new();
|
|
private List<string> allPath = new();
|
|
|
|
|
|
|
|
public DependencyAnalyzer()
|
|
{
|
|
//dependencyAnalysisDic.Add(new Predicate<string>(path => !Directory.Exists(path)), new UnityDependencyAnalysis1());
|
|
//dependencyAnalysisDic.Add(new Predicate<string>(path => !Directory.Exists(path)), new UnityDependencyAnalysis2());
|
|
dependencyAnalysisDic.Add(new Predicate<string>(path => Directory.Exists(path)), new FolderDependencyAnalysis());
|
|
}
|
|
|
|
|
|
private void Visivt(string path)
|
|
{
|
|
path = path.ToUniversalPath();
|
|
if (FileExtensionHelper.Exclude(path))
|
|
{
|
|
return;
|
|
}
|
|
allPath.Add(path);
|
|
|
|
}
|
|
|
|
public void Analyze(string rootFolder)
|
|
{
|
|
Stopwatch sw = Stopwatch.StartNew();
|
|
sw.Start();
|
|
Utils.TraverseDirectory(rootFolder, Visivt, -1);
|
|
Parallel.ForEach(allPath, (path) =>
|
|
{
|
|
path = path.ToUnityRelatePath();
|
|
foreach (var item in dependencyAnalysisDic)
|
|
{
|
|
if (item.Key(path))
|
|
{
|
|
item.Value.Analyze(path, assetIdentify2AssetNodeDic);
|
|
}
|
|
}
|
|
});
|
|
|
|
sw.Stop();
|
|
UnityEngine.Debug.Log($"分析引用耗时:{sw.ElapsedMilliseconds / 1000f}s");
|
|
AssetDependencyGraphDB db = new AssetDependencyGraphDB("", "", "localhost");
|
|
sw.Restart();
|
|
db.Clean();
|
|
Parallel.ForEach(assetIdentify2AssetNodeDic, item =>
|
|
{
|
|
db.UpdateOrInsert(item.Value);
|
|
});
|
|
sw.Stop();
|
|
UnityEngine.Debug.Log($"更新数据库:{sw.ElapsedMilliseconds / 1000f}s");
|
|
}
|
|
}
|
|
}
|