private static IEnumerable <string> CollectDependencies(string asset, ref DependencyCache cache)
        {
            // Try get dependencies from cache
            var assetGuid = AssetDatabase.AssetPathToGUID(asset);

            if (cache.cacheData.TryGetValue(assetGuid, out var cacheData))
            {
                if (ValidateDependency(ref cache, cacheData))
                {
                    return(cacheData.dependencies.Select(AssetDatabase.GUIDToAssetPath).ToList());
                }
            }
            return(AnalyzeDependencies(asset, ref cache));
        }
        private static void ResolveBundleDependencies(ref BundlesInfo bundlesInfo, ref DependencyCache cache)
        {
            var info      = bundlesInfo;
            var cacheData = cache;

            foreach (var kv in bundlesInfo)
            {
                //Debug.Log(string.Format("Resolving bundle dependency: {0}", kv.Key));

                var bundleInfo = kv.Value;
                bundleInfo.dependencies.Clear();

                void ResolveAssetDependency(string asset)
                {
                    var dependencies = CollectDependencies(asset, ref cacheData);

                    foreach (var dependencyAsset in dependencies)
                    {
                        // Which bundle contains this asset?
                        var depBundle = info.FirstOrDefault(
                            v => v.Value.assets.Contains(dependencyAsset)).Key;

                        if (string.IsNullOrEmpty(depBundle))
                        {
                            continue;
                        }

                        if (depBundle == kv.Key)
                        {
                            continue;
                        }

                        if (bundleInfo.dependencies.Contains(depBundle))
                        {
                            continue;
                        }
                        bundleInfo.dependencies.Add(depBundle);
                    }
                }

                foreach (var asset in bundleInfo.assets)
                {
                    ResolveAssetDependency(asset);
                }
            }
        }
 private NewDependencyCache()
 {
     m_cache = new DependencyCache.Builder { CacheDir = FileHelper.NewTmpDir("deps-cache") }.Build();
 }
        private static BundlesInfo ParseSharedBundleInfo(ref DependenciesInfo depsInfo,
                                                         ref ReservedSharedBundleInfo reserved, ref DependencyCache cache)
        {
            var sharedDict = new Dictionary <string, string>();

            var index = 0;

            // Determine shared bundles
            foreach (var kv in depsInfo)
            {
                var asset = kv.Key;

                // Ignore this asset when forced build in shared bundle.
                if (reserved.ContainsKey(asset))
                {
                    continue;
                }

                var depSet = kv.Value;

                // The count of dependencies no greater than 1 means that there are no other bundles
                // sharing this asset
                if (depSet.referenceInBundles.Count <= 1)
                {
                    continue;
                }

                // Otherwise, assets which depended by the same bundles will be separated to shared bundle.
                if (!sharedDict.ContainsKey(asset))
                {
                    sharedDict[asset] = string.Format(BundlerBuildSettings.kSharedBundleFormatter,
                                                      (++index).ToString());

                    // Sub-assets dependencies.
                    var deps = CollectDependencies(asset, ref cache);
                    foreach (var dep in deps)
                    {
                        if (reserved.ContainsKey(dep))
                        {
                            continue;
                        }
                        sharedDict[dep] = string.Format(BundlerBuildSettings.kSharedBundleFormatter,
                                                        (++index).ToString());
                    }
                }
            }

            // Collect shared bundles info
            var bundlesInfo = new BundlesInfo();

            foreach (var kv in sharedDict)
            {
                var name = sharedDict[kv.Key];
                if (bundlesInfo.ContainsKey(name))
                {
                    throw new BundleException("Shared bundle duplicated: " + name);
                }

                bundlesInfo[name] = new BundleInfo();
                bundlesInfo[name].assets.Add(kv.Key);
            }

            // Generate unique bundle name according to assets list.
            var sharedBundle = new BundlesInfo();

            foreach (var kv in bundlesInfo)
            {
                var assets = kv.Value.assets.ToList();
                assets.Sort((a, b) => string.Compare(a, b, StringComparison.Ordinal));

                var bundleName = string.Join("-", assets.ToArray());
                if (BundlerBuildSettings.kHashSharedBundle)
                {
                    using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(bundleName)))
                    {
                        var nameHash = CalculateMd5(stream);
                        bundleName = string.Format(BundlerBuildSettings.kSharedBundleFormatter, nameHash);
                    }
                }
                else
                {
                    bundleName = string.Format(BundlerBuildSettings.kSharedBundleFormatter, bundleName.ToLower());
                }

                var bundleInfo = new BundleInfo();
                foreach (var asset in kv.Value.assets)
                {
                    bundleInfo.assets.Add(asset);
                }
                sharedBundle.Add(bundleName, bundleInfo);
            }

            // Add reserved shared bundles info
            foreach (var kv in reserved)
            {
                var assetName = kv.Key;
                var bundle    = kv.Value;
                if (!sharedBundle.ContainsKey(bundle))
                {
                    sharedBundle.Add(bundle, new BundleInfo());
                }
                sharedBundle[bundle].assets.Add(assetName);
            }

            return(sharedBundle);
        }
        private static bool ValidateDependency(ref DependencyCache cache, DependencyCacheData cacheData)
        {
            if (cacheData.validated)
            {
                return(true);
            }

            var hash = AssetDatabase.GetAssetDependencyHash(cacheData.assetPath);

            if (cacheData.assetHash != hash)
            {
                Debug.LogFormat("Hash not match: {0}, previous: {1}, current: {2}",
                                cacheData.assetPath, cacheData.assetHash, hash);
                return(false);
            }

            var ret = true;

            foreach (var dependency in cacheData.dependencies)
            {
                var dependencyPath = AssetDatabase.GUIDToAssetPath(dependency);
                if (!File.Exists(PathUtility.RelativeProjectPathToAbsolutePath(dependencyPath)))
                {
                    Debug.LogFormat("Dependency file missing: {0}, {1}, hash validation not passed: {2}",
                                    dependency, dependencyPath, cacheData.assetPath);
                    return(false);
                }

                if (cache.cacheData.TryGetValue(dependency, out var data))
                {
                    ret &= ValidateDependency(ref cache, data);
                }
                else
                {
                    Debug.LogFormat("Dependency cache data missing: {0}, {1}, hash validation not passed: {2}",
                                    dependency, dependencyPath, cacheData.assetPath);
                    return(false);
                }
            }

            foreach (var scriptRef in cacheData.scriptsRefs)
            {
                var scriptPath = AssetDatabase.GUIDToAssetPath(scriptRef);
                if (!File.Exists(PathUtility.RelativeProjectPathToAbsolutePath(scriptPath)))
                {
                    Debug.LogFormat("Script file missing: {0}, {1}, hash validation not passed: {2}",
                                    scriptRef, scriptPath, cacheData.assetPath);
                    return(false);
                }

                if (cache.cacheData.TryGetValue(scriptRef, out var data))
                {
                    ret &= ValidateDependency(ref cache, data);
                }
                else
                {
                    Debug.LogFormat("Script cache data missing: {0}, {1}, hash validation not passed: {2}",
                                    scriptRef, scriptPath, cacheData.assetPath);
                    return(false);
                }
            }

            cacheData.validated = ret;
            return(ret);
        }
        private static IEnumerable <string> AnalyzeDependencies(string asset, ref DependencyCache cache)
        {
            if (++CollectedCount % 2000 == 0)
            {
                Debug.Log($"[{CollectedCount}]Unloading assets to reduce memory...");
                Resources.UnloadUnusedAssets();
                GC.Collect();
            }

            string[] dep1;
            // On Unity 2018 or newer, LightingDataAsset depends on SceneAsset directly,
            // which will lead to circular dependency
            var assetObj = AssetDatabase.LoadAssetAtPath <UnityEngine.Object>(asset);

            if (assetObj is LightingDataAsset lightingDataAsset)
            {
                dep1 = GetLightingDataValidDependencies(lightingDataAsset);
            }
            else
            {
                dep1 = AssetDatabase.GetDependencies(asset, true);
                dep1 = dep1.Where(v => !IsBuiltinResource(v))
                       .Where(v => IsProjectResource(v))
                       .ToArray();
            }

            var dep2 = dep1.Where(v => v != asset).ToArray();
            var dep3 = dep2.Distinct().ToList();

            var scriptRefs = dep3.Where(v => IsAssembly(v) || IsScript(v)).ToList();
            var assetDeps  = dep3.Where(v => !(IsAssembly(v) || IsScript(v))).ToList();

            var assetGuid = AssetDatabase.AssetPathToGUID(asset);

            cache.cacheData[assetGuid] = new DependencyCacheData {
                assetHash    = AssetDatabase.GetAssetDependencyHash(asset),
                dependencies = assetDeps.Select(AssetDatabase.AssetPathToGUID).ToList(),
                scriptsRefs  = scriptRefs.Select(AssetDatabase.AssetPathToGUID).ToList(),
                validated    = true,
            };

            foreach (var dep in assetDeps)
            {
                var guid = AssetDatabase.AssetPathToGUID(dep);
                if (!cache.cacheData.ContainsKey(guid) || !ValidateDependency(ref cache, cache.cacheData[guid]))
                {
                    AnalyzeDependencies(dep, ref cache);
                }
            }

            foreach (var dep in scriptRefs)
            {
                var guid = AssetDatabase.AssetPathToGUID(dep);
                if (!cache.cacheData.ContainsKey(guid) || !ValidateDependency(ref cache, cache.cacheData[guid]))
                {
                    AnalyzeDependencies(dep, ref cache);
                }
            }

            assetDeps.Sort();
            return(assetDeps);
        }
        private static BundlesInfo GenerateBundlesInfo(ref DependenciesInfo depsInfo,
                                                       ref ReservedSharedBundleInfo reserved, ref DependencyCache cache)
        {
            var sharedBundles     = ParseSharedBundleInfo(ref depsInfo, ref reserved, ref cache);
            var noneSharedBundles = ParseNoneSharedBundleInfo(ref depsInfo, ref sharedBundles);

            var bundles = new BundlesInfo();

            sharedBundles.Concat(noneSharedBundles).ToList().ForEach(kv => {
                if (kv.Value.assets.Count > 0)
                {
                    bundles.Add(kv.Key, kv.Value);
                }
            });
            return(bundles);
        }
        private static void AddDependenciesInfo(string bundleName, string relativePath, ref DependenciesInfo info,
                                                ref ReservedSharedBundleInfo reserved, ref DependencyCache cache)
        {
            var dependencies = CollectDependencies(relativePath, ref cache);
            var deps         = dependencies.ToList();

            deps.Add(relativePath);

            foreach (var dependency in deps)
            {
                if (!info.ContainsKey(dependency))
                {
                    info[dependency] = new DependencyInfo();
                }

                if (IsShader(dependency))
                {
                    if (BundlerBuildSettings.kSeparateShaderBundle)
                    {
                        info[dependency].referenceInBundles.Add(BundlerBuildSettings.kSeparatedShaderBundleName);
                        reserved[dependency] = BundlerBuildSettings.kSeparatedShaderBundleName;
                    }
                }
                info[dependency].referenceInBundles.Add(bundleName);
            }
        }
        private static void ParseBundleDependenciesByRuleOfPackBySubDirectory(BundleRule rule, ref DependenciesInfo depsInfo,
                                                                              ref ReservedSharedBundleInfo reserved, ref DependencyCache cache)
        {
            var excludePattern = rule.excludePattern;

            var searchPath     = PathUtility.RelativeProjectPathToAbsolutePath(rule.path);
            var subDirectories = Directory.GetDirectories(searchPath, "*.*", (SearchOption)rule.depth)
                                 .Where(v => !IsExclude(v, excludePattern))
                                 .ToArray();

            foreach (var subDirectory in subDirectories)
            {
                var files = Directory
                            .GetFiles(subDirectory, rule.searchPattern, SearchOption.AllDirectories)
                            .Where(v => !IsMeta(v))
                            .Where(v => !IsExclude(v, excludePattern))
                            .ToArray();

                var bundleName = PathUtility.NormalizeAssetBundlePath(subDirectory);
                bundleName = string.Format(BundlerBuildSettings.kBundleFormatter, bundleName);
                bundleName = BundlerBuildSettings.kHashAssetBundlePath
                    ? PathUtility.HashPath(bundleName)
                    : bundleName;

                try {
                    var index = 0;
                    foreach (var file in files)
                    {
                        EditorUtility.DisplayProgressBar("Parsing Rule of Pack By Sub Directory Name", file,
                                                         (float)index++ / files.Length);

                        var relativePath = PathUtility.AbsolutePathToRelativeProjectPath(file);
                        if (rule.shared)
                        {
                            TryAddToForceSharedBundle(relativePath, bundleName, ref reserved);
                        }

                        AddDependenciesInfo(bundleName, relativePath, ref depsInfo, ref reserved, ref cache);
                    }
                }
                finally {
                    EditorUtility.ClearProgressBar();
                }
            }
        }
        private static DependenciesInfo ParseBundleDependenciesFromRules(BundlerBuildRule buildRule,
                                                                         ref ReservedSharedBundleInfo reserved, ref DependencyCache cache)
        {
            buildRule.rules.Sort((a, b) => - a.shared.CompareTo(b.shared));

            var depsInfo = new DependenciesInfo();

            foreach (var rule in buildRule.rules)
            {
                var packType = (PackType)Enum.Parse(typeof(PackType), rule.packType);
                switch (packType)
                {
                case PackType.PackByFile:
                    ParseBundleDependenciesByRuleOfPackByFile(rule, ref depsInfo, ref reserved, ref cache);
                    break;

                case PackType.PackByDirectory:
                    ParseBundleDependenciesByRuleOfPackByDirectory(rule, ref depsInfo, ref reserved, ref cache);
                    break;

                case PackType.PackBySubDirectory:
                    ParseBundleDependenciesByRuleOfPackBySubDirectory(rule, ref depsInfo, ref reserved, ref cache);
                    break;

                default:
                    throw new ArgumentOutOfRangeException(rule.packType);
                }
            }

            return(depsInfo);
        }
        private static void SaveAssetDependenciesCache(DependencyCache cache)
        {
            var jsonData = JsonUtility.ToJson(cache);

            File.WriteAllText(DependenciesCacheFilePath, jsonData);
        }
 private string[] GetDependencyEntries(string currentDependencyDirectoryPath)
 {
     string          currentDependencyFilePath   = VirtualPath.Combine('/', currentDependencyDirectoryPath, ".dep");
     DependencyCache dependencyCacheEntry        = null;
     if (!dependencyCache.TryGetValue(currentDependencyFilePath, out dependencyCacheEntry) || dependencyCacheEntry.dependencyListLastModified < File.GetLastWriteTime(currentDependencyFilePath))
     {
         dependencyCacheEntry    =
         new DependencyCache()
         {
             dependencyListLastModified  = File.GetLastWriteTime(currentDependencyFilePath),
             dependencyList              = File.ReadAllLines(this.Context.Server.MapPath(currentDependencyFilePath))
         };
         VirtualFileAssembler.dependencyCache[currentDependencyFilePath] = dependencyCacheEntry;
     }
     return dependencyCacheEntry.dependencyList;
 }