Subject: [PATCH v3 1/4] gnu: glib: Support load search paths from etc/search-paths.d files.

From: [email-masked]
Date: Mon, 27 Jan 2025 21:21:20 +0800

From: 宋文武 <[email-masked]>

Add a new function "g_build_guix_search_path_dirs" to GLIB, which in addition
to environment variables, reads search path values from the etc/search-paths.d
directory of the current executable.  This can be used to replace wrapper
scripts.

Use it for GUIX_GSETTINGS_SCHEMA_DIR, GUIX_GIO_EXTRA_MODULES,
GUIX_XDG_DATA_DIRS and GUIX_XDG_CONFIG_DIRS.

* gnu/packages/patches/glib-guix-search-paths.patch: New file.
* gnu/local.mk (dist_patch_DATA): Register it.
* gnu/packages/glib.scm (glib)[source]: Add patch.
[native-search-paths]: Add GUIX_GSETTINGS_SCHEMA_DIR.  Replace
GIO_EXTRA_MODULES with GUIX_GIO_EXTRA_MODULES.

Change-Id: I1d6d113fc38b20ebd4dce195f6d9c58ce85967e4
---
 gnu/local.mk                                  |   1 +
 gnu/packages/glib.scm                         |   9 +-
 .../patches/glib-guix-search-paths.patch      | 162 ++++++++++++++++++
 3 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 gnu/packages/patches/glib-guix-search-paths.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index 6961b8816c..7ae66dd57d 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1438,6 +1438,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/git-filter-repo-generate-doc.patch	\
   %D%/packages/patches/gklib-suitesparse.patch			\
   %D%/packages/patches/glib-appinfo-watch.patch			\
+  %D%/packages/patches/glib-guix-search-paths.patch		\
   %D%/packages/patches/glib-skip-failing-test.patch		\
   %D%/packages/patches/glibc-2.33-riscv64-miscompilation.patch	\
   %D%/packages/patches/glibc-2.39-git-updates.patch	\
