about summary refs log tree commit diff
path: root/pkgs/build-support/make-hardcode-gsettings-patch
diff options
context:
space:
mode:
authorJan Tojnar <jtojnar@gmail.com>2022-12-15 20:25:32 +0100
committerJan Tojnar <jtojnar@gmail.com>2022-12-19 17:17:32 +0100
commit35d24b51f536cf3ead96b284e862ef8d9fe47edc (patch)
tree63f6dfcf86ba9ee843d3263302cfd99da779a87d /pkgs/build-support/make-hardcode-gsettings-patch
parent04f574a1c0fde90b51bf68198e2297ca4e7cccf4 (diff)
makeHardcodeGsettingsPatch: Rename from glib.mkHardcodeGsettingsPatch
glib expression is messy enough as is.

Also rename the `glib-schema-to-var` argument to `schemaIdToVariableMapping` to better match Nixpkgs coding style.
Diffstat (limited to 'pkgs/build-support/make-hardcode-gsettings-patch')
-rw-r--r--pkgs/build-support/make-hardcode-gsettings-patch/default.nix60
-rw-r--r--pkgs/build-support/make-hardcode-gsettings-patch/hardcode-gsettings.cocci70
2 files changed, 130 insertions, 0 deletions
diff --git a/pkgs/build-support/make-hardcode-gsettings-patch/default.nix b/pkgs/build-support/make-hardcode-gsettings-patch/default.nix
new file mode 100644
index 0000000000000..a1d2de21c4cb3
--- /dev/null
+++ b/pkgs/build-support/make-hardcode-gsettings-patch/default.nix
@@ -0,0 +1,60 @@
+{
+  runCommand,
+  git,
+  coccinelle,
+  python3,
+}:
+
+/*
+  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 = makeHardcodeGsettingsPatch {
+      inherit src;
+      schemaIdToVariableMapping = {
+         ...
+      };
+    };
+
+    updateScript =
+      let
+        updateSource = ...;
+        updatePatch = _experimental-update-script-combinators.copyAttrOutputToFile "evolution-ews.hardcodeGsettingsPatch" ./hardcode-gsettings.patch;
+      in
+      _experimental-update-script-combinators.sequence [
+        updateSource
+        updatePatch
+      ];
+    };
+  }
+  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.
+*/
+{
+  src,
+  schemaIdToVariableMapping,
+}:
+
+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 schemaIdToVariableMapping)} ./glib-schema-to-var.json
+    git init
+    git add -A
+    spatch --sp-file "${./hardcode-gsettings.cocci}" --dir . --in-place
+    git diff > "$out"
+  ''
diff --git a/pkgs/build-support/make-hardcode-gsettings-patch/hardcode-gsettings.cocci b/pkgs/build-support/make-hardcode-gsettings-patch/hardcode-gsettings.cocci
new file mode 100644
index 0000000000000..bedacf846bc49
--- /dev/null
+++ b/pkgs/build-support/make-hardcode-gsettings-patch/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);
++}