[latexila] Create the BuildJobRunner class



commit c86b3f13b52fe57ff30104125e2e9210ca7d2860
Author: SÃbastien Wilmet <swilmet src gnome org>
Date:   Wed Jul 11 00:40:34 2012 +0200

    Create the BuildJobRunner class
    
    To summarize a little, there is the BottomPanel containing the
    BuildView. When a build tool is executed, a BuildToolRunner is created,
    which fill the build view. A BuildToolRunner creates, for each build
    job, a BuildJobRunner. In turn, a BuildJobRunner creates a
    BuildCommandRunner, or (in the future), call gtk_show_uri(), and creates
    a PostProcessor with the output of the command.

 src/build_job_runner.vala  |  183 +++++++++++++++++++++++++++
 src/build_tool_runner.vala |  297 +++++++++++++------------------------------
 src/main_window.vala       |    2 +-
 3 files changed, 274 insertions(+), 208 deletions(-)
---
diff --git a/src/build_job_runner.vala b/src/build_job_runner.vala
new file mode 100644
index 0000000..d8cc264
--- /dev/null
+++ b/src/build_job_runner.vala
@@ -0,0 +1,183 @@
+/*
+ * This file is part of LaTeXila.
+ *
+ * Copyright  2012 SÃbastien Wilmet
+ *
+ * LaTeXila is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LaTeXila is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LaTeXila.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: SÃbastien Wilmet
+ */
+
+public class BuildJobRunner : GLib.Object
+{
+    private BuildJob _build_job;
+    private File _on_file;
+
+    private BuildCommandRunner? _command_runner = null;
+    private PostProcessor? _post_processor = null;
+
+    public signal void finished (bool success);
+
+    public BuildJobRunner (BuildJob build_job, File on_file)
+    {
+        _build_job = build_job;
+        _on_file = on_file;
+    }
+
+    public string get_command_line () throws ShellError
+    {
+        string[] command_args = get_command_args (true);
+        return string.joinv (" ", command_args);
+    }
+
+    public string get_command_name ()
+    {
+        try
+        {
+            string[] command_args = get_command_args (true);
+
+            if (command_args.length == 0)
+                return "";
+
+            return command_args[0];
+        }
+        catch (ShellError e)
+        {
+            return "";
+        }
+    }
+
+    public void run () throws ShellError, Error
+    {
+        string[] command_args = get_command_args ();
+        string working_directory = _on_file.get_parent ().get_parse_name ();
+
+        _command_runner = new BuildCommandRunner (command_args, working_directory);
+
+        _command_runner.finished.connect ((exit_status) =>
+        {
+            create_post_processor ();
+            _post_processor.set_status (exit_status);
+            _post_processor.process (_on_file, _command_runner.get_output ());
+
+            finished (exit_status == 0);
+
+            _command_runner = null;
+        });
+
+        if (_build_job.post_processor == PostProcessorType.NO_OUTPUT)
+            _command_runner.execute_without_output ();
+        else
+            _command_runner.execute_with_output ();
+    }
+
+    public Node<BuildMsg?> get_messages ()
+    {
+        if (_post_processor == null)
+        {
+            warning ("Build job runner: try to get messages too early");
+
+            return new Node<BuildMsg?> (BuildMsg ());
+        }
+
+        return _post_processor.get_messages ();
+    }
+
+    public void abort ()
+    {
+        if (_command_runner != null)
+            _command_runner.abort ();
+    }
+
+    private string[] get_command_args (bool for_printing = false) throws ShellError
+    {
+        /* Separate arguments */
+        string[] args = {};
+        Shell.parse_argv (_build_job.command, out args);
+
+        /* Re-add quotes if needed */
+        if (for_printing)
+        {
+            string[] new_args = {};
+            foreach (string arg in args)
+            {
+                if (arg.contains (" "))
+                    new_args += "\"" + arg + "\"";
+                else
+                    new_args += arg;
+            }
+
+            args = new_args;
+        }
+
+        /* Replace placeholders */
+        string base_filename = _on_file.get_basename ();
+        string base_shortname = Utils.get_shortname (base_filename);
+
+        string[] new_args = {};
+        foreach (string arg in args)
+        {
+            if (arg.contains ("$view"))
+                // TODO use gtk_show_uri() instead of xdg-open
+                new_args += arg.replace ("$view", "xdg-open");
+
+            else if (arg.contains ("$filename"))
+                new_args += arg.replace ("$filename", base_filename);
+
+            else if (arg.contains ("$shortname"))
+                new_args += arg.replace ("$shortname", base_shortname);
+
+            else
+                new_args += arg;
+        }
+
+        return new_args;
+    }
+
+    private void create_post_processor ()
+    {
+        switch (_build_job.post_processor)
+        {
+            case PostProcessorType.ALL_OUTPUT:
+                _post_processor = new AllOutputPostProcessor ();
+                break;
+
+            case PostProcessorType.LATEX:
+                _post_processor = new LatexPostProcessor ();
+                break;
+
+            case PostProcessorType.LATEXMK:
+                GLib.Settings settings =
+                    new GLib.Settings ("org.gnome.latexila.preferences.latex");
+
+                bool show_all = settings.get_boolean ("latexmk-always-show-all");
+
+                _post_processor = new LatexmkPostProcessor (show_all);
+                break;
+
+            case PostProcessorType.NO_OUTPUT:
+                _post_processor = new NoOutputPostProcessor ();
+                break;
+
+            case PostProcessorType.RUBBER:
+                _post_processor = new RubberPostProcessor ();
+                break;
+
+            default:
+                warning ("Unknown post processor. Use no-output.");
+                _post_processor = new NoOutputPostProcessor ();
+                break;
+        }
+    }
+}
diff --git a/src/build_tool_runner.vala b/src/build_tool_runner.vala
index bbb14e3..c81f34f 100644
--- a/src/build_tool_runner.vala
+++ b/src/build_tool_runner.vala
@@ -21,292 +21,175 @@ using Gtk;
 
 public class BuildToolRunner : GLib.Object
 {
-    private BuildCommandRunner? _command_runner = null;
+    private BuildTool _tool;
+    private File _on_file;
+    private BuildView _view;
+    private Gtk.Action _action_stop_exec;
 
-    private BuildView view;
-    private bool latexmk_show_all;
-    private Gtk.Action action_stop_exec;
+    private TreeIter _main_title;
 
-    private File file;
-    private string filename;
-    private string shortname;
-    private string directory;
+    private int _job_num;
+    private BuildJob _current_job;
+    private TreeIter _current_job_title;
+    private BuildJobRunner? _current_job_runner = null;
 
-    private unowned Gee.ArrayList<BuildJob?> jobs;
-    private int job_num = 0;
-    private BuildJob current_job;
-
-    private TreeIter main_title;
-    private TreeIter[] job_titles;
+    private bool _aborted = false;
 
     public signal void finished ();
 
-    public BuildToolRunner (File file, BuildTool tool, BuildView view,
+    public BuildToolRunner (BuildTool build_tool, File on_file, BuildView build_view,
         Gtk.Action action_stop_exec)
     {
-        this.file = file;
-        this.action_stop_exec = action_stop_exec;
-
-        filename = file.get_parse_name ();
-        shortname = Utils.get_shortname (filename);
-        directory = file.get_parent ().get_parse_name ();
-
-        GLib.Settings settings =
-            new GLib.Settings ("org.gnome.latexila.preferences.latex");
-        latexmk_show_all = settings.get_boolean ("latexmk-always-show-all");
+        _tool = build_tool;
+        _on_file = on_file;
+        _view = build_view;
+        _action_stop_exec = action_stop_exec;
 
-        // verify if file extension is allowed for the build tool
-        string[] extensions = tool.extensions.split (" ");
-        if (0 < tool.extensions.length
-            && ! (Utils.get_extension (filename) in extensions))
+        if (! match_allowed_extensions ())
         {
-            warning ("Bad file extension");
+            warning ("Build tool runner: bad file extension");
             return;
         }
 
-        jobs = tool.jobs;
-        this.view = view;
-        view.clear ();
-        main_title = view.add_main_title (tool.label, BuildState.RUNNING);
+        _view.clear ();
+        _action_stop_exec.set_sensitive (true);
 
-        if (! add_job_titles ())
-            return;
+        _main_title = _view.add_main_title (_tool.label, BuildState.RUNNING);
 
-        action_stop_exec.set_sensitive (true);
+        _job_num = 0;
         proceed ();
     }
 
-    // Returns true on success, false otherwise.
-    private bool add_job_titles ()
-    {
-        job_num = 0;
-
-        foreach (BuildJob job in jobs)
-        {
-            string[] command;
-
-            try
-            {
-                command = get_command_args (job.command, true);
-            }
-            catch (ShellError e)
-            {
-                TreeIter job_title =
-                    view.add_job_title (job.command, BuildState.FAILED);
-
-                BuildMsg message = BuildMsg ();
-                message.text = "Failed to parse command line:";
-                message.type = BuildMsgType.ERROR;
-                view.append_single_message (job_title, message);
-
-                message.text = e.message;
-                message.type = BuildMsgType.INFO;
-                view.append_single_message (job_title, message);
-
-                failed ();
-                return false;
-            }
-
-            string job_title = string.joinv (" ", command);
-            job_titles += view.add_job_title (job_title, BuildState.RUNNING);
-
-            job_num++;
-        }
-
-        job_num = 0;
-        return true;
-    }
-
-    /* Abort the running process */
     public void abort ()
     {
-        if (_command_runner != null)
-            _command_runner.abort ();
+        if (_current_job_runner != null)
+            _current_job_runner.abort ();
+
+        _action_stop_exec.set_sensitive (false);
+        _view.set_title_state (_main_title, BuildState.ABORTED);
+        _view.set_title_state (_current_job_title, BuildState.ABORTED);
 
-        action_stop_exec.set_sensitive (false);
-        view.set_title_state (main_title, BuildState.ABORTED);
-        for (int i = job_num ; i < job_titles.length ; i++)
-            view.set_title_state (job_titles[i], BuildState.ABORTED);
+        _aborted = true;
     }
 
-    private void on_command_finished (int exit_status)
+    private bool match_allowed_extensions ()
     {
-        return_if_fail (_command_runner != null);
+        string[] allowed_extensions = _tool.extensions.split (" ");
 
-        // Create post processor
-        PostProcessor post_processor;
-        switch (current_job.post_processor)
-        {
-            case PostProcessorType.ALL_OUTPUT:
-                post_processor = new AllOutputPostProcessor ();
-                break;
-            case PostProcessorType.LATEX:
-                post_processor = new LatexPostProcessor ();
-                break;
-            case PostProcessorType.LATEXMK:
-                post_processor = new LatexmkPostProcessor (latexmk_show_all);
-                break;
-            case PostProcessorType.NO_OUTPUT:
-                post_processor = new NoOutputPostProcessor ();
-                break;
-            case PostProcessorType.RUBBER:
-                post_processor = new RubberPostProcessor ();
-                break;
-            default:
-                warning ("Unknown post processor. Use no-output.");
-                post_processor = new NoOutputPostProcessor ();
-                break;
-        }
+        if (allowed_extensions.length == 0)
+            return true;
 
-        post_processor.set_status (exit_status);
-        post_processor.process (file, _command_runner.get_output ());
+        string filename = _on_file.get_parse_name ();
+        string extension = Utils.get_extension (filename);
 
-        view.append_messages (job_titles[job_num], post_processor.get_messages ());
-
-        if (post_processor.successful)
-        {
-            view.set_title_state (job_titles[job_num], BuildState.SUCCEEDED);
-            job_num++;
-            proceed ();
-        }
-        else
-        {
-            view.set_title_state (job_titles[job_num], BuildState.FAILED);
-            failed ();
-        }
+        return extension in allowed_extensions;
     }
 
     private void proceed ()
     {
-        // all jobs executed, finished
-        if (job_num >= jobs.size)
+        // All jobs executed, finished.
+        if (_tool.jobs.size <= _job_num)
         {
-            view.set_title_state (main_title, BuildState.SUCCEEDED);
-            action_stop_exec.set_sensitive (false);
+            _view.set_title_state (_main_title, BuildState.SUCCEEDED);
+            _action_stop_exec.set_sensitive (false);
             finished ();
             return;
         }
 
-        current_job = jobs[job_num];
-        string[] command;
+        _current_job = _tool.jobs[_job_num];
+        run_current_job ();
+    }
 
-        try
-        {
-            command = get_command_args (current_job.command);
-        }
-        catch (ShellError e)
+    private void run_current_job ()
+    {
+        _current_job_runner = new BuildJobRunner (_current_job, _on_file);
+
+        if (! add_job_title ())
         {
-            // This should never append, since the command has already been parsed for
-            // printing purpose.
-            critical ("Separate command arguments worked the first timeâ");
             failed ();
             return;
         }
 
-        // Attention, rubber doesn't support filenames with spaces, warn the user
-        if (current_job.post_processor == PostProcessorType.RUBBER
-            && filename.contains (" "))
+        _current_job_runner.finished.connect ((success) =>
         {
-            BuildMsg message = BuildMsg ();
-            message.text =
-                _("Rubber may not support filenames with spaces (even in a directory)");
-            message.type = BuildMsgType.WARNING;
-            message.filename = filename;
+            _view.append_messages (_current_job_title,
+                _current_job_runner.get_messages ());
 
-            view.append_single_message (job_titles[job_num], message);
-        }
+            if (_aborted)
+                return;
+
+            BuildState state = success ? BuildState.SUCCEEDED : BuildState.FAILED;
+            _view.set_title_state (_current_job_title, state);
 
-        _command_runner = new BuildCommandRunner (command, directory);
+            _job_num++;
+            proceed ();
+        });
 
         try
         {
-            if (current_job.post_processor == PostProcessorType.NO_OUTPUT)
-                _command_runner.execute_without_output ();
-            else
-                _command_runner.execute_with_output ();
+            _current_job_runner.run ();
+        }
+        catch (ShellError e)
+        {
+            // This error is already catched in add_job_title() normally.
+            critical ("Failed to parse command line a second time: %s", e.message);
+            failed ();
         }
         catch (Error e)
         {
-            view.set_title_state (job_titles[job_num], BuildState.FAILED);
-
             BuildMsg error_msg = BuildMsg ();
             error_msg.text = e.message;
             error_msg.type = BuildMsgType.ERROR;
-            view.append_single_message (job_titles[job_num], error_msg);
+            _view.append_single_message (_current_job_title, error_msg);
 
             // If the command doesn't seem to be installed, display a more understandable
             // message.
             if (e is SpawnError.NOENT)
             {
+                string command_name = _current_job_runner.get_command_name ();
+
                 BuildMsg info_msg = BuildMsg ();
                 info_msg.text =
-                    _("%s doesn't seem to be installed.").printf (command[0]);
-                view.append_single_message (job_titles[job_num], info_msg);
+                    _("%s doesn't seem to be installed.").printf (command_name);
+
+                _view.append_single_message (_current_job_title, info_msg);
             }
 
             failed ();
         }
-
-        _command_runner.finished.connect (on_command_finished);
     }
 
-    private string[] get_command_args (string command_line, bool for_printing = false)
-        throws ShellError
+    // Returns true on success.
+    private bool add_job_title ()
     {
-        /* separate arguments */
-        string[] command = {};
-
         try
         {
-            Shell.parse_argv (command_line, out command);
+            string command_line = _current_job_runner.get_command_line ();
+            _current_job_title = _view.add_job_title (command_line, BuildState.RUNNING);
+            return true;
         }
-        catch (ShellError e)
+        catch (ShellError error)
         {
-            warning ("Separate command arguments: %s", e.message);
-            throw e;
-        }
+            _current_job_title =
+                _view.add_job_title (_current_job.command, BuildState.FAILED);
 
-        /* re-add quotes if needed */
-        if (for_printing)
-        {
-            for (int cmd_num = 0 ; cmd_num < command.length ; cmd_num++)
-            {
-                string cur_cmd = command[cmd_num];
-                if (cur_cmd.contains (" "))
-                    command[cmd_num] = "\"" + cur_cmd + "\"";
-            }
-        }
+            BuildMsg message = BuildMsg ();
+            message.text = "Failed to parse command line:";
+            message.type = BuildMsgType.ERROR;
+            _view.append_single_message (_current_job_title, message);
 
-        /* replace placeholders */
-        string base_filename = file.get_basename ();
-        string base_shortname = Utils.get_shortname (base_filename);
+            message.text = error.message;
+            message.type = BuildMsgType.INFO;
+            _view.append_single_message (_current_job_title, message);
 
-        for (int i = 0 ; i < command.length ; i++)
-        {
-            if (command[i].contains ("$view"))
-            {
-                // TODO use gtk_show_uri() instead of xdg-open
-                command[i] = command[i].replace ("$view", "xdg-open");
-            }
-            else if (command[i].contains ("$filename"))
-            {
-                command[i] = command[i].replace ("$filename", base_filename);
-            }
-            else if (command[i].contains ("$shortname"))
-            {
-                command[i] = command[i].replace ("$shortname", base_shortname);
-            }
+            return false;
         }
-
-        return command;
     }
 
     private void failed ()
     {
-        view.set_title_state (main_title, BuildState.FAILED);
-        for (int i = job_num + 1 ; i < job_titles.length ; i++)
-            view.set_title_state (job_titles[i], BuildState.ABORTED);
-
-        action_stop_exec.set_sensitive (false);
+        _view.set_title_state (_main_title, BuildState.FAILED);
+        _view.set_title_state (_current_job_title, BuildState.FAILED);
+        _action_stop_exec.set_sensitive (false);
     }
 }
diff --git a/src/main_window.vala b/src/main_window.vala
index 5ac397e..990278c 100644
--- a/src/main_window.vala
+++ b/src/main_window.vala
@@ -1249,7 +1249,7 @@ public class MainWindow : Window
         }
 
         File main_file = active_document.get_main_file ();
-        build_tool_runner = new BuildToolRunner (main_file, tool, _build_view,
+        build_tool_runner = new BuildToolRunner (tool, main_file, _build_view,
             action_stop_exec);
 
         // refresh file browser when compilation is finished



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]