[gnome-builder] cargo: add minimal Cargo build system support



commit 0c1e239f293a5ddd1df1def231e2e8c749eebd17
Author: Christian Hergert <chergert redhat com>
Date:   Fri Oct 28 14:15:30 2016 -0700

    cargo: add minimal Cargo build system support
    
    This doesn't yet handle running, so we still need to figure out how to
    extract enough information to use our own runners instead of cargo run.

 configure.ac                  |    2 +
 plugins/Makefile.am           |    1 +
 plugins/cargo/Makefile.am     |   12 +++
 plugins/cargo/cargo.plugin    |   11 ++
 plugins/cargo/cargo_plugin.py |  212 +++++++++++++++++++++++++++++++++++++++++
 plugins/cargo/configure.ac    |   12 +++
 6 files changed, 250 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 88bcae1..61b6bbf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -283,6 +283,7 @@ m4_include([plugins/autotools/configure.ac])
 m4_include([plugins/autotools-templates/configure.ac])
 m4_include([plugins/build-tools/configure.ac])
 m4_include([plugins/c-pack/configure.ac])
+m4_include([plugins/cargo/configure.ac])
 m4_include([plugins/clang/configure.ac])
 m4_include([plugins/color-picker/configure.ac])
 m4_include([plugins/command-bar/configure.ac])
@@ -593,6 +594,7 @@ echo ""
 echo "  Autotools ............................ : ${enable_autotools_plugin}"
 echo "  Build Tools .......................... : ${enable_build_tools_plugin}"
 echo "  C Language Pack ...................... : ${enable_c_pack_plugin}"
+echo "  Cargo ................................ : ${enable_cargo_plugin}"
 echo "  Clang ................................ : ${enable_clang_plugin}"
 echo "  Color picker.......................... : ${enable_color_picker_plugin}"
 echo "  Command Bar .......................... : ${enable_command_bar_plugin}"
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 4f633ab..ee1f04f 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = \
        autotools-templates \
        build-tools \
        c-pack \
+       cargo \
        clang \
        color-picker \
        command-bar \
