using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using UnityEditor.Compilation; using System.Collections.Generic; using IntelligentProjectAnalyzer.Analyzer; using UnityEditor.SceneManagement; // All using statements now refer to the new data-only class using static IntelligentProjectAnalyzer.Editor.DependencyBuilderData; namespace IntelligentProjectAnalyzer.Editor { /// /// Handles all communication with the Unity API that must occur on the main thread /// to gather data for the dependency analysis. /// public static class AssetDataFetcher { /// /// Finds all relevant asset GUIDs for scripts, prefabs, and ScriptableObjects. /// /// A tuple containing queues of GUIDs for each asset type. public static (Queue scriptGuids, Queue prefabGuids, Queue soGuids, Queue sceneGuids, Queue miscGuids) FindAssetGuids() { var scripts = new Queue(AssetDatabase.FindAssets("t:MonoScript")); var prefabs = new Queue(AssetDatabase.FindAssets("t:Prefab")); var scriptableObjects = new Queue(AssetDatabase.FindAssets("t:ScriptableObject")); var scenes = new Queue(AssetDatabase.FindAssets("t:Scene")); var allGuids = new Queue(AssetDatabase.FindAssets("")); var knownGuids = new HashSet(); knownGuids.UnionWith(scriptableObjects); knownGuids.UnionWith(prefabs); knownGuids.UnionWith(scripts); knownGuids.UnionWith(scenes); var miscGuids = new Queue(allGuids.Except(knownGuids)); return (scripts, prefabs, scriptableObjects, scenes, miscGuids); } /// /// Gathers metadata for a single script asset. Its dependencies will be filled in later. /// public static ScriptMetadata PreFetchScriptMetadata(string guid) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/")) return null; return new ScriptMetadata { Guid = guid, FullPath = Path.GetFullPath(assetPath).Replace('\\', '/') }; } /// /// Gathers metadata for a single prefab asset, including its direct script dependencies. /// public static PrefabMetadata PreFetchPrefabMetadata(string guid) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/")) return null; var go = AssetDatabase.LoadAssetAtPath(assetPath); if (go == null) return null; var prefabMetadata = new PrefabMetadata { Guid = guid }; var hash = new HashSet(); // Find all components on the prefab and its children to get their script GUIDs. var components = go.GetComponentsInChildren(true); foreach (var component in components) { if (component == null) continue; var script = MonoScript.FromMonoBehaviour(component as MonoBehaviour); if (script == null) continue; var scriptAssetPath = AssetDatabase.GetAssetPath(script); if (string.IsNullOrEmpty(scriptAssetPath)) continue; var scriptGuid = AssetDatabase.AssetPathToGUID(scriptAssetPath); if (!string.IsNullOrEmpty(scriptGuid) && scriptAssetPath.StartsWith("Assets/")) { hash.Add(scriptGuid); } } var assetDependencies = AssetDatabase.GetDependencies(assetPath); foreach (var dependency in assetDependencies) { if (string.IsNullOrEmpty(dependency) || !dependency.StartsWith("Assets/") || dependency == assetPath) { continue; } var dependencyGuid = AssetDatabase.AssetPathToGUID(dependency); if (!string.IsNullOrEmpty(dependencyGuid)) { hash.Add(dependencyGuid); } } prefabMetadata.DependencyGuids = hash.ToList(); return prefabMetadata; } /// /// Gathers metadata for a single ScriptableObject asset, including its direct script dependency. /// public static ScriptableObjectMetadata PreFetchScriptableObjectMetadata(string guid) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/")) return null; var so = AssetDatabase.LoadAssetAtPath(assetPath); if (so == null) return null; var script = MonoScript.FromScriptableObject(so); if (script == null) return null; var scriptAssetPath = AssetDatabase.GetAssetPath(script); if (string.IsNullOrEmpty(scriptAssetPath)) return null; var hash = new HashSet(); var scriptGuid = AssetDatabase.AssetPathToGUID(scriptAssetPath); var soMetadata = new ScriptableObjectMetadata { Guid = guid }; if (!string.IsNullOrEmpty(scriptGuid) && scriptAssetPath.StartsWith("Assets/")) { hash.Add(scriptGuid); } var assetDependencies = AssetDatabase.GetDependencies(assetPath); foreach (var dependency in assetDependencies) { if (string.IsNullOrEmpty(dependency) || !dependency.StartsWith("Assets/") || dependency == assetPath) { continue; } var dependencyGuid = AssetDatabase.AssetPathToGUID(dependency); if (!string.IsNullOrEmpty(dependencyGuid)) { hash.Add(dependencyGuid); } } soMetadata.DependencyGuids = hash.ToList(); return soMetadata; } /// /// Gathers all data required to initialize the Roslyn C# analysis. /// public static RoslynSetupData PreFetchRoslynSetupData(List scriptMetadata) { var setupData = new RoslynSetupData { SourceFiles = scriptMetadata.Select(s => s.FullPath).ToArray(), PreprocessorSymbols = EditorUserBuildSettings.activeScriptCompilationDefines }; var references = new List(); foreach (var assembly in CompilationPipeline.GetAssemblies()) { references.AddRange(assembly.compiledAssemblyReferences.Select(Path.GetFullPath)); } setupData.References = references.Distinct().ToArray(); var prefixes = new List { "System", "UnityEngine", "UnityEditor" }; var settings = AnalyzerSettingsCrud.GetOrCreateSettings(); if (settings != null && settings.CustomSystemTypes != null) { prefixes.AddRange(settings.CustomSystemTypes.Where(p => !string.IsNullOrEmpty(p) && !prefixes.Contains(p))); } setupData.SystemTypePrefixes = prefixes; setupData.TypeToGuidMap = new Dictionary(); var tempAnalyzer = new RoslynTypeDependencyAnalyzer(setupData.SourceFiles, setupData.References, setupData.PreprocessorSymbols, setupData.SystemTypePrefixes); foreach (var pair in tempAnalyzer.TypeToPathMap) { var projectPath = Directory.GetParent(Application.dataPath)?.FullName.Replace('\\', '/'); var relativePath = pair.Value.Replace('\\', '/').Replace(projectPath + "/", ""); var guid = AssetDatabase.AssetPathToGUID(relativePath); if (!string.IsNullOrEmpty(guid)) { setupData.TypeToGuidMap[pair.Key] = guid; } } return setupData; } public static MiscAssetMetadata PreFetchMiscAssetMetadata(string guid) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/")) return null; var miscMetaData = new MiscAssetMetadata() { Guid = guid }; var hash = new HashSet(); var assetDependencies = AssetDatabase.GetDependencies(assetPath); foreach (var dependency in assetDependencies) { if (string.IsNullOrEmpty(dependency) || !dependency.StartsWith("Assets/") || dependency == assetPath) { continue; } var dependencyGuid = AssetDatabase.AssetPathToGUID(dependency); if (!string.IsNullOrEmpty(dependencyGuid)) { hash.Add(dependencyGuid); } } miscMetaData.DependencyGuids = hash.ToList(); return miscMetaData; } public static SceneMetadata PreFetchSceneMetadata(string guid) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/")) return null; var scene = EditorSceneManager.OpenScene(assetPath, OpenSceneMode.Single); var sceneMetaData = new SceneMetadata() { Guid = guid }; var hash = new HashSet(); // Find all components on the scene and its children to get their script GUIDs. foreach (var root in scene.GetRootGameObjects()) { var components = root.GetComponentsInChildren(true); foreach (var component in components) { if (component == null) continue; var script = MonoScript.FromMonoBehaviour(component as MonoBehaviour); if (script == null) continue; var scriptAssetPath = AssetDatabase.GetAssetPath(script); if (string.IsNullOrEmpty(scriptAssetPath)) continue; var scriptGuid = AssetDatabase.AssetPathToGUID(scriptAssetPath); if (!string.IsNullOrEmpty(scriptGuid) && scriptAssetPath.StartsWith("Assets/")) { hash.Add(scriptGuid); } } } var assetDependencies = AssetDatabase.GetDependencies(assetPath); foreach (var dependency in assetDependencies) { if (string.IsNullOrEmpty(dependency) || !dependency.StartsWith("Assets/") || dependency == assetPath) { continue; } var dependencyGuid = AssetDatabase.AssetPathToGUID(dependency); if (!string.IsNullOrEmpty(dependencyGuid)) { hash.Add(dependencyGuid); } } sceneMetaData.DependencyGuids = hash.ToList(); return sceneMetaData; } } }