diff --git a/gnu/packages/glib.scm b/gnu/packages/glib.scm
index e04eedb7ba..0704ba2c53 100644
--- a/gnu/packages/glib.scm
+++ b/gnu/packages/glib.scm
@@ -258,7 +258,8 @@ (define glib
         (base32 "0c3vagxl77wma85qinbj974jvw96n5bvch2m7hqcwxq8fa5spsj4"))
        (patches
         (search-patches "glib-appinfo-watch.patch"
-                        "glib-skip-failing-test.patch"))
+                        "glib-skip-failing-test.patch"
+                        "glib-guix-search-paths.patch"))
        (modules '((guix build utils)))
        (snippet
         '(begin
@@ -516,9 +517,13 @@ (define glib
       (search-path-specification
        (variable "XDG_DATA_DIRS")
        (files '("share")))
+      ;; To load gsettings schemas from GTK, etc.
+      (search-path-specification
+       (variable "GUIX_GSETTINGS_SCHEMA_DIR")
+       (files '("share/glib-2.0/schemas")))
       ;; To load extra gio modules from glib-networking, etc.
       (search-path-specification
-       (variable "GIO_EXTRA_MODULES")
+       (variable "GUIX_GIO_EXTRA_MODULES")
        (files '("lib/gio/modules")))))
     (search-paths native-search-paths)
     (synopsis "Low-level core library for GNOME projects")
diff --git a/gnu/packages/patches/glib-guix-search-paths.patch b/gnu/packages/patches/glib-guix-search-paths.patch
new file mode 100644
index 0000000000..565c045a55
--- /dev/null
+++ b/gnu/packages/patches/glib-guix-search-paths.patch
@@ -0,0 +1,162 @@
+diff --git a/gio/giomodule.c b/gio/giomodule.c
+index 76c2028..7afa8ef 100644
+--- a/gio/giomodule.c
++++ b/gio/giomodule.c
+@@ -1330,6 +1330,13 @@ _g_io_modules_ensure_loaded (void)
+       g_io_modules_scan_all_in_directory_with_scope (module_dir, scope);
+       g_free (module_dir);
+ 
++      /* Load modules from GUIX_GIO_EXTRA_MODULES. */
++      gchar **guix_giomodule_dirs = g_build_guix_search_path_dirs ("GUIX_GIO_EXTRA_MODULES");
++      for (int i = 0; guix_giomodule_dirs[i] != NULL; i++) {
++        g_io_modules_scan_all_in_directory_with_scope (guix_giomodule_dirs[i], scope);
++      }
++      g_strfreev (guix_giomodule_dirs);
++
+       g_io_module_scope_free (scope);
+ 
+       /* Initialize types from built-in "modules" */
+diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
+index e8ccc8c..6ac0f32 100644
+--- a/gio/gsettingsschema.c
++++ b/gio/gsettingsschema.c
+@@ -354,6 +354,13 @@ initialise_schema_sources (void)
+       while (i--)
+         try_prepend_data_dir (dirs[i]);
+ 
++      /* Load schemas from GUIX_GSETTINGS_SCHEMA_DIR. */
++      char **guix_schema_dirs = g_build_guix_search_path_dirs ("GUIX_GSETTINGS_SCHEMA_DIR");
++      i = g_strv_length(guix_schema_dirs);
++      while (i--)
++        try_prepend_dir (guix_schema_dirs[i]);
++      g_strfreev (guix_schema_dirs);
++
+       try_prepend_data_dir (g_get_user_data_dir ());
+ 
+       /* Disallow loading extra schemas if running as setuid, as that could
+diff --git a/glib/gutils.c b/glib/gutils.c
+index 8628a56..0f71890 100644
+--- a/glib/gutils.c
++++ b/glib/gutils.c
+@@ -2708,6 +2708,16 @@ g_build_system_data_dirs (void)
+     data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
+ #endif
+ 
++  /* Use data files from GUIX_XDG_DATA_DIRS. */
++  GStrvBuilder *builder = g_strv_builder_new ();
++  g_strv_builder_addv (builder, (const gchar **) data_dir_vector);
++  g_strfreev (data_dir_vector);
++  data_dir_vector = g_build_guix_search_path_dirs ("GUIX_XDG_DATA_DIRS");
++  g_strv_builder_addv (builder, (const gchar **) data_dir_vector);
++  g_strfreev (data_dir_vector);
++  data_dir_vector = g_strv_builder_end (builder);
++  g_strv_builder_unref (builder);
++
+   return g_steal_pointer (&data_dir_vector);
+ }
+ 
+@@ -2800,6 +2810,16 @@ g_build_system_config_dirs (void)
+   conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
+ #endif
+ 
++  /* Use config files from GUIX_XDG_CONFIG_DIRS. */
++  GStrvBuilder *builder = g_strv_builder_new ();
++  g_strv_builder_addv (builder, (const gchar **) conf_dir_vector);
++  g_strfreev (conf_dir_vector);
++  conf_dir_vector = g_build_guix_search_path_dirs ("GUIX_XDG_CONFIG_DIRS");
++  g_strv_builder_addv (builder, (const gchar **) conf_dir_vector);
++  g_strfreev (conf_dir_vector);
++  conf_dir_vector = g_strv_builder_end (builder);
++  g_strv_builder_unref (builder);
++
+   return g_steal_pointer (&conf_dir_vector);
+ }
+ 
+@@ -2849,6 +2869,73 @@ g_get_system_config_dirs (void)
+   return system_config_dirs;
+ }
+ 
++gchar **
++g_build_guix_search_path_dirs (const gchar *variable)
++{
++  gchar **dirs = NULL;
++  char *value = NULL;
++  GStrvBuilder *builder = g_strv_builder_new ();
++
++  /* First add paths from the etc/search-paths.d, which can be used to replace wrapper script. */
++  gchar *out_path = NULL;
++  gchar *search_paths_d = NULL;
++  gchar *exe_path = g_file_read_link ("/proc/self/exe", NULL);
++
++  /* For scripts, we use GUIX_MAIN_SCRIPT_PATH to find its location. */
++  if (g_strcmp0 (exe_path, g_getenv ("GUIX_INTERPRETER_PATH")) == 0) {
++    g_free (exe_path);
++    exe_path = g_getenv ("GUIX_MAIN_SCRIPT_PATH");
++  }
++
++  /* We install executables under "bin" or "libexec", can also be a subdirectory of "libexec". */
++  if (exe_path && (g_str_match_string("/bin/", exe_path, FALSE) ||
++                   g_str_match_string("/libexec/", exe_path, FALSE))) {
++    /* Find output directory, which is the parent directory of "bin" or "libexec". */
++    out_path = g_path_get_dirname (exe_path);
++    while (g_str_match_string("/bin/", out_path, FALSE) ||
++           g_str_match_string("/libexec/", out_path, FALSE)) {
++      gchar *dir_path = out_path;
++      out_path = g_path_get_dirname (dir_path);
++      g_free (dir_path);
++    }
++
++    /* Now add paths from etc/search-paths.d/VARIABLE file. */
++    search_paths_d = g_build_filename (out_path, "etc", "search-paths.d", NULL);
++    if (g_file_test (search_paths_d, G_FILE_TEST_EXISTS)) {
++      gchar *var_path = g_build_filename (search_paths_d, variable, NULL);
++      if (g_file_get_contents (var_path, &value, NULL, NULL)) {
++        dirs = g_strsplit (value, G_SEARCHPATH_SEPARATOR_S, 0);
++        g_strv_builder_addv (builder, (const gchar **) dirs);
++        g_strfreev (dirs);
++        g_free (value);
++      }
++      g_free (var_path);
++    }
++  }
++
++  free (exe_path);
++  g_free (out_path);
++  g_free (search_paths_d);
++
++  /* Then add paths from the environment variable. */
++  gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
++  if (is_setuid) /* we don't want to access arbitrary files when running as setuid. */
++    value = NULL;
++  else
++    value = g_strdup (g_getenv (variable));
++
++  if (value && value[0]) {
++      dirs = g_strsplit (value, G_SEARCHPATH_SEPARATOR_S, 0);
++      g_strv_builder_addv (builder, (const gchar **) dirs);
++      g_strfreev (dirs);
++  }
++  g_free (value);
++
++  dirs = g_strv_builder_end (builder);
++  g_strv_builder_unref (builder);
++  return dirs;
++}
++
+ /**
+  * g_nullify_pointer:
+  * @nullify_location: (not nullable): the memory address of the pointer.
+diff --git a/glib/gutils.h b/glib/gutils.h
+index efc6914..710cf27 100644
+--- a/glib/gutils.h
++++ b/glib/gutils.h
+@@ -36,6 +36,9 @@
+ 
+ G_BEGIN_DECLS
+ 
++GLIB_AVAILABLE_IN_ALL
++gchar **g_build_guix_search_path_dirs (const gchar *variable);
++
+ GLIB_AVAILABLE_IN_ALL
+ const gchar *         g_get_user_name        (void);
+ GLIB_AVAILABLE_IN_ALL

base-commit: 77603927fba0edc2c4d9de122aa132b968a051e5
-- 
2.47.1