[gnome-builder/wip/tingping/meson] meson: Initial plugin



commit 62d535ede83d156c4bb19f34ff64e36d8ffef9ec
Author: Patrick Griffis <tingping tingping se>
Date:   Wed Aug 17 16:42:48 2016 -0400

    meson: Initial plugin

 configure.ac                           |    2 +
 libide/buildsystem/ide-builder.c       |    4 +
 plugins/meson/Makefile.am              |   14 ++
 plugins/meson/configure.ac             |   12 ++
 plugins/meson/meson.plugin             |   11 ++
 plugins/meson/meson_plugin/__init__.py |  262 ++++++++++++++++++++++++++++++++
 6 files changed, 305 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d757d24..75539f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -300,6 +300,7 @@ m4_include([plugins/html-completion/configure.ac])
 m4_include([plugins/html-preview/configure.ac])
 m4_include([plugins/jedi/configure.ac])
 m4_include([plugins/jhbuild/configure.ac])
+m4_include([plugins/meson/configure.ac])
 m4_include([plugins/mingw/configure.ac])
 m4_include([plugins/project-tree/configure.ac])
 m4_include([plugins/python-gi-imports-completion/configure.ac])
@@ -587,6 +588,7 @@ echo "  GNOME Code Assistance ................ : ${enable_gnome_code_assistance_
 echo "  HTML Autocompletion .................. : ${enable_html_completion_plugin}"
 echo "  HTML and Markdown Preview ............ : ${enable_html_preview_plugin}"
 echo "  JHBuild .............................. : ${enable_jhbuild_plugin}"
+echo "  Meson ................................ : ${enable_meson_plugin}"
 echo "  MinGW ................................ : ${enable_mingw_plugin}"
 echo "  Project Creation ..................... : ${enable_create_project_plugin}"
 echo "  Project Tree ......................... : ${enable_project_tree_plugin}"
diff --git a/libide/buildsystem/ide-builder.c b/libide/buildsystem/ide-builder.c
index 165d58e..1829ec1 100644
--- a/libide/buildsystem/ide-builder.c
+++ b/libide/buildsystem/ide-builder.c
@@ -222,6 +222,10 @@ ide_builder_init (IdeBuilder *self)
 {
 }
 
