def run_command(self, **args): # Initialize a settings wrapper (because we have so many!) self._settings = AdvancedBuilderSettings(self.window, args) self._exec = OutputWindowController() self._exec.init(self, self._settings.active_task(), jump_to_error = self._settings.jump_to_error()) self._current_phase = None self._quiet = self._settings.quiet() self._exec.quiet = self._quiet self._stop = False # Get the phases self._phases = [] for phase_config in self._settings.build_phases(): phase = self._get_phase_object(phase_config) printcons("Evaluating phase ", phase) if(phase is None) or (not phase.is_valid()): self._exec.write("Invalid config for phase: %s ([%s]), aborting" % (phase_config.get("name"), str(phase))) return self._phases.append(phase) # Run it this way, to not run into concurrency issues. sublime.set_timeout(self._run_tasks, 200)
class AdvancedBuilderCommand(sublime_plugin.WindowCommand): """ The sublime plugin command class. """ PHASE_SOLUTION = "solution" PHASE_SOLUTION = "unity_project" PHASE_COPY = "copy" PHASE_STYLECOP = "stylecop" PHASE_COMMAND = "command" RUN_ASYNC = True def run(self, **args): """ Run the command. This is called by sublime, when a build is executed. """ kill = args.get("kill") if(kill is not None) and (kill): # Kill the currently running process and get out if(hasattr(self, "_exec")) and (self._exec is not None): printcons("Killing current task") self.cancel_command() else: printcons("No task to kill") return if(hasattr(self, "_exec")) and (self._exec is not None): # There is a command already running, ask if it should be stopped stop = sublime.ok_cancel_dialog("There is already a command being processed, do you want to cancel it?", "Stop") if(stop): # Stop the old process and continue to use the new one self.cancel_command() else: # Get out, there should only be one build running return self.run_command(**args) def cancel_command(self): self._exec.kill() self._stop = True if(not self._quiet): self._exec.write("Killed: %s [%s]" % (self._current_phase, self._settings.active_configuration())) self._cleanup_command() def run_command(self, **args): # Initialize a settings wrapper (because we have so many!) self._settings = AdvancedBuilderSettings(self.window, args) self._exec = OutputWindowController() self._exec.init(self, self._settings.active_task(), jump_to_error = self._settings.jump_to_error()) self._current_phase = None self._quiet = self._settings.quiet() self._exec.quiet = self._quiet self._stop = False # Get the phases self._phases = [] for phase_config in self._settings.build_phases(): phase = self._get_phase_object(phase_config) printcons("Evaluating phase ", phase) if(phase is None) or (not phase.is_valid()): self._exec.write("Invalid config for phase: %s ([%s]), aborting" % (phase_config.get("name"), str(phase))) return self._phases.append(phase) # Run it this way, to not run into concurrency issues. sublime.set_timeout(self._run_tasks, 200) def _cleanup_command(self): self._exec = None self._settings = None self._phases = None self._current_phase = None def _run_tasks(self): """ Run all tasks sequentially. """ if(self._stop): # Now it gets funky: If a process was killed, the new one # will spawn a new _run_tasks method. That would mean we # have two. But because this method does not hold a context # by itself, we don't care, which of the two is stopped, it # will be the first one to come in here. Care must be taken # however on when to clean up the _exec context, which needs # to happen when _stop is set to True, so as to not screw up # the context of the new process. self._stop = False return if(self._exec.is_running()): # Still waiting for the process to finish, wait another 0.1s sublime.set_timeout(self._run_tasks, 100) return if(len(self._phases) < 1): # We are done self._exec.done() self._cleanup_command() return; if(self._exec.has_errors) and (self._current_phase is not None) and (self._current_phase.stop_on_error): # Don't start the next one, this one broke. if(not self._quiet): self._exec.write("%s [%s] has errors, stopping" % (self._current_phase, self._settings.active_configuration())) self._exec.done() self._cleanup_command() return # The last phase finished successfully, start a new one. started = False while(not started): if(len(self._phases) < 1): self._exec.done() self._cleanup_command() return; self._current_phase = self._phases[0] self._phases = self._phases[1:] # Start the next phase and poll for its completion started = self._run_new_phase(self._current_phase) # Wait for 0.2s before checking again sublime.set_timeout(self._run_tasks, 200) def _run_new_phase(self, phase): if(not (self._settings.build_all() or phase.should_run())): # A task, that doesn't apply to the configuration. if(not self._quiet): self._exec.write("Skipped: %s [%s]" % (phase, self._settings.active_configuration())) return False task = phase.get_task() if(task is None): # The task had errors on building its run configuration, get out! return False; self._exec.write("%s [%s]" % (phase, self._settings.active_configuration())) self._exec.run(**task) return self._exec.is_running() def _get_phase_object(self, phase_config): """ Get a BuildPhase object for the type of the build phase. @returns The appropriet, initialized phase for the configured type or None, if none exists. """ # Resolve the type of the phase phase_type = phase_config.get("type") if(phase_type is None): return None # Resolve the class of the type phase_class = supported_build_phases.get(phase_type) if(phase_class is None): return None phase = phase_class() phase.init(self._settings, **phase_config) return phase