about summary refs log tree commit diff
path: root/pkgs/development/libraries/glib
diff options
context:
space:
mode:
authorGuillaume Girol <symphorien+git@xlumurb.eu>2022-10-13 12:00:00 +0000
committerGuillaume Girol <symphorien+git@xlumurb.eu>2022-11-11 13:30:00 +0100
commite094494915761e7b4e6de47f5b9c87a301edb974 (patch)
treec69a625242a5140baa5bb40e400775c41174cff7 /pkgs/development/libraries/glib
parent0533be43f1ce89c51ce9720386258ea5c19fcb1d (diff)
glib: add an update script combinator to patch gsettings schema paths
and use it in evolution-data-server and evolution-ews as a proof of
concept
Diffstat (limited to 'pkgs/development/libraries/glib')
-rw-r--r--pkgs/development/libraries/glib/default.nix51
-rw-r--r--pkgs/development/libraries/glib/hardcode-gsettings.cocci70
2 files changed, 121 insertions, 0 deletions
diff --git a/pkgs/development/libraries/glib/default.nix b/pkgs/development/libraries/glib/default.nix
index 8e3d1a45dbe1c..b38ba682a76be 100644
--- a/pkgs/development/libraries/glib/default.nix
+++ b/pkgs/development/libraries/glib/default.nix
@@ -8,6 +8,8 @@
 , coreutils, dbus, libxml2, tzdata
 , desktop-file-utils, shared-mime-info
 , darwin
+# update script
+, runCommand, git, coccinelle
 }:
 
 assert stdenv.isLinux -> util-linuxMinimal != null;
@@ -246,6 +248,55 @@ stdenv.mkDerivation (finalAttrs: {
       packageName = "glib";
       versionPolicy = "odd-unstable";
     };
+    /*
+      can be used as part of an update script to automatically create a patch
+      hardcoding the path of all gsettings schemas in C code.
+      For example:
+      passthru = {
+        hardcodeGsettingsPatch = glib.mkHardcodeGsettingsPatch {
+          inherit src;
+          glib-schema-to-var = {
+             ...
+          };
+        };
+
+        updateScript =
+          let
+            updateSource = ...;
+            patch = _experimental-update-script-combinators.copyAttrOutputToFile "evolution-ews.hardcodeGsettingsPatch" ./hardcode-gsettings.patch;
+          in
+          _experimental-update-script-combinators.sequence [
+            updateSource
+            patch
+          ];
+        };
+      }
+      takes as input a mapping from schema path to variable name.
+      For example `{ "org.gnome.evolution" = "EVOLUTION_SCHEMA_PATH"; }`
+      hardcodes looking for `org.gnome.evolution` into `@EVOLUTION_SCHEMA_PATH@`.
+      All schemas must be listed.
+    */
+    mkHardcodeGsettingsPatch = { src, glib-schema-to-var }:
+      runCommand
+        "hardcode-gsettings.patch"
+        {
+          inherit src;
+          nativeBuildInputs = [
+            git
+            coccinelle
+            python3 # For patch script
+          ];
+        }
+        ''
+          unpackPhase
+          cd "''${sourceRoot:-.}"
+          set -x
+          cp ${builtins.toFile "glib-schema-to-var.json" (builtins.toJSON glib-schema-to-var)} ./glib-schema-to-var.json
+          git init
+          git add -A
+          spatch --sp-file "${./hardcode-gsettings.cocci}" --dir . --in-place
+          git diff > "$out"
+        '';
   };
 
   meta = with lib; {
diff --git a/pkgs/development/libraries/glib/hardcode-gsettings.cocci b/pkgs/development/libraries/glib/hardcode-gsettings.cocci
new file mode 100644
index 0000000000000..bedacf846bc49
--- /dev/null
+++ b/pkgs/development/libraries/glib/hardcode-gsettings.cocci
@@ -0,0 +1,70 @@
+/**
+ * Since Nix does not have a standard location like /usr/share,
+ * where GSettings system could look for schemas, we need to point the software to a correct location somehow.
+ * For executables, we handle this using wrappers but this is not an option for libraries like e-d-s.
+ * Instead, we hardcode the schema path when creating the settings.
+ * A schema path (ie org.gnome.evolution) can be replaced by @EVOLUTION_SCHEMA_PATH@
+ * which is then replaced at build time by substituteAll.
+ * The mapping is provided in a json file ./glib-schema-to-var.json
+ */
+
+@initialize:python@
+@@
+import json
+
+cpp_constants = {}
+
+def register_cpp_constant(const_name, val):
+    cpp_constants[const_name] = val.strip()
+
+def resolve_cpp_constant(const_name):
+    return cpp_constants.get(const_name, const_name)
+
+with open("./glib-schema-to-var.json") as mapping_file:
+    schema_to_var = json.load(mapping_file);
+
+def get_schema_directory(schema_path):
+    # Sometimes the schema id is referenced using C preprocessor #define constant in the same file
+    # let’s try to resolve it first.
+    schema_path = resolve_cpp_constant(schema_path.strip()).strip('"')
+    if schema_path in schema_to_var:
+        return f'"@{schema_to_var[schema_path]}@"'
+    raise Exception(f"Unknown schema path {schema_path!r}, please add it to ./glib-schema-to-var.json")
+
+
+@find_cpp_constants@
+identifier const_name;
+expression val;
+@@
+
+#define const_name val
+
+@script:python record_cpp_constants depends on find_cpp_constants@
+const_name << find_cpp_constants.const_name;
+val << find_cpp_constants.val;
+@@
+
+register_cpp_constant(const_name, val)
+
+
+@depends on ever record_cpp_constants || never record_cpp_constants@
+// We want to run after #define constants have been collected but even if there are no #defines.
+expression SCHEMA_PATH;
+expression settings;
+// Coccinelle does not like autocleanup macros in + sections,
+// let’s use fresh id with concatenation to produce the code as a string.
+fresh identifier schema_source_decl = "g_autoptr(GSettingsSchemaSource) " ## "schema_source";
+fresh identifier schema_decl = "g_autoptr(GSettingsSchema) " ## "schema";
+fresh identifier SCHEMA_DIRECTORY = script:python(SCHEMA_PATH) { get_schema_directory(SCHEMA_PATH) };
+@@
+-settings = g_settings_new(SCHEMA_PATH);
++{
++	schema_source_decl;
++	schema_decl;
++	schema_source = g_settings_schema_source_new_from_directory(SCHEMA_DIRECTORY,
++	                                                            g_settings_schema_source_get_default(),
++	                                                            TRUE,
++	                                                            NULL);
++	schema = g_settings_schema_source_lookup(schema_source, SCHEMA_PATH, FALSE);
++	settings = g_settings_new_full(schema, NULL, NULL);
++}