// -------------------------------------------------------------------------
        /// Deletes the generate code files.
        ///
        /// @param iStorage The VS storage to convert to code.
        ///
        public void DeleteGeneratedFilesFor(iCS_IStorage iStorage)
        {
            var fileName = NameUtility.ToTypeName(iStorage.TypeName);
            var folder   = CodeGenerationUtility.GetCodeGenerationFolder(iStorage);

            CSharpFileUtils.DeleteCSharpFile(folder, fileName);
        }
Esempio n. 2
0
        string GenerateHandleMessageMethodCode(string senderTypeName, IEnumerable <MethodInfo> methods)
        {
            var handleMessageCode = methods.Select(method =>
            {
                var parameters          = method.GetParameters();
                var readMessageDataCode = parameters.Select(param =>
                {
                    var postfix = TypeUtility.GetPacketReaderReadMethodPostfix(param.ParameterType);
                    return($"var {param.Name} = reader.Read{postfix}();");
                })
                                          .Join($"{Environment.NewLine}	");

                var paramNamesCode = CodeGenerationUtility.GenerateParameterNameCode(parameters);

                var code =
                    $@"void Handle{method.Name}({senderTypeName} sender, PacketReader reader)
{{
	{readMessageDataCode}
	
	Handle_{method.Name}(sender{paramNamesCode});
	on{method.Name}?.Invoke(sender{paramNamesCode});
}}";

                return(code);
            })
                                    .Join($"{Environment.NewLine}{Environment.NewLine}");

            return(handleMessageCode);
        }
Esempio n. 3
0
        string GenerateCompositedCode(Type messageGroupType, string senderTypeName, IEnumerable <MethodInfo> methods)
        {
            var usingCode           = TypeUtility.GetUnfriendlyParameterTypeUsingCode(methods, messageGroupType.Namespace, new[] { "Neti.Packets" });
            var messageHandlingCode = GenerateMessageHandlingCode(senderTypeName, methods);

            return(CodeGenerationUtility.BuildMessageGroupCode(usingCode,
                                                               messageGroupType.Namespace,
                                                               messageGroupType.Name.TrimStart('I'),
                                                               "abstract class MessageHandling",
                                                               messageHandlingCode));
        }
Esempio n. 4
0
        string GenerateAbstractMessageMethodCode(string senderTypeName, IEnumerable <MethodInfo> methods)
        {
            var code = methods.Select(method =>
            {
                var paramTypeNamesCode = CodeGenerationUtility.GenerateParameterTypeNameCode(method.GetParameters());

                return($@"protected abstract void Handle_{method.Name}({senderTypeName} sender{paramTypeNamesCode});");
            })
                       .JoinWithLine();

            return(code);
        }
Esempio n. 5
0
        string GenerateRpcCode(Type messageGroupType, IEnumerable <MethodInfo> rpcMethods, string senderTypeName)
        {
            var usingCode = TypeUtility.GetUnfriendlyParameterTypeUsingCode(rpcMethods, messageGroupType.Namespace);
            var rpcCode   = rpcMethods.Select(method => GenerateRpcMethodCode(method, senderTypeName))
                            .Join($"{Environment.NewLine}{Environment.NewLine}")
                            .InsertAfterEachLine(CodeConstants.InternalClassCodeIndent);

            return(CodeGenerationUtility.BuildMessageGroupCode(usingCode,
                                                               messageGroupType.Namespace,
                                                               messageGroupType.Name.TrimStart('I'),
                                                               "static class Rpc",
                                                               rpcCode));
        }
Esempio n. 6
0
        public StateCodeGenerator(ILanguageAbstraction generatorsFactory, string namespaceName, string stateMachineName, string stateName, bool useStateBase, bool isInternal, IGraph graph)
            : base(generatorsFactory, namespaceName, stateMachineName)
        {
            CodeGenerationUtility.CheckValidPartialIdentifierArgument(stateName, nameof(stateName));

            if (graph == null)
            {
                throw new ArgumentNullException(nameof(graph));
            }

            this.stateName    = stateName;
            this.graph        = graph;
            this.useStateBase = useStateBase;
            this.isInternal   = isInternal;
        }
