[gnome-builder/wip/gtk4-port: 1558/1774] libide/foundry: implement layer merging for run context
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port: 1558/1774] libide/foundry: implement layer merging for run context
- Date: Mon, 11 Jul 2022 22:31:49 +0000 (UTC)
commit db860ec8b8339f74ae50b8d7d1a78b4f93ddc94a
Author: Christian Hergert <chergert redhat com>
Date: Fri Jun 17 01:02:12 2022 -0700
libide/foundry: implement layer merging for run context
src/libide/foundry/ide-run-context.c | 276 ++++++++++++++++++++++++++++++++---
src/libide/foundry/ide-run-context.h | 10 +-
src/libide/foundry/libide-foundry.h | 1 +
src/tests/meson.build | 6 +
src/tests/test-run-context.c | 66 +++++++++
5 files changed, 338 insertions(+), 21 deletions(-)
---
diff --git a/src/libide/foundry/ide-run-context.c b/src/libide/foundry/ide-run-context.c
index 81a60c716..14d356161 100644
--- a/src/libide/foundry/ide-run-context.c
+++ b/src/libide/foundry/ide-run-context.c
@@ -48,6 +48,12 @@ struct _IdeRunContext
G_DEFINE_FINAL_TYPE (IdeRunContext, ide_run_context, G_TYPE_OBJECT)
+IdeRunContext *
+ide_run_context_new (void)
+{
+ return g_object_new (IDE_TYPE_RUN_CONTEXT, NULL);
+}
+
static void
ide_run_context_layer_clear (IdeRunContextLayer *layer)
{
@@ -56,6 +62,9 @@ ide_run_context_layer_clear (IdeRunContextLayer *layer)
g_assert (layer->qlink.prev == NULL);
g_assert (layer->qlink.next == NULL);
+ if (layer->handler_data_destroy)
+ g_clear_pointer (&layer->handler_data, layer->handler_data_destroy);
+
g_clear_pointer (&layer->cwd, g_free);
g_clear_pointer (&layer->argv, g_array_unref);
g_clear_pointer (&layer->env, g_array_unref);
@@ -66,12 +75,37 @@ static void
ide_run_context_layer_free (IdeRunContextLayer *layer)
{
ide_run_context_layer_clear (layer);
+
g_slice_free (IdeRunContextLayer, layer);
}
+static void
+strptr_free (gpointer data)
+{
+ char **strptr = data;
+ g_clear_pointer (strptr, g_free);
+}
+
+static void
+ide_run_context_layer_init (IdeRunContextLayer *layer)
+{
+ g_assert (layer != NULL);
+
+ layer->qlink.data = layer;
+ layer->argv = g_array_new (TRUE, TRUE, sizeof (char *));
+ layer->env = g_array_new (TRUE, TRUE, sizeof (char *));
+ layer->unix_fd_map = ide_unix_fd_map_new ();
+
+ g_array_set_clear_func (layer->argv, strptr_free);
+ g_array_set_clear_func (layer->env, strptr_free);
+}
+
static IdeRunContextLayer *
ide_run_context_current_layer (IdeRunContext *self)
{
+ g_assert (IDE_IS_RUN_CONTEXT (self));
+ g_assert (self->layers.length > 0);
+
return self->layers.head->data;
}
@@ -104,15 +138,9 @@ ide_run_context_class_init (IdeRunContextClass *klass)
static void
ide_run_context_init (IdeRunContext *self)
{
- self->root.qlink.data = self;
- g_queue_push_head_link (&self->layers, &self->root.qlink);
-}
+ ide_run_context_layer_init (&self->root);
-static void
-strptr_free (gpointer data)
-{
- char **strptr = data;
- g_clear_pointer (strptr, g_free);
+ g_queue_push_head_link (&self->layers, &self->root.qlink);
}
void
@@ -126,17 +154,13 @@ ide_run_context_push (IdeRunContext *self,
g_return_if_fail (IDE_IS_RUN_CONTEXT (self));
layer = g_slice_new0 (IdeRunContextLayer);
- layer->qlink.data = layer;
- layer->argv = g_array_new (TRUE, FALSE, sizeof (char *));
- layer->env = g_array_new (TRUE, FALSE, sizeof (char *));
- layer->unix_fd_map = ide_unix_fd_map_new ();
+
+ ide_run_context_layer_init (layer);
+
layer->handler = handler;
layer->handler_data = handler_data;
layer->handler_data_destroy = handler_data_destroy;
- g_array_set_clear_func (layer->argv, strptr_free);
- g_array_set_clear_func (layer->env, strptr_free);
-
g_queue_push_head_link (&self->layers, &layer->qlink);
}
@@ -152,6 +176,26 @@ ide_run_context_get_argv (IdeRunContext *self)
return (const char * const *)&g_array_index (layer->argv, char *, 0);
}
+void
+ide_run_context_set_argv (IdeRunContext *self,
+ const char * const *argv)
+{
+ IdeRunContextLayer *layer;
+
+ g_return_if_fail (IDE_IS_RUN_CONTEXT (self));
+
+ layer = ide_run_context_current_layer (self);
+
+ g_array_set_size (layer->argv, 0);
+
+ if (argv != NULL)
+ {
+ char **copy = g_strdupv ((char **)argv);
+ g_array_append_vals (layer->argv, copy, g_strv_length (copy));
+ g_free (copy);
+ }
+}
+
const char * const *
ide_run_context_get_environ (IdeRunContext *self)
{
@@ -176,10 +220,58 @@ ide_run_context_set_environ (IdeRunContext *self,
g_array_set_size (layer->env, 0);
- if (environ != NULL)
+ if (environ != NULL && environ[0] != NULL)
{
char **copy = g_strdupv ((char **)environ);
g_array_append_vals (layer->env, copy, g_strv_length (copy));
+ g_free (copy);
+ }
+}
+
+void
+ide_run_context_add_environ (IdeRunContext *self,
+ const char * const *environ)
+{
+ IdeRunContextLayer *layer;
+
+ g_return_if_fail (IDE_IS_RUN_CONTEXT (self));
+
+ if (environ == NULL || environ[0] == NULL)
+ return;
+
+ layer = ide_run_context_current_layer (self);
+
+ for (guint i = 0; environ[i]; i++)
+ {
+ const char *pair = environ[i];
+ const char *eq = strchr (pair, '=');
+ char **dest = NULL;
+ gsize keylen;
+
+ if (eq == NULL)
+ continue;
+
+ keylen = eq - pair;
+
+ for (guint j = 0; j < layer->env->len; j++)
+ {
+ const char *ele = g_array_index (layer->env, const char *, j);
+
+ if (strncmp (pair, ele, keylen) == 0 && ele[keylen] == '=')
+ {
+ dest = &g_array_index (layer->env, char *, j);
+ break;
+ }
+ }
+
+ if (dest == NULL)
+ {
+ g_array_set_size (layer->env, layer->env->len + 1);
+ dest = &g_array_index (layer->env, char *, layer->env->len - 1);
+ }
+
+ g_clear_pointer (dest, g_free);
+ *dest = g_strdup (pair);
}
}
@@ -228,6 +320,25 @@ ide_run_context_prepend_argv (IdeRunContext *self,
g_array_insert_val (layer->argv, 0, copy);
}
+void
+ide_run_context_prepend_args (IdeRunContext *self,
+ const char * const *args)
+{
+ IdeRunContextLayer *layer;
+ char **copy;
+
+ g_return_if_fail (IDE_IS_RUN_CONTEXT (self));
+
+ if (args == NULL || args[0] == NULL)
+ return;
+
+ layer = ide_run_context_current_layer (self);
+
+ copy = g_strdupv ((char **)args);
+ g_array_insert_vals (layer->argv, 0, copy, g_strv_length (copy));
+ g_free (copy);
+}
+
void
ide_run_context_append_argv (IdeRunContext *self,
const char *arg)
@@ -252,12 +363,15 @@ ide_run_context_append_args (IdeRunContext *self,
char **copy;
g_return_if_fail (IDE_IS_RUN_CONTEXT (self));
- g_return_if_fail (args != NULL);
+
+ if (args == NULL || args[0] == NULL)
+ return;
layer = ide_run_context_current_layer (self);
copy = g_strdupv ((char **)args);
g_array_append_vals (layer->argv, copy, g_strv_length (copy));
+ g_free (copy);
}
gboolean
@@ -272,11 +386,13 @@ ide_run_context_append_args_parsed (IdeRunContext *self,
g_return_val_if_fail (IDE_IS_RUN_CONTEXT (self), FALSE);
g_return_val_if_fail (args != NULL, FALSE);
+ layer = ide_run_context_current_layer (self);
+
if (!g_shell_parse_argv (args, &argc, &argv, error))
return FALSE;
- layer = ide_run_context_current_layer (self);
g_array_append_vals (layer->argv, argv, argc);
+ g_free (argv);
return TRUE;
}
@@ -385,6 +501,110 @@ ide_run_context_unsetenv (IdeRunContext *self,
}
}
+void
+ide_run_context_environ_to_argv (IdeRunContext *self)
+{
+ static const char *envstr = "env";
+ IdeRunContextLayer *layer;
+ char **args;
+ gsize len;
+
+ g_assert (IDE_IS_RUN_CONTEXT (self));
+
+ layer = ide_run_context_current_layer (self);
+
+ if (layer->env->len == 0)
+ return;
+
+ args = g_array_steal (layer->env, &len);
+ g_array_insert_vals (layer->argv, 0, args, len);
+ g_array_insert_val (layer->argv, 0, envstr);
+ g_free (args);
+}
+
+static gboolean
+ide_run_context_default_handler (IdeRunContext *self,
+ const char * const *argv,
+ const char * const *env,
+ const char *cwd,
+ IdeUnixFDMap *unix_fd_map,
+ gpointer user_data,
+ GError **error)
+{
+ IdeRunContextLayer *layer;
+
+ g_assert (IDE_IS_RUN_CONTEXT (self));
+ g_assert (argv != NULL);
+ g_assert (env != NULL);
+ g_assert (IDE_IS_UNIX_FD_MAP (unix_fd_map));
+
+ layer = ide_run_context_current_layer (self);
+
+ if (cwd != NULL)
+ {
+ /* If the working directories do not match, we can't satisfy this and
+ * need to error out.
+ */
+ if (layer->cwd != NULL && !ide_str_equal (cwd, layer->cwd))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Cannot resolve differently requested cwd: %s and %s",
+ cwd, layer->cwd);
+ return FALSE;
+ }
+
+ ide_run_context_set_cwd (self, cwd);
+ }
+
+ /* Merge all the FDs unless there are collisions */
+ if (!ide_unix_fd_map_steal_from (layer->unix_fd_map, unix_fd_map, error))
+ return FALSE;
+
+ /* Replace environment for this layer to use "env FOO=Bar" style subcommand
+ * so that it's evironment doesn't attach to the parent program.
+ */
+ ide_run_context_environ_to_argv (self);
+
+ /* Then make sure the higher layer's environment has higher priority */
+ ide_run_context_set_environ (self, env);
+
+ /* Now prepend the arguments and set new working dir */
+ ide_run_context_prepend_args (self, argv);
+
+ return TRUE;
+}
+
+static gboolean
+ide_run_context_callback_layer (IdeRunContext *self,
+ IdeRunContextLayer *layer,
+ GError **error)
+{
+ IdeRunContextHandler handler;
+ gpointer handler_data;
+ gboolean ret;
+
+ g_return_val_if_fail (IDE_IS_RUN_CONTEXT (self), FALSE);
+ g_return_val_if_fail (layer != NULL, FALSE);
+ g_return_val_if_fail (layer != &self->root, FALSE);
+
+ handler = layer->handler ? layer->handler : ide_run_context_default_handler;
+ handler_data = layer->handler ? layer->handler_data : NULL;
+
+ ret = handler (self,
+ (const char * const *)&g_array_index (layer->argv, const char *, 0),
+ (const char * const *)&g_array_index (layer->env, const char *, 0),
+ layer->cwd,
+ layer->unix_fd_map,
+ handler_data,
+ error);
+
+ ide_run_context_layer_free (layer);
+
+ return ret;
+}
+
/**
* ide_run_context_end:
* @self: a #IdeRunContext
@@ -396,12 +616,28 @@ IdeSubprocessLauncher *
ide_run_context_end (IdeRunContext *self,
GError **error)
{
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+
g_return_val_if_fail (IDE_IS_RUN_CONTEXT (self), NULL);
g_return_val_if_fail (self->ended == FALSE, NULL);
self->ended = TRUE;
- /* TODO: Process layers */
+ while (self->layers.length > 1)
+ {
+ IdeRunContextLayer *layer = ide_run_context_current_layer (self);
- return NULL;
+ g_queue_unlink (&self->layers, &layer->qlink);
+
+ if (!ide_run_context_callback_layer (self, layer, error))
+ return FALSE;
+ }
+
+ launcher = ide_subprocess_launcher_new (0);
+
+ ide_subprocess_launcher_set_argv (launcher, ide_run_context_get_argv (self));
+ ide_subprocess_launcher_set_environ (launcher, ide_run_context_get_environ (self));
+ ide_subprocess_launcher_set_cwd (launcher, ide_run_context_get_cwd (self));
+
+ return g_steal_pointer (&launcher);
}
diff --git a/src/libide/foundry/ide-run-context.h b/src/libide/foundry/ide-run-context.h
index 3fd4fd4ff..dca8bd10e 100644
--- a/src/libide/foundry/ide-run-context.h
+++ b/src/libide/foundry/ide-run-context.h
@@ -42,7 +42,7 @@ typedef gboolean (*IdeRunContextHandler) (IdeRunContext *self,
const char * const *argv,
const char * const *env,
const char *cwd,
- IdeUnixFDMap *fd_map,
+ IdeUnixFDMap *unix_fd_map,
gpointer user_data,
GError **error);
@@ -64,6 +64,11 @@ IDE_AVAILABLE_IN_ALL
void ide_run_context_set_environ (IdeRunContext *self,
const char * const *environ);
IDE_AVAILABLE_IN_ALL
+void ide_run_context_add_environ (IdeRunContext *self,
+ const char * const *environ);
+IDE_AVAILABLE_IN_ALL
+void ide_run_context_environ_to_argv (IdeRunContext *self);
+IDE_AVAILABLE_IN_ALL
const char *ide_run_context_get_cwd (IdeRunContext *self);
IDE_AVAILABLE_IN_ALL
void ide_run_context_set_cwd (IdeRunContext *self,
@@ -76,6 +81,9 @@ IDE_AVAILABLE_IN_ALL
void ide_run_context_prepend_argv (IdeRunContext *self,
const char *arg);
IDE_AVAILABLE_IN_ALL
+void ide_run_context_prepend_args (IdeRunContext *self,
+ const char * const *args);
+IDE_AVAILABLE_IN_ALL
void ide_run_context_append_argv (IdeRunContext *self,
const char *arg);
IDE_AVAILABLE_IN_ALL
diff --git a/src/libide/foundry/libide-foundry.h b/src/libide/foundry/libide-foundry.h
index ad8035a79..6ed70b3ba 100644
--- a/src/libide/foundry/libide-foundry.h
+++ b/src/libide/foundry/libide-foundry.h
@@ -57,6 +57,7 @@ G_BEGIN_DECLS
#include "ide-pipeline.h"
#include "ide-run-command.h"
#include "ide-run-command-provider.h"
+#include "ide-run-context.h"
#include "ide-run-manager.h"
#include "ide-runner-addin.h"
#include "ide-runner.h"
diff --git a/src/tests/meson.build b/src/tests/meson.build
index 7292a42c3..21a5b5666 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -72,3 +72,9 @@ test_shortcuts = executable('test-shortcuts', 'test-shortcuts.c',
dependencies: [ libgtk_dep, libide_gui_dep ],
)
test('test-shortcuts', test_shortcuts, env: test_env, depends: [libide_gir])
+
+test_run_context = executable('test-run-context', 'test-run-context.c',
+ c_args: test_cflags,
+ dependencies: [ libide_foundry_dep ],
+)
+test('test-run-context', test_run_context, env: test_env)
diff --git a/src/tests/test-run-context.c b/src/tests/test-run-context.c
new file mode 100644
index 000000000..c8569f406
--- /dev/null
+++ b/src/tests/test-run-context.c
@@ -0,0 +1,66 @@
+/* test-run-context.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+
+#include <libide-foundry.h>
+
+static void
+test_run_context_default_handler (void)
+{
+ IdeRunContext *run_context;
+ IdeSubprocessLauncher *launcher;
+ g_autoptr(GError) error = NULL;
+
+ run_context = ide_run_context_new ();
+
+ ide_run_context_set_cwd (run_context, "/home/user");
+ ide_run_context_set_argv (run_context, IDE_STRV_INIT ("ls", "-lsah"));
+ ide_run_context_setenv (run_context, "USER", "user");
+ ide_run_context_setenv (run_context, "UID", "1000");
+ ide_run_context_take_fd (run_context, dup (STDOUT_FILENO), STDOUT_FILENO);
+
+ ide_run_context_push (run_context, NULL, NULL, NULL);
+ ide_run_context_set_argv (run_context, IDE_STRV_INIT ("wrapper", "--"));
+ ide_run_context_set_environ (run_context, IDE_STRV_INIT ("USER=nobody"));
+
+ launcher = ide_run_context_end (run_context, &error);
+ g_assert_no_error (error);
+ g_assert_true (IDE_IS_SUBPROCESS_LAUNCHER (launcher));
+
+ g_assert_true (g_strv_equal (ide_subprocess_launcher_get_argv (launcher),
+ IDE_STRV_INIT ("wrapper", "--", "env", "USER=user", "ls", "-lsah")));
+ g_assert_true (g_strv_equal (ide_subprocess_launcher_get_environ (launcher),
+ IDE_STRV_INIT ("USER=nobody")));
+
+ g_assert_finalize_object (launcher);
+ g_assert_finalize_object (run_context);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/Ide/Foundry/RunContext/default_handler", test_run_context_default_handler);
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]