Unverified Commit 0f9b14c4 authored by Loïc Blot's avatar Loïc Blot 💬
Browse files

Add golang plugin

* Handle *.go files
* Runs `go build` in the project root
* Needs GOROOT & GOPATH to be set in project env variables
* Display go version in SDK section
* Add project-tree build & rebuild enable support with callback functions
parent f5592c7d
Pipeline #52997 passed with stages
in 27 minutes and 38 seconds
......@@ -41,6 +41,7 @@ option('plugin_git', type: 'boolean')
option('plugin_gjs_symbols', type: 'boolean')
option('plugin_glade', type: 'boolean')
option('plugin_gnome_code_assistance', type: 'boolean')
option('plugin_golang', type: 'boolean')
option('plugin_go_langserv', type: 'boolean')
option('plugin_gradle', type: 'boolean')
option('plugin_grep', type: 'boolean')
......
......@@ -235,3 +235,4 @@ static void
gbp_flatpak_build_system_discovery_init (GbpFlatpakBuildSystemDiscovery *self)
{
}
/* ide-golang--build-system-discovery.c
*
* Copyright 2019 Loïc BLOT <loic.blot@unix-experience.fr>
*
* 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/>.
*/
#define G_LOG_DOMAIN "ide-golang-build-system-discovery"
#include "gbp-golang-build-system-discovery.h"
#define DISCOVERY_MAX_DEPTH 3
struct _GbpGolangBuildSystemDiscovery
{
GObject parent_instance;
};
static gchar *
gbp_golang_build_system_discovery_discover (IdeBuildSystemDiscovery *discovery,
GFile *project_file,
GCancellable *cancellable,
gint *priority,
GError **error)
{
g_autoptr(GFileEnumerator) enumerator = NULL;
gpointer infoptr;
g_autoptr(GPtrArray) manifests = NULL;
IDE_ENTRY;
g_assert (GBP_IS_GOLANG_BUILD_SYSTEM_DISCOVERY (discovery));
g_assert (G_IS_FILE (project_file));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
g_assert (priority != NULL);
manifests = g_ptr_array_new_with_free_func (g_object_unref);
enumerator = g_file_enumerate_children (project_file,
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK","
G_FILE_ATTRIBUTE_STANDARD_NAME","
G_FILE_ATTRIBUTE_STANDARD_TYPE,
G_FILE_QUERY_INFO_NONE,
cancellable,
NULL);
if (enumerator == NULL)
IDE_RETURN (NULL);
while (NULL != (infoptr = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
{
g_autoptr(GFileInfo) info = infoptr;
GFileType file_type;
const gchar *name;
if (g_file_info_get_is_symlink (info))
continue;
if (NULL == (name = g_file_info_get_name (info)))
continue;
file_type = g_file_info_get_file_type (info);
if (file_type != G_FILE_TYPE_REGULAR)
continue;
if (g_strcmp0 (name, "go.sum") != 0)
continue;
IDE_TRACE_MSG ("Discovered buildsystem of type \"golang\"");
IDE_RETURN (g_strdup ("golang"));
}
IDE_RETURN (NULL);
}
static void
build_system_discovery_iface_init (IdeBuildSystemDiscoveryInterface *iface)
{
iface->discover = gbp_golang_build_system_discovery_discover;
}
G_DEFINE_TYPE_WITH_CODE (GbpGolangBuildSystemDiscovery,
gbp_golang_build_system_discovery,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_SYSTEM_DISCOVERY, build_system_discovery_iface_init))
static void
gbp_golang_build_system_discovery_class_init (GbpGolangBuildSystemDiscoveryClass *klass)
{
}
static void
gbp_golang_build_system_discovery_init (GbpGolangBuildSystemDiscovery *self)
{
}
/* ide-golang--build-system-discovery.h
*
* Copyright 2019 Loïc BLOT <loic.blot@unix-experience.fr>
*
* 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/>.
*/
#pragma once
#include <libide-foundry.h>
G_BEGIN_DECLS
#define TYPE_GOLANG_BUILD_SYSTEM_DISCOVERY (gbp_golang_build_system_discovery_get_type())
G_DECLARE_FINAL_TYPE (GbpGolangBuildSystemDiscovery, gbp_golang_build_system_discovery, GBP, GOLANG_BUILD_SYSTEM_DISCOVERY, GObject)
G_END_DECLS
/* golang-plugin.c
*
* Copyright 2018 Loïc BLOT <loic.blot@unix-experience.fr>
*
* 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/>.
*/
#include <libpeas/peas.h>
#include <libide-foundry.h>
#include <libide-editor.h>
#include "ide-golang-build-system.h"
#include "ide-golang-application-addin.h"
#include "ide-golang-pipeline-addin.h"
#include "ide-golang-preferences-addin.h"
void
ide_golang_register_types (PeasObjectModule *module)
{
peas_object_module_register_extension_type (module, IDE_TYPE_PIPELINE_ADDIN, IDE_TYPE_GOLANG_PIPELINE_ADDIN);
peas_object_module_register_extension_type (module, IDE_TYPE_BUILD_SYSTEM, IDE_TYPE_GOLANG_BUILD_SYSTEM);
peas_object_module_register_extension_type (module, IDE_TYPE_APPLICATION_ADDIN, IDE_TYPE_GOLANG_APPLICATION_ADDIN);
peas_object_module_register_extension_type (module, IDE_TYPE_PREFERENCES_ADDIN, IDE_TYPE_GOLANG_PREFERENCES_ADDIN);
}
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/plugins/golang">
<file>golang.plugin</file>
</gresource>
</gresources>
[Plugin]
Module=golang_plugin
Name=Golang
Description=Provides integration with the Golang toolchain
Authors=Loïc BLOT <loic.blot@unix-experience.fr>
Copyright=Copyright © 2018 Loïc BLOT
Builtin=true
Embedded=ide_golang_register_types
X-Project-File-Filter-Pattern=go.mod,*.go
X-Project-File-Filter-Name=Golang project
X-Builder-ABI=@PACKAGE_ABI@
X-Symbol-Resolver-Languages=go
\ No newline at end of file
/* ide-golang-application-addin.h
*
* Copyright 2018 Loïc BLOT <loic.blot@unix-experience.fr>
*
* 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/>.
*/
#include <glib/gi18n.h>
#include <libide-foundry.h>
#include <libide-editor.h>
#include "ide-golang-application-addin.h"
struct _IdeGolangApplicationAddin
{
GObject parent_instance;
gchar *golang_version;
GRegex *go_version_regex;
};
static void application_addin_iface_init (IdeApplicationAddinInterface *iface);
static IdeGolangApplicationAddin *golang_application_addin = NULL;
static const gchar *goversion_pattern = "^go version (.*)\n?$";
G_DEFINE_TYPE_EXTENDED (IdeGolangApplicationAddin,
ide_golang_application_addin,
G_TYPE_OBJECT,
0,
G_IMPLEMENT_INTERFACE (IDE_TYPE_APPLICATION_ADDIN,
application_addin_iface_init))
static void
ide_golang_application_addin_class_init (IdeGolangApplicationAddinClass *klass)
{
}
static void
ide_golang_application_addin_init (IdeGolangApplicationAddin *addin)
{
g_autoptr(GError) error = NULL;
if (!(addin->go_version_regex = g_regex_new (goversion_pattern, G_REGEX_MULTILINE, 0, &error)))
{
g_assert(error != NULL);
g_error("Unable to create regex when parsing golang version: %s", error->message);
IDE_EXIT;
}
addin->golang_version = g_strdup("unknown");
}
static void
ide_golang_application_addin_load (IdeApplicationAddin *addin,
IdeApplication *application)
{
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
g_autoptr(IdeSubprocess) subprocess = NULL;
g_autofree gchar *stdoutstr = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
IdeGolangApplicationAddin *self = (IdeGolangApplicationAddin *)addin;
golang_application_addin = self;
IDE_ENTRY;
g_assert (IDE_IS_APPLICATION_ADDIN (addin));
g_assert (IDE_IS_APPLICATION (application));
launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
ide_subprocess_launcher_push_argv (launcher, "go");
ide_subprocess_launcher_push_argv (launcher, "version");
ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error);
if (subprocess == NULL)
{
g_assert (error != NULL);
g_error ("%s", error->message);
IDE_EXIT;
}
if (!ide_subprocess_communicate_utf8 (subprocess, NULL, NULL, &stdoutstr, NULL, &error))
{
g_assert(error != NULL);
g_error("Unable to communicate with subprocess while fetching golang version: %s", error->message);
IDE_EXIT;
}
if (!ide_subprocess_wait (subprocess, NULL, &error))
{
g_assert(error != NULL);
g_error("Unable to wait when communication with go version: %s", error->message);
IDE_EXIT;
}
// Search for golang version and return immediately when found.
g_regex_match (self->go_version_regex, stdoutstr, 0, &match_info);
while (g_match_info_matches (match_info))
{
gint begin = 0;
gint end = 0;
if (g_match_info_fetch_pos (match_info, 1, &begin, &end) && begin >= 0 && end > begin)
{
g_free (self->golang_version);
self->golang_version = g_strndup(&stdoutstr[begin], end - begin);
g_debug("Found golang version: %s", self->golang_version);
IDE_EXIT;
}
g_match_info_next (match_info, &error);
}
IDE_EXIT;
}
static void
ide_golang_application_addin_unload (IdeApplicationAddin *addin,
IdeApplication *application)
{
IdeGolangApplicationAddin *self = (IdeGolangApplicationAddin *)addin;
g_assert (IDE_IS_APPLICATION_ADDIN (addin));
g_assert (IDE_IS_APPLICATION (application));
g_free(self->go_version_regex);
}
static void
application_addin_iface_init (IdeApplicationAddinInterface *iface)
{
iface->load = ide_golang_application_addin_load;
iface->unload = ide_golang_application_addin_unload;
}
const gchar *golang_get_go_version(void)
{
return golang_application_addin->golang_version;
}
/* ide-golang-application-addin.h
*
* Copyright 2018 Loïc BLOT <loic.blot@unix-experience.fr>
*
* 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/>.
*/
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
#define IDE_TYPE_GOLANG_APPLICATION_ADDIN (ide_golang_application_addin_get_type())
G_DECLARE_FINAL_TYPE (IdeGolangApplicationAddin, ide_golang_application_addin, IDE, GOLANG_APPLICATION_ADDIN, GObject)
G_END_DECLS
const gchar *golang_get_go_version (void);
/* ide-golang-build-system.c
*
* Copyright 2018 Loïc BLOT <loic.blot@unix-experience.fr>
*
* 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/>.
*/
#define G_LOG_DOMAIN "ide-golang-build-system"
#include "config.h"
#include <gio/gio.h>
#include <string.h>
#include "ide-golang-build-system.h"
struct _IdeGolangBuildSystem
{
IdeObject parent_instance;
gchar *goroot;
gchar *gopath;
};
static void async_initable_iface_init (GAsyncInitableIface *iface);
static void build_system_iface_init (IdeBuildSystemInterface *iface);
G_DEFINE_TYPE_WITH_CODE (IdeGolangBuildSystem,
ide_golang_build_system,
IDE_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_SYSTEM, build_system_iface_init))
enum {
PROP_0,
PROP_PROJECT_FILE,
PROP_GOROOT,
PROP_GOPATH,
N_PROPS,
};
static GParamSpec *properties [N_PROPS];
static void
ide_golang_build_system_constructed (GObject *object)
{
IdeGolangBuildSystem *self = (IdeGolangBuildSystem *)object;
IdeBufferManager *buffer_manager;
IdeContext *context;
G_OBJECT_CLASS (ide_golang_build_system_parent_class)->constructed (object);
context = ide_object_get_context (IDE_OBJECT (self));
g_assert (IDE_IS_CONTEXT (context));
buffer_manager = ide_buffer_manager_from_context (context);
g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
}
static void
ide_golang_build_system_finalize (GObject *object)
{
IdeGolangBuildSystem *self = (IdeGolangBuildSystem *)object;
g_clear_pointer (&self->goroot, g_free);
g_clear_pointer (&self->gopath, g_free);
G_OBJECT_CLASS (ide_golang_build_system_parent_class)->finalize (object);
}
static void
ide_golang_build_system_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
IdeGolangBuildSystem *self = IDE_GOLANG_BUILD_SYSTEM (object);
switch (prop_id)
{
case PROP_GOROOT:
g_value_set_string (value, self->goroot);
break;
case PROP_GOPATH:
g_value_set_string (value, self->gopath);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_golang_build_system_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
IdeGolangBuildSystem *self = IDE_GOLANG_BUILD_SYSTEM (object);
switch (prop_id)
{
case PROP_GOROOT:
g_free (self->goroot);
self->goroot = g_value_dup_string (value);
break;
case PROP_GOPATH:
g_free (self->gopath);
self->gopath = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static gchar *
ide_golang_build_system_get_id (IdeBuildSystem *build_system)
{
return g_strdup ("golang");
}
static gchar *
ide_golang_build_system_get_display_name (IdeBuildSystem *build_system)
{
return g_strdup ("Golang");
}
static gint
ide_golang_build_system_get_priority (IdeBuildSystem *system)
{
return 0;
}
static void
ide_golang_build_system_get_build_flags_async (IdeBuildSystem *build_system,
GFile *file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
IdeGolangBuildSystem *self = (IdeGolangBuildSystem *)build_system;
g_autoptr(IdeTask) task = NULL;
IDE_ENTRY;
g_assert (IDE_IS_GOLANG_BUILD_SYSTEM (self));
g_assert (G_IS_FILE (file));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (self, cancellable, callback, user_data);
ide_task_set_source_tag (task, ide_golang_build_system_get_build_flags_async);
IDE_EXIT;
}
static gchar **
ide_golang_build_system_get_build_flags_finish (IdeBuildSystem *build_system,
GAsyncResult *result,
GError **error)
{
g_assert (IDE_IS_GOLANG_BUILD_SYSTEM (build_system));
g_assert (IDE_IS_TASK (result));
return ide_task_propagate_pointer (IDE_TASK (result), error);
}
static void
ide_golang_build_system_init (IdeGolangBuildSystem *self)
{
}
static void
ide_golang_build_system_init_async (GAsyncInitable *initable,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
IdeGolangBuildSystem *system = (IdeGolangBuildSystem *)initable;
g_autoptr(IdeTask) task = NULL;
IDE_ENTRY;
g_return_if_fail (IDE_IS_GOLANG_BUILD_SYSTEM (system));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (initable, cancellable, callback, user_data);
ide_task_set_source_tag (task, ide_golang_build_system_init_async);
ide_task_set_priority (task, G_PRIORITY_HIGH);
ide_task_return_boolean (task, TRUE);
}