Esempio n. 7
0
        string GenerateRpcMethodCode(MethodInfo method, string senderTypeName)
        {
            var parameters    = method.GetParameters();
            var parameterCode = CodeGenerationUtility.GenerateParameterTypeNameCode(parameters);
            var writeCode     = parameters.Select(param => $"{Environment.NewLine}		writer.Write({param.Name});").Join();

            return
                ($@"public static void {method.Name}({senderTypeName} sender{parameterCode})
{{
	using (var writer = sender.CreatePacketWriter())
	{{
		writer.Write(MessageId.{method.Name});{writeCode}
	}}
}}");
        }
Esempio n. 8
0
        public CodeGeneratorBase(ILanguageAbstraction languageAbstraction, string namespaceName, string stateMachineName)
        {
            if (languageAbstraction == null)
            {
                throw new ArgumentNullException(nameof(languageAbstraction));
            }
            if (string.IsNullOrWhiteSpace(namespaceName) == false)
            {
                this.namespaceName = namespaceName;
            }

            CodeGenerationUtility.CheckValidIdentifierArgument(stateMachineName, nameof(stateMachineName));

            Language = languageAbstraction;
            this.stateMachineName = stateMachineName;
        }
Esempio n. 9
0
        string GenerateHandlerDefinitionCode(string senderTypeName, IEnumerable <MethodInfo> methods)
        {
            var delegateCode = methods.Select(method =>
            {
                var paramCode = CodeGenerationUtility.GenerateParameterTypeNameCode(method.GetParameters());

                return($"public delegate void {method.Name}Handler({senderTypeName} sender{paramCode});");
            })
                               .JoinWithLine();
            var declaringCode = methods.Select(method => $"{method.Name}Handler on{method.Name};").JoinWithLine();
            var eventCode     = methods.Select(method => $"public event {method.Name}Handler On{method.Name} {{ add {{ on{method.Name} += value; }} remove {{ on{method.Name} -= value; }} }}").JoinWithLine();

            var code =
                $@"{delegateCode}

{declaringCode}

{eventCode}";

            return(code);
        }
        FileDefinition myCodeRoot = null;     ///< Code global definition.

        // -------------------------------------------------------------------------
        /// Builds global scope code definition.
        ///
        /// @param iStorage The VS storage to convert to code.
        /// @return The complete visual script code.
        ///
        public void GenerateCodeFor(iCS_IStorage iStorage)
        {
            // -- Nothing to do if no or empty Visual Script. --
            if (iStorage == null || iStorage.EditorObjects.Count == 0)
            {
                return;
            }

            // -- Build code global scope. --
            var typeName      = NameUtility.ToTypeName(iStorage.TypeName);
            var namespaceName = CodeGenerationUtility.GetNamespace(iStorage);
            var baseType      = CodeGenerationUtility.GetBaseType(iStorage);

            myCodeRoot = new FileDefinition(typeName, namespaceName, baseType, iStorage);

            // -- Generate code. --
            var result = myCodeRoot.GenerateCode(0);

            // -- Write final code to file. --
            var fileName = typeName;
            var folder   = CodeGenerationUtility.GetCodeGenerationFolder(iStorage);

            CSharpFileUtils.WriteCSharpFile(folder, fileName, result.ToString());
        }
Esempio n. 11
0
        public static void Init()
        {
            if (_init)
            {
                return;
            }

            LogUtils.Verbose("Context.Init");

            _init = true;

            foreach (var source in Sources)
            {
                _assets[source.GetAssetPath()] = source;
            }

            foreach (var storage in Storages)
            {
                _assets[storage.GetAssetPath()] = storage;
            }

            var assemblies = Assemblies;

            if (assemblies.ProxyAssembly == null)
            {
                return;
            }

            try
            {
                var roots            = Roots;
                var wellLocatedRoots = new List <RootDefinition>();
                foreach (var r in roots.All)
                {
                    if (!assemblies.IsAssemblyValidForRoot(r.Assembly))
                    {
                        LogUtils.Warning($"Config root {r.Root.FullName} is defined in assembly {r.Assembly.FullName}. This is not supported - please put it into separate assembly with AssemblyDefinition or manually.");
                        continue;
                    }

                    wellLocatedRoots.Add(r);
                }

                var validGroups     = new List <string>();
                var validAttributes = new List <AssetDeclarationAttributeBase>();

                var attributes = wellLocatedRoots.SelectMany(r => r.Attributes)
                                 .ToList();
                var groups = attributes.Select(a => a.Group)
                             .ToList();
                foreach (var attribute in attributes)
                {
                    if (groups.Count(g => g == attribute.Group) > 1)
                    {
                        var duplicateRoots = wellLocatedRoots.Where(r => r.Contains(attribute.Group));

                        var log = new StringBuilder();
                        log.AppendFormat("Group name \"{0}\" is declared multiple times. This is not supported.",
                                         attribute.Group)
                        .AppendLine();
                        log.AppendLine("Declarations found in these types:");
                        foreach (var root in duplicateRoots)
                        {
                            log.AppendFormat("{0} ({1})", root.Root.Name, root.Root.AssemblyQualifiedName)
                            .AppendLine();
                        }

                        log.AppendLine("These group will be ignored until duplication is fixed.");

                        LogUtils.Error(log);
                        continue;
                    }

                    if (!CodeGenerationUtility.IsValidGroupName(attribute.Group))
                    {
                        LogUtils.Error($"Group {attribute.Group} name is not valid! Group will be ignored.");
                        continue;
                    }

                    validAttributes.Add(attribute);
                    validGroups.Add(attribute.Group);
                }

                _groups    = validGroups;
                Attributes = validAttributes;
            }
            catch (Exception e)
            {
                LogUtils.Verbose(e);
            }

            try
            {
                var postprocessorType  = typeof(IPostProcessAssets);
                var postprocessorTypes = assemblies.All
                                         .SelectMany(a => a.GetTypes())
                                         .Where(t => !t.IsAbstract && !t.IsInterface)
                                         .Where(t => postprocessorType.IsAssignableFrom(t))
                                         .ToList();

                PostProcessors = new List <IPostProcessAssets>();
                var ordered      = new Dictionary <int, List <IPostProcessAssets> >();
                var orders       = new Dictionary <IPostProcessAssets, PostProcessOrderAttribute>();
                var defaultOrder = new PostProcessOrderAttribute();
                Func <int, List <IPostProcessAssets> > getGroup = i =>
                {
                    List <IPostProcessAssets> g;
                    if (ordered.TryGetValue(i, out g))
                    {
                        return(g);
                    }
                    g = new List <IPostProcessAssets>();

                    ordered[i] = g;

                    return(g);
                };
                Func <IPostProcessAssets, PostProcessOrderAttribute> getOrder = a =>
                {
                    PostProcessOrderAttribute o;
                    if (!orders.TryGetValue(a, out o) ||
                        o == null)
                    {
                        o = defaultOrder;
                    }

                    return(o);
                };
                foreach (var type in postprocessorTypes)
                {
                    try
                    {
                        var instance = Activator.CreateInstance(type, type.IsNotPublic) as IPostProcessAssets;
                        PostProcessors.Add(instance);

                        var order = type.GetSingle <PostProcessOrderAttribute>()
                                    ?? defaultOrder;

                        orders[instance] = order;

                        getGroup(order.Group).Add(instance);
                    }
                    catch (Exception e)
                    {
                        LogUtils.Verbose(e);
                    }
                }

                foreach (var p in ordered)
                {
                    p.Value.Sort((a1, a2) => getOrder(a1).Order.CompareTo(getOrder(a1).Order));
                }

                OrderedPostProcessors = new IPostProcessAssets[ordered.Count][];
                var index = 0;
                foreach (var groupIndex in ordered.Keys.OrderBy(k => k))
                {
                    var g = ordered[groupIndex];
                    if (g == null || g.Count == 0)
                    {
                        continue;
                    }
                    OrderedPostProcessors[index] = g.ToArray();
                    index++;
                }
            }
            catch (Exception e)
            {
                LogUtils.Verbose(e);
            }
        }
Esempio n. 12
0
        private static void BuildAssembly(YamlyProjectAssemblies assemblies, bool debug = false)
        {
            var outputAssetsPath = CodeGenerationUtility.GetProxyAssemblyOutputPath(assemblies.ProxyAssembly);

            if (Settings.VerboseLogs)
            {
                Debug.Log($"Build proxy assembly at path {outputAssetsPath}");
            }

            var assetPathsToReimport = new List <string>(2);

            var outputSystemPath = Application.dataPath.Replace("Assets", outputAssetsPath);
            var assemblyBuilder  = new ProxyAssemblyBuilder
            {
                TargetAssemblies        = assemblies.TargetAssemblies,
                IgnoreAssemblies        = assemblies.IgnoreAssemblies,
                OutputAssembly          = outputSystemPath,
                TreatWarningsAsErrors   = true,
                IncludeDebugInformation = debug
            }.Build();

            if (assemblyBuilder.CompilerResults.Errors.Count != 0)
            {
                Debug.LogError($"Proxy assembly builder have {assemblyBuilder.CompilerResults.Errors.Count} errors!");
                foreach (var error in assemblyBuilder.CompilerResults.Errors)
                {
                    Debug.LogError(error);
                }

                return;
            }

            if (Settings.VerboseLogs)
            {
                Debug.Log("Proxy assembly builder have no errors.");
            }

            assetPathsToReimport.Add(outputAssetsPath);

            if (assemblies.ProxyAssembly != null)
            {
                outputSystemPath = CodeGenerationUtility.GetUtilityAssemblySystemPath();
                outputAssetsPath = outputSystemPath.ToAssetsPath();

                if (Settings.VerboseLogs)
                {
                    Debug.Log($"Build utility assembly at path {outputAssetsPath}");
                }

                var groups         = _roots.SelectMany(r => r.Attributes).Select(a => a.Group).Distinct().ToArray();
                var utilityBuilder = new UtilityAssemblyBuilder(groups)
                {
                    OutputAssembly  = outputSystemPath,
                    TargetNamespace = "Yamly.UnityEditor.Utility"
                }.Build();

                if (utilityBuilder.CompilerResults.Errors.Count != 0)
                {
                    Debug.LogError($"Utility assembly builder have {utilityBuilder.CompilerResults.Errors.Count} errors!");
                    foreach (var error in utilityBuilder.CompilerResults.Errors)
                    {
                        Debug.LogError(error);
                    }

                    return;
                }

                if (Settings.VerboseLogs)
                {
                    Debug.Log("Utility assembly builder have no errors.");
                }

                assetPathsToReimport.Add(utilityBuilder.OutputAssembly.ToAssetsPath());
            }
            else
            {
                if (Settings.VerboseLogs)
                {
                    Debug.Log("Delay building utility assembly until proxy assembly imported.");
                }

                YamlyEditorPrefs.IsAssemblyBuildPending = true;
            }

            try
            {
                AssetDatabase.StartAssetEditing();
                foreach (var assetPath in assetPathsToReimport)
                {
                    AssetDatabase.ImportAsset(assetPath);
                }
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            AssetDatabase.StopAssetEditing();
            AssetDatabase.Refresh(ImportAssetOptions.Default);
        }
Esempio n. 13
0
        // Можем импортнуть файл кода или библиотеку, в котором есть конфиг. Надо пересобрать библиотеку.
        // Можем удалить файл кода или библиотеку, в котором есть конфиг. Надо пересобрать библиотеку.


        // Можем переместить импортнуть ассет, который попадает в источник. Надо пересобрать все машруты с этим ассетом.
        // Можем удалить ассет, который попадает в источник. Надо пересобрать все машруты с этим ассетом.
        // Можем переместить ассет, который попадает в источник. Надо проверить, что исходный и конечный источник тот же. Если тот же - игнорировать. Если изменился - надо пересобрать все маршруты.
        // Можем импортнуть новый источник. Надо пересобрать все его маршруты.
        // Можем удалить источник. Надо пересобрать все маршруты, которые он затрагивал.
        // Можем переместить источни. Надо пересобрать его маршруты.
        // Можем импортнуть новое хранилище. Надо пересобрать все его маршруты.

        // Можем переместить файл кода или библиотеку, в котором есть конфиг. Ничего не делать.
        // Можем удалить хранилище. Ничего не делать.
        // Можем переместить хранилище. Ничего не делать.
        private static void ProcessAssets(YamlyPostprocessAssetsContext ctx)
        {
            var assemblies = GetAssemblies();

            InitRoots(assemblies);

            if (!_rebuildAssemblyAfterReloadScriptsPending)
            {
                if (assemblies.ProxyAssembly == null ||
                    assemblies.IsProxyAssemblyInvalid ||
                    string.IsNullOrEmpty(assemblies.ProxyAssembly.Location))
                {
                    if (_roots.Any())
                    {
                        BuildAssembly(assemblies);
                    }
                    return;
                }
            }

            var codeAssets = AssetDatabase.FindAssets($"t:{nameof(TextAsset)}")
                             .Select(AssetDatabase.GUIDToAssetPath)
                             .Where(p => p.EndsWith(".cs"))
                             .Select(AssetDatabase.LoadAssetAtPath <TextAsset>)
                             .ToArray();

            var codeFilePaths = _roots.SelectMany(r => GetCodeFiles(r, codeAssets))
                                .Distinct()
                                .ToList();

            var exludedAssemblies = new List <string>
            {
                CodeGenerationUtility.GetUtilityAssemblySystemPath().ToAssetsPath()
            };

            var proxyAssemblyLocation = assemblies.ProxyAssembly?.Location.ToAssetsPath();

            if (!string.IsNullOrEmpty(proxyAssemblyLocation))
            {
                exludedAssemblies.Add(proxyAssemblyLocation);
            }

            var rebuildAssembly = false;

            foreach (var assetPath in ctx.ChangedAndDeletedAssets)
            {
                if (assetPath.EndsWith(".cs"))
                {
                    if (codeFilePaths.Contains(assetPath))
                    {
                        rebuildAssembly = true;
                    }
                }

                if (assetPath.EndsWith(".dll"))
                {
                    if (!exludedAssemblies.Contains(assetPath))
                    {
                        rebuildAssembly = true;
                    }
                }
            }

            if (rebuildAssembly ||
                _rebuildAssemblyAfterReloadScriptsPending)
            {
                if (!_rebuildAssemblyAfterReloadScriptsPending)
                {
                    YamlyEditorPrefs.IsAssemblyBuildPending = true;
                }
                YamlyEditorPrefs.AssetImportContext = new YamlyPostprocessAssetsContext
                {
                    ImportedAssets      = ctx.ImportedAssets.Where(p => !IsCodeFilePath(p)).ToArray(),
                    DeletedAssets       = ctx.DeletedAssets.Where(p => !IsCodeFilePath(p)).ToArray(),
                    MovedAssets         = ctx.MovedAssets.Where(p => !IsCodeFilePath(p)).ToArray(),
                    MovedFromAssetPaths = ctx.MovedFromAssetPaths.Where(p => !IsCodeFilePath(p)).ToArray()
                };
                return;
            }

            if (_roots.Count == 0)
            {
                return;
            }

            var sourceDefinitions = AssetUtility.LoadAll <FolderSource>()
                                    .Cast <SourceBase>()
                                    .Concat(AssetUtility.LoadAll <SingleSource>())
                                    .ToList();

            if (sourceDefinitions.Count == 0)
            {
                return;
            }

            var groups = _roots.SelectMany(r => r.Attributes)
                         .Select(a => a.Group)
                         .Distinct()
                         .ToArray();
            var storageDefinitions = AssetUtility.LoadAll <Storage>().ToList();

            for (var i = 0; i < storageDefinitions.Count; i++)
            {
                var storage = storageDefinitions[i];
                storage.ExcludedGroups.RemoveAll(g => !groups.Contains(g));
                foreach (var s in storage.Storages)
                {
                    if (s == null ||
                        !storage.Includes(s.Group))
                    {
                        Object.DestroyImmediate(s, true);

                        EditorUtility.SetDirty(storage);
                    }
                }

                storage.Storages.RemoveAll(s => s == null);

                var assets         = AssetDatabase.LoadAllAssetsAtPath(storage.GetAssetPath());
                var haveNullAssets = false;
                foreach (var asset in assets)
                {
                    if (asset == storage)
                    {
                        continue;
                    }

                    if (storage.Storages.Contains(asset))
                    {
                        continue;
                    }

                    if (asset == null)
                    {
                        haveNullAssets = true;
                    }

                    Object.DestroyImmediate(asset, true);
                }

                if (haveNullAssets)
                {
                    var assetPath   = storage.GetAssetPath();
                    var storages    = storage.Storages.ToArray();
                    var storageName = storage.name;
                    storage      = Object.Instantiate(storage);
                    storage.name = storageName;
                    storage.Storages.Clear();
                    foreach (var storageBase in storages)
                    {
                        var instance = Object.Instantiate(storageBase);
                        instance.name = storageBase.name;

                        storage.Storages.Add(instance);
                    }

                    if (Settings.VerboseLogs)
                    {
                        Debug.Log($"Storage at path ${assetPath} contains missing storage instances and will be overwritten.", storage);
                    }

                    AssetDatabase.DeleteAsset(assetPath);

                    AssetDatabase.StartAssetEditing();
                    {
                        AssetDatabase.CreateAsset(storage, assetPath);
                        foreach (var storageBase in storage.Storages)
                        {
                            AssetDatabase.AddObjectToAsset(storageBase, assetPath);
                        }
                    }
                    AssetDatabase.StopAssetEditing();

                    AssetDatabase.ImportAsset(assetPath);

                    storageDefinitions[i] = storage;
                }
            }

            var routes = new List <DataRoute>();

            foreach (var d in _roots)
            {
                var codePaths = GetCodeFiles(d, codeAssets);
                routes.AddRange(d.Attributes.Select(a => CreateRoute(d, a, sourceDefinitions, storageDefinitions, codePaths)));
            }

            var routesToRebuild = new List <DataRoute>();

            foreach (var assetPath in ctx.All)
            {
                foreach (var route in routes)
                {
                    if (routesToRebuild.Contains(route))
                    {
                        continue;
                    }

                    if (route.ContainsAsset(assetPath) ||
                        route.ContainsSource(assetPath) ||
                        route.ContainsStorage(assetPath))
                    {
                        routesToRebuild.Add(route);
                    }
                }
            }

            var context = new YamlyProjectContext
            {
                ProxyAssembly = assemblies.ProxyAssembly,
                Storages      = storageDefinitions,
                Sources       = sourceDefinitions,
                Roots         = _roots
            };

            foreach (var route in routesToRebuild)
            {
                Rebuild(route, context);
            }

            AssetDatabase.Refresh();
        }
Esempio n. 14
0
        private static void ProcessAssets(YamlyPostprocessAssetsContext ctx)
        {
            Context.Init();
            Context.ClearAssetsCache();

            var assemblies = Context.Assemblies;
            var roots      = Context.Roots;

            if (!_rebuildAssemblyAfterReloadScriptsPending)
            {
                if (assemblies.ProxyAssembly == null ||
                    assemblies.IsProxyAssemblyInvalid ||
                    string.IsNullOrEmpty(assemblies.ProxyAssembly.Location))
                {
                    if (roots.Any())
                    {
                        BuildAssembly(assemblies);
                    }
                    return;
                }
            }

            var exludedAssemblies = new List <string>
            {
                CodeGenerationUtility.GetUtilityAssemblySystemPath().ToAssetsPath()
            };

            var proxyAssemblyLocation = assemblies.ProxyAssembly?.Location.ToAssetsPath();

            if (!string.IsNullOrEmpty(proxyAssemblyLocation))
            {
                exludedAssemblies.Add(proxyAssemblyLocation);
            }

            var rebuildAssembly = false;

            foreach (var assetPath in ctx.ChangedAndDeletedAssets)
            {
                if (roots.Any(r => IsCodeFile(r, assetPath)))
                {
                    rebuildAssembly = true;
                    break;
                }
            }

            if (rebuildAssembly ||
                _rebuildAssemblyAfterReloadScriptsPending)
            {
                if (!_rebuildAssemblyAfterReloadScriptsPending)
                {
                    YamlyEditorPrefs.IsAssemblyBuildPending = true;
                }
                YamlyEditorPrefs.AssetImportContext = new YamlyPostprocessAssetsContext
                {
                    ImportedAssets      = ctx.ImportedAssets.Where(p => !IsCodeFilePath(p)).ToArray(),
                    DeletedAssets       = ctx.DeletedAssets.Where(p => !IsCodeFilePath(p)).ToArray(),
                    MovedAssets         = ctx.MovedAssets.Where(p => !IsCodeFilePath(p)).ToArray(),
                    MovedFromAssetPaths = ctx.MovedFromAssetPaths.Where(p => !IsCodeFilePath(p)).ToArray()
                };
                return;
            }

            if (roots.Count == 0)
            {
                LogUtils.Verbose("Stop processing: no roots found.");
                return;
            }

            var sources = Context.Sources;

            if (sources.Count == 0)
            {
                LogUtils.Verbose("Stop processing: no sources found.");
                return;
            }

            var groups   = Context.Groups;
            var storages = Context.Storages;

            CleanupStorages(storages, groups);

            var routes = new List <DataRoute>();

            foreach (var d in roots)
            {
                routes.AddRange(d.ValidAttributes.Select(a => CreateRoute(d, a, sources, storages)));
            }

            var routesToRebuild = new List <DataRoute>();

            foreach (var assetPath in ctx.All)
            {
                foreach (var route in routes)
                {
                    if (routesToRebuild.Contains(route))
                    {
                        continue;
                    }

                    if (route.ContainsAsset(assetPath) ||
                        route.ContainsSource(assetPath) ||
                        route.ContainsStorage(assetPath))
                    {
                        routesToRebuild.Add(route);
                    }
                }
            }

            foreach (var route in routesToRebuild)
            {
                Rebuild(route);
            }

            foreach (var route in routesToRebuild)
            {
                PostProcess(route);
            }

            AssetDatabase.Refresh();
        }