diff --git a/plugins/cargo/Makefile.am b/plugins/cargo/Makefile.am
new file mode 100644
index 0000000..1c801a4
--- /dev/null
+++ b/plugins/cargo/Makefile.am
@@ -0,0 +1,12 @@
+if ENABLE_CARGO_PLUGIN
+
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+dist_plugin_DATA = \
+       cargo.plugin \
+       cargo_plugin.py
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/cargo/cargo.plugin b/plugins/cargo/cargo.plugin
new file mode 100644
index 0000000..218ddb0
--- /dev/null
+++ b/plugins/cargo/cargo.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Module=cargo_plugin
+Name=Cargo
+Loader=python3
+Description=Provides integration with the Cargo build system
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2016 Christian Hergert
+Builtin=true
+Hidden=true
+X-Project-File-Filter-Pattern=Cargo.toml
+X-Project-File-Filter-Name=Cargo (Cargo.toml)
diff --git a/plugins/cargo/cargo_plugin.py b/plugins/cargo/cargo_plugin.py
new file mode 100644
index 0000000..d8579f7
--- /dev/null
+++ b/plugins/cargo/cargo_plugin.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python3
+
+#
+# cargo_plugin.py
+#
+# Copyright (C) 2016 Christian Hergert <chris dronelabs com>
+#
+# This program 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.
+#
+# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import gi
+import threading
+
+gi.require_version('Ide', '1.0')
+
+from gi.repository import Gio
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Ide
+
+_ = Ide.gettext
+
+class CargoBuildSystem(Ide.Object, Ide.BuildSystem, Gio.AsyncInitable):
+    project_file = GObject.Property(type=Gio.File)
+
+    def do_init_async(self, io_priority, cancellable, callback, data):
+        task = Gio.Task.new(self, cancellable, callback)
+
+        # This is all done synchronously, doing it in a thread would probably
+        # be somewhat ideal although unnecessary at this time.
+
+        try:
+            # Maybe this is a Cargo.toml
+            if self.props.project_file.get_basename() in ('Cargo.toml',):
+                task.return_boolean(True)
+                return
+
+            # Maybe this is a directory with a Cargo.toml
+            if self.props.project_file.query_file_type() == Gio.FileType.DIRECTORY:
+                child = self.props.project_file.get_child('Cargo.toml')
+                if child.query_exists(None):
+                    self.props.project_file = child
+                    task.return_boolean(True)
+                    return
+        except Exception as ex:
+            task.return_error(ex)
+
+        raise NotImplemented
+
+    def do_init_finish(self, task):
+        return task.propagate_boolean()
+
+    def do_get_priority(self):
+        # Priority is used to determine the order of discovery
+        return 2000
+
+    def do_get_build_flags_async(self, ifile, cancellable, callback, data):
+        # GTask sort of is painful from Python.
+        # We can use it to attach some data to return from the finish
+        # function though.
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_flags = []
+        task.return_boolean(True)
+
+    def do_get_build_flags_finish(self, result):
+        if task.propagate_boolean():
+            return result.build_flags
+
+    def do_get_builder(self, config):
+        return CargoBuilder(config, context=self.get_context())
+
+    def do_get_build_targets_async(self, cancellable, callback, data):
+        # TODO: We need a way to figure out what "cargo run" will do so that
+        #       we can synthesize that as a build result.
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_targets = []
+        task.return_boolean(True)
+
+    def do_get_build_targets_finish(self, task):
+        if task.propagate_boolean():
+            return task.build_targets
+
+class CargoBuilder(Ide.Builder):
+    config = GObject.Property(type=Ide.Configuration)
+
+    def __init__(self, config, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.config = config
+
+    def do_build_async(self, flags, cancellable, callback, data):
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_result = CargoBuildResult(self.config, flags, context=self.get_context())
+
+        def wrap_execute():
+            try:
+                task.build_result.build()
+                task.build_result.set_mode(_('Successful'))
+                task.build_result.set_failed(False)
+                task.build_result.set_running(False)
+                task.return_boolean(True)
+            except Exception as ex:
+                task.build_result.set_mode(_("Failed"))
+                task.build_result.set_failed(True)
+                task.build_result.set_running(False)
+                task.return_error(ex)
+
+        thread = threading.Thread(target=wrap_execute)
+        thread.start()
+
+        return task.build_result
+
+    def do_build_finish(self, task):
+        if task.propagate_boolean():
+            return task.build_result
+
+    def do_install_async(self, cancellable, callback, data):
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_result = CargoBuildResult(self.config, 0, context=self.get_context())
+
+        def wrap_execute():
+            try:
+                task.build_result.install()
+                self = task.get_source_object()
+                task.build_result.set_mode(_('Successful'))
+                task.build_result.set_failed(False)
+                task.build_result.set_running(False)
+                task.return_boolean(True)
+            except Exception as ex:
+                task.build_result.set_mode(_("Failed"))
+                task.build_result.set_failed(True)
+                task.build_result.set_running(False)
+                task.return_error(ex)
+
+        thread = threading.Thread(target=wrap_execute)
+        thread.start()
+
+        return task.build_result
+
+    def do_install_finish(self, task):
+        if task.propagate_boolean():
+            return task.build_result
+
+class CargoBuildResult(Ide.BuildResult):
+    runtime = GObject.Property(type=Ide.Runtime)
+
+    def __init__(self, config, flags, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        context = self.get_context()
+        vcs = context.get_vcs()
+
+        self.flags = flags
+        self.runtime = config.get_runtime()
+        self.directory = vcs.get_working_directory().get_path()
+
+        self.set_running(True)
+
+    def _run(self, mode, *args):
+        self.set_mode(mode)
+
+        if self.runtime is not None:
+            launcher = self.runtime.create_launcher()
+        else:
+            launcher = Ide.SubprocessLauncher()
+            launcher.set_run_on_host(True)
+            launcher.set_clear_environment(False)
+
+        launcher.set_cwd(self.directory)
+        launcher.push_argv('cargo')
+        for arg in args:
+            launcher.push_argv(arg)
+
+        subprocess = launcher.spawn_sync()
+
+        self.log_subprocess(subprocess)
+
+        subprocess.wait_check()
+
+        return True
+
+    def build(self):
+        # Do a clean first if requested.
+        if self.flags & Ide.BuilderBuildFlags.FORCE_CLEAN != 0:
+            self._run(_("Cleaning…"), 'clean')
+
+        if self.flags & Ide.BuilderBuildFlags.NO_BUILD == 0:
+            self._run(_("Building…"), 'build', '--verbose')
+
+        self.set_mode(_('Successful'))
+        self.set_failed(False)
+        self.set_running(False)
+
+        return True
+
+    def install(self):
+        self.build()
+        self._run(_('Installing…'), 'install')
+
+        self.set_mode(_('Successful'))
+        self.set_failed(False)
+        self.set_running(False)
diff --git a/plugins/cargo/configure.ac b/plugins/cargo/configure.ac
new file mode 100644
index 0000000..6f3b90f
--- /dev/null
+++ b/plugins/cargo/configure.ac
@@ -0,0 +1,12 @@
+# --enable-cargo-plugin=yes/no
+AC_ARG_ENABLE([cargo-plugin],
+              [AS_HELP_STRING([--enable-cargo-plugin=@<:@yes/no@:>@],
+                              [Build with support for Cargo integration.])],
+              [enable_cargo_plugin=$enableval],
+              [enable_cargo_plugin=yes])
+
+# for if ENABLE_CARGO_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_CARGO_PLUGIN, test x$enable_cargo_plugin = xyes)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/cargo/Makefile])


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