+/**
+ * ide_builder_install_async:
+ * @result: (out) (nullable)
+ */
 void
 ide_builder_install_async (IdeBuilder           *self,
                            IdeBuildResult      **result,
diff --git a/plugins/meson/Makefile.am b/plugins/meson/Makefile.am
new file mode 100644
index 0000000..a38b3ac
--- /dev/null
+++ b/plugins/meson/Makefile.am
@@ -0,0 +1,14 @@
+if ENABLE_MESON_PLUGIN
+
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+dist_plugin_DATA = meson.plugin
+
+moduledir = $(libdir)/gnome-builder/plugins/meson_plugin
+dist_module_DATA = meson_plugin/__init__.py
+
+endif
+
+-include $(top_srcdir)/git.mk
+
diff --git a/plugins/meson/configure.ac b/plugins/meson/configure.ac
new file mode 100644
index 0000000..6950ae2
--- /dev/null
+++ b/plugins/meson/configure.ac
@@ -0,0 +1,12 @@
+# --enable-meson-plugin=yes/no
+AC_ARG_ENABLE([meson-plugin],
+              [AS_HELP_STRING([--enable-meson-plugin=@<:@yes/no@:>@],
+                              [Build with support for the Meson build system])],
+              [enable_meson_plugin=$enableval],
+              [enable_meson_plugin=yes])
+
+# for if ENABLE_MESON_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_MESON_PLUGIN, test x$enable_python_scripting = xyes && test x$enable_meson_plugin = 
xyes)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/meson/Makefile])
diff --git a/plugins/meson/meson.plugin b/plugins/meson/meson.plugin
new file mode 100644
index 0000000..081a4b9
--- /dev/null
+++ b/plugins/meson/meson.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Module=meson_plugin
+Loader=python3
+Name=Meson
+Description=Provides integration with the Meson build system
+Authors=Patrick Griffis <tingping tingping se>
+Copyright=Copyright © 2016 Patrick Griffis
+Builtin=true
+Hidden=true
+X-Project-File-Filter-Pattern=meson.build
+X-Project-File-Filter-Name=Meson Project (meson.build)
diff --git a/plugins/meson/meson_plugin/__init__.py b/plugins/meson/meson_plugin/__init__.py
new file mode 100644
index 0000000..69bb594
--- /dev/null
+++ b/plugins/meson/meson_plugin/__init__.py
@@ -0,0 +1,262 @@
+# __init__.py
+#
+# Copyright (C) 2016 Patrick Griffis <tingping tingping se>
+#
+# 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/>.
+
+from os import path
+import threading
+import json
+import gi
+
+gi.require_version('Ide', '1.0')
+
+from gi.repository import (
+    GLib,
+    GObject,
+    Gio,
+    Ide
+)
+# TODO: Make *everything* here async!
+
+
+class MesonBuildSystem(Ide.Object, Ide.BuildSystem, Gio.AsyncInitable):
+    project_file = GObject.Property(type=Gio.File)
+
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+
+    def do_init_async(self, priority, cancel, callback, data=None):
+        task = Gio.Task.new(self, cancel, callback)
+        task.set_priority(priority)
+
+        project_file = self.get_context().get_project_file()
+        if project_file.get_basename() == 'meson.build':
+            task.return_boolean(True)
+        else:
+            child = project_file.get_child('meson.build')
+            exists = child.query_exists(cancel)
+            if exists:
+                self.project_file = child
+            task.return_boolean(exists)
+
+    def do_init_finish(self, result):
+        return result.propagate_boolean()
+
+    def do_get_priority(self):
+        return -200 # Lower priority than Autotools for now
+
+    def do_get_builder(self, config):
+        return MesonBuilder(context=self.get_context(), configuration=config)
+
+    def do_get_build_flags_async(self, file, cancellable, callback):
+        task = Gio.Task.new(self, cancellable, callback)
+        # TODO
+        task.build_flags = []
+        task.return_boolean(True)
+
+    def do_get_build_flags_finish(self, result):
+        print('Returning flags:', result.build_flags)
+        return result.build_flags
+
+    def do_get_build_targets_async(self, cancellable, callback, data=None):
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_targets = None
+
+        # FIXME: API cleanup for this?
+        config = Ide.Configuration.new(self.get_context(), 'meson-bootstrap', 'local', 'host')
+        builder = self.get_builder(config)
+
+        import subprocess
+        try:
+            ret = subprocess.check_output(['mesonintrospect', '--targets', 
builder._get_build_dir().get_path()])
+        except subprocess.CalledProcessError:
+            task.return_error(GLib.Error('Failed to run mesonintrospect'))
+            return
+
+        #launcher = Ide.SubprocessLauncher.new(Gio.SubprocessFlags.NONE)
+        #launcher.push_args(['mesonintrospect', '--targets', builder._get_build_dir().get_path()])
+        #proc = launcher.spawn_sync(cancellable)
+        #try:
+        #   ret, stdout, stderr = proc.communicate_utf8(None, cancellable)
+        #    print(ret, stdout, stderr)
+        #    proc.wait(cancellable)
+        #except GLib.Error as e:
+        #    task.build_targets = None
+        #    task.return_error(e)
+        #    return
+
+        targets = []
+        try:
+            meson_targets = json.loads(ret.decode('utf-8'))
+        except json.JSONDecodeError:
+            task.return_error(GLib.Error('Failed to decode meson json'))
+            return
+
+        for t in meson_targets:
+            name = t['filename']
+            if isinstance(name, list):
+                name = name[0]
+
+            install_dir = 'FIXME'
+            if t['type'] == 'executable':
+                # FIXME: hardcoded, upstream api addition needed
+                install_dir = path.join(config.get_prefix(), 'bin')
+
+            ide_target = MesonBuildTarget(install_dir, name=name)
+            if t['type'] == 'executable':
+                # Sorted by bin first
+                targets.insert(0, ide_target)
+            else:
+                targets.append(ide_target)
+
+        task.build_targets = targets
+        task.return_boolean(True)
+
+    def do_get_build_targets_finish(self, result):
+        if result.build_targets is None:
+            raise result.propagate_error()
+        print('Returning targets:', result.build_targets)
+        return result.build_targets
+
+
+class MesonBuilder(Ide.Builder):
+    configuration = GObject.Property(type=Ide.Configuration)
+
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+
+    def _get_build_dir(self) -> Gio.File:
+        context = self.get_context()
+
+        # This matches the Autotools layout
+        project_id = context.get_project().get_id()
+        buildroot = context.get_root_build_dir()
+        device = self.props.configuration.get_device()
+        device_id = device.get_id()
+        system_type = device.get_system_type()
+
+        return Gio.File.new_for_path(path.join(buildroot, project_id, device_id, system_type))
+
+    def _get_source_dir(self) -> Gio.File:
+        context = self.get_context()
+        return context.get_vcs().get_working_directory()
+
+    def do_build_async(self, flags, cancellable, callback, data=None):
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_result = MesonBuildResult()
+        task.build_result.set_mode('Building...')
+
+        builddir = self._get_build_dir()
+        # FIXME: 'rebuild' cleans.. not bootstraps?
+        clean = flags & Ide.BuilderBuildFlags.FORCE_CLEAN and not flags & 
Ide.BuilderBuildFlags.FORCE_BOOTSTRAP
+
+        if self.props.configuration.get_dirty() or flags & Ide.BuilderBuildFlags.FORCE_BOOTSTRAP:
+            import shutil
+            print('deleting builddir')
+            try:
+                shutil.rmtree(builddir.get_path())
+            except FileNotFoundError:
+                pass
+            self.props.configuration.set_dirty(False)
+
+        if not builddir.query_exists():
+            try:
+                builddir.make_directory_with_parents(cancellable)
+            except GLib.Error as e:
+                task.return_error(e)
+                return
+
+        if not builddir.get_child('build.ninja').query_exists() and not clean:
+            sourcedir = self._get_source_dir()
+
+            launcher = Ide.SubprocessLauncher.new(Gio.SubprocessFlags.NONE)
+            launcher.set_cwd(builddir.get_path())
+
+            extra_opts = self.props.configuration.get_config_opts().split()
+            extra_opts.append('--prefix=' + self.props.configuration.get_prefix())
+            launcher.push_args(['meson', sourcedir.get_path()] + extra_opts)
+
+            subproc = launcher.spawn_sync()
+            print("running meson")
+            subproc.wait()
+
+        launcher = Ide.SubprocessLauncher.new(Gio.SubprocessFlags.NONE)
+        launcher.set_cwd(builddir.get_path())
+        launcher.push_args(['ninja'])
+        if clean:
+            launcher.push_args(['clean'])
+        subproc = launcher.spawn_sync()
+        try:
+            ret = subproc.wait_check()
+            task.build_result.set_mode('Building Sucessful')
+        except GLib.Error as e:
+            task.build_result.set_mode('Building Failed')
+            task.return_error(e)
+            return
+
+    def do_build_finish(self, result) -> Ide.BuildResult:
+        #if result.build_result is None:
+        #    raise result.propagate_error() # FIXME: Doesn't work?
+        return result.build_result
+
+    def do_install_async(self, cancellable, callback, data=None):
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_result = None
+
+        builddir = self._get_build_dir()
+        launcher = Ide.SubprocessLauncher.new(Gio.SubprocessFlags.NONE)
+        launcher.set_cwd(builddir.get_path())
+        launcher.push_args(['ninja', 'install'])
+        proc = launcher.spawn_sync()
+        proc.wait()
+
+        task.build_result = MesonBuildResult()
+        task.return_boolean(True)
+        return MesonBuildResult()
+
+    def do_install_finish(self, result) -> Ide.BuildResult:
+        if not result.build_result:
+            raise result.propagate_error()
+        return result.build_result
+
+
+class MesonBuildResult(Ide.BuildResult):
+
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+
+
+class MesonBuildTarget(Ide.Object, Ide.BuildTarget):
+    # FIXME: These should be part of the BuildTarget interface
+    name = GObject.Property(type=str)
+    install_directory = GObject.Property(type=Gio.File)
+
+    def __init__(self, install_dir, **kwargs):
+        super().__init__(**kwargs)
+        self.props.install_directory = Gio.File.new_for_path(install_dir)
+
+    def do_get_install_directory(self):
+        return self.props.install_directory
+
+
+#class MesonMiner(Ide.ProjectMiner):
+#    def __init__(**kwargs):
+#        super().__init__(**kwargs)
+#
+#    def do_mine_async(self, cancel, callback):
+#        pass
+#
+#    def do_mine_finish(self, result):
+#        pass


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