/**
         * Execute Run operations using current graph
         */
        public void Perform(
            BuildTarget target,
            bool isRun,
            bool forceVisitAll,
            Action <NodeData, string, float> updateHandler)
        {
            LogUtility.Logger.Log(LogType.Log, (isRun) ? "---Build BEGIN---" : "---Setup BEGIN---");
            m_isBuilding = true;

            if (isRun)
            {
                AssetBundleBuildReport.ClearReports();
            }

            var saveData = SaveData.Data;

            foreach (var e in m_nodeExceptions)
            {
                var errorNode = saveData.Nodes.Find(n => n.Id == e.Id);
                // errorNode may not be found if user delete it on graph
                if (errorNode != null)
                {
                    LogUtility.Logger.LogFormat(LogType.Log, "[Perform] {0} is marked to revisit due to last error", errorNode.Name);
                    errorNode.NeedsRevisit = true;
                }
            }

            m_nodeExceptions.Clear();
            m_lastTarget = target;

            PerformGraph oldGraph = m_performGraph[gIndex];

            gIndex = (gIndex + 1) % 2;
            PerformGraph newGraph = m_performGraph[gIndex];

            newGraph.BuildGraphFromSaveData(target, oldGraph);

            PerformGraph.Perform performFunc =
                (NodeData data,
                 IEnumerable <PerformGraph.AssetGroups> incoming,
                 IEnumerable <ConnectionData> connectionsToOutput,
                 PerformGraph.Output outputFunc) =>
            {
                DoNodeOperation(target, data, incoming, connectionsToOutput, outputFunc, isRun, updateHandler);
            };

            newGraph.VisitAll(performFunc, forceVisitAll);

            if (isRun && m_nodeExceptions.Count == 0)
            {
                Postprocess();
            }

            m_isBuilding = false;
            LogUtility.Logger.Log(LogType.Log, (isRun) ? "---Build END---" : "---Setup END---");
        }