Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion pkgs/build-support/make-hardcode-gsettings-patch/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
if optional schema exists. Its invocation will be replaced with TRUE
for known schemas.

- `preferDefaultSchemaSource`: when true, generated patches will first
look up the schema in the default schema source and only fall back to
the hardcoded schema directory if it is missing there.

- `patches`: A list of patches to apply before generating the patch.

Example:
Expand Down Expand Up @@ -59,8 +63,16 @@
patches ? [ ],
schemaIdToVariableMapping,
schemaExistsFunction ? null,
preferDefaultSchemaSource ? false,
}:

let
spFile =
if preferDefaultSchemaSource then
./hardcode-gsettings-default-first.cocci
else
./hardcode-gsettings.cocci;
in
runCommand "hardcode-gsettings.patch"
{
inherit src patches;
Expand All @@ -79,6 +91,6 @@ runCommand "hardcode-gsettings.patch"
cp ${builtins.toFile "glib-schema-exists-function.json" (builtins.toJSON schemaExistsFunction)} ./glib-schema-exists-function.json
git init
git add -A
spatch --sp-file "${./hardcode-gsettings.cocci}" --dir . --in-place
spatch --sp-file "${spFile}" --dir . --in-place
git diff > "$out"
''
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/**
* 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 patch the source code to look for the schema in a schema source
* through a hardcoded path to the schema.
*
* This variant keeps the default schema source first and only falls back to a hardcoded
* directory when the schema is missing there.
*
* For each schema id referenced in the source code (e.g. org.gnome.evolution),
* a variable name such as `EVOLUTION` must be provided in the ./glib-schema-to-var.json JSON file.
* It will end up in the resulting patch as `@EVOLUTION@` placeholder, which should be replaced at build time
* with a path to the directory containing a `gschemas.compiled` file that includes the schema.
*/

@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_id):
# Sometimes the schema id is referenced using C preprocessor #define constant in the same file
# let’s try to resolve it first.
schema_id = resolve_cpp_constant(schema_id.strip()).strip('"')
if schema_id in schema_to_var:
return f'"@{schema_to_var[schema_id]}@"'
raise Exception(f"Unknown schema path {schema_id!r}, please add it to ./glib-schema-to-var.json")


@script:python schema_exists_fn@
fn;
@@
import json

with open("./glib-schema-exists-function.json") as fn_file:
if (fn := json.load(fn_file)):
coccinelle.fn = fn


@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_ID;
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 = NULL";
fresh identifier schema_decl = "g_autoptr(GSettingsSchema) " ## "schema = NULL";
fresh identifier SCHEMA_DIRECTORY = script:python(SCHEMA_ID) { get_schema_directory(SCHEMA_ID) };
@@
-settings = g_settings_new(SCHEMA_ID);
+{
+ GSettingsSchemaSource *default_schema_source;
+ schema_source_decl;
+ schema_decl;
+ default_schema_source = g_settings_schema_source_get_default();
+ if (default_schema_source != NULL)
+ schema = g_settings_schema_source_lookup(default_schema_source, SCHEMA_ID, TRUE);
+ if (schema == NULL) {
+ schema_source = g_settings_schema_source_new_from_directory(SCHEMA_DIRECTORY,
+ default_schema_source,
+ TRUE,
+ NULL);
+ schema = g_settings_schema_source_lookup(schema_source, SCHEMA_ID, FALSE);
+ }
+ settings = g_settings_new_full(schema, NULL, NULL);
+}


@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_ID;
expression settings;
expression BACKEND;
// 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 = NULL";
fresh identifier schema_decl = "g_autoptr(GSettingsSchema) " ## "schema = NULL";
fresh identifier SCHEMA_DIRECTORY = script:python(SCHEMA_ID) { get_schema_directory(SCHEMA_ID) };
@@
-settings = g_settings_new_with_backend(SCHEMA_ID, BACKEND);
+{
+ GSettingsSchemaSource *default_schema_source;
+ schema_source_decl;
+ schema_decl;
+ default_schema_source = g_settings_schema_source_get_default();
+ if (default_schema_source != NULL)
+ schema = g_settings_schema_source_lookup(default_schema_source, SCHEMA_ID, TRUE);
+ if (schema == NULL) {
+ schema_source = g_settings_schema_source_new_from_directory(SCHEMA_DIRECTORY,
+ default_schema_source,
+ TRUE,
+ NULL);
+ schema = g_settings_schema_source_lookup(schema_source, SCHEMA_ID, FALSE);
+ }
+ settings = g_settings_new_full(schema, BACKEND, NULL);
+}


@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_ID;
expression settings;
expression BACKEND;
expression PATH;
// 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 = NULL";
fresh identifier schema_decl = "g_autoptr(GSettingsSchema) " ## "schema = NULL";
fresh identifier SCHEMA_DIRECTORY = script:python(SCHEMA_ID) { get_schema_directory(SCHEMA_ID) };
@@
-settings = g_settings_new_with_backend_and_path(SCHEMA_ID, BACKEND, PATH);
+{
+ GSettingsSchemaSource *default_schema_source;
+ schema_source_decl;
+ schema_decl;
+ default_schema_source = g_settings_schema_source_get_default();
+ if (default_schema_source != NULL)
+ schema = g_settings_schema_source_lookup(default_schema_source, SCHEMA_ID, TRUE);
+ if (schema == NULL) {
+ schema_source = g_settings_schema_source_new_from_directory(SCHEMA_DIRECTORY,
+ default_schema_source,
+ TRUE,
+ NULL);
+ schema = g_settings_schema_source_lookup(schema_source, SCHEMA_ID, FALSE);
+ }
+ settings = g_settings_new_full(schema, BACKEND, PATH);
+}


@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_ID;
expression settings;
expression PATH;
// 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 = NULL";
fresh identifier schema_decl = "g_autoptr(GSettingsSchema) " ## "schema = NULL";
fresh identifier SCHEMA_DIRECTORY = script:python(SCHEMA_ID) { get_schema_directory(SCHEMA_ID) };
@@
-settings = g_settings_new_with_path(SCHEMA_ID, PATH);
+{
+ GSettingsSchemaSource *default_schema_source;
+ schema_source_decl;
+ schema_decl;
+ default_schema_source = g_settings_schema_source_get_default();
+ if (default_schema_source != NULL)
+ schema = g_settings_schema_source_lookup(default_schema_source, SCHEMA_ID, TRUE);
+ if (schema == NULL) {
+ schema_source = g_settings_schema_source_new_from_directory(SCHEMA_DIRECTORY,
+ default_schema_source,
+ TRUE,
+ NULL);
+ schema = g_settings_schema_source_lookup(schema_source, SCHEMA_ID, FALSE);
+ }
+ settings = g_settings_new_full(schema, NULL, PATH);
+}


@replace_schema_exists_fns 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_ID;
identifier schema_exists_fn.fn;
@@
-fn(SCHEMA_ID)
+TRUE
39 changes: 35 additions & 4 deletions pkgs/by-name/gt/gtk3/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
cups,
broadwaySupport ? true,
wayland-scanner,
_experimental-update-script-combinators,
makeHardcodeGsettingsPatch,
testers,
}:

Expand Down Expand Up @@ -215,6 +217,14 @@ stdenv.mkDerivation (finalAttrs: {
# See: https://developer.gnome.org/gtk3/stable/gtk-building.html#extra-configuration-options
env.NIX_CFLAGS_COMPILE = "-DG_ENABLE_DEBUG -DG_DISABLE_CAST_CHECKS";

# The patch needs build-time substitution because it hardcodes GTK's own
# compiled schema directory in the final $out as a fallback lookup path.
prePatch = ''
substitute ${./patches/3.0-hardcode-gsettings.patch} hardcode-gsettings.patch \
--subst-var-by gtk ${glib.makeSchemaPath "$out" "${finalAttrs.pname}-${finalAttrs.version}"}
patches="$patches $PWD/hardcode-gsettings.patch"
'';

postPatch = ''
# See https://github.com/NixOS/nixpkgs/issues/132259
substituteInPlace meson.build \
Expand Down Expand Up @@ -273,11 +283,32 @@ stdenv.mkDerivation (finalAttrs: {
'';

passthru = {
updateScript = gnome.updateScript {
packageName = "gtk";
attrPath = "gtk3";
freeze = true;
hardcodeGsettingsPatch = makeHardcodeGsettingsPatch {
schemaIdToVariableMapping = {
"org.gtk.Settings.ColorChooser" = "gtk";
"org.gtk.Settings.EmojiChooser" = "gtk";
"org.gtk.Settings.FileChooser" = "gtk";
"org.gtk.Demo" = "gtk";
"org.gtk.exampleapp" = "gtk"; # Not actually installed.
};
inherit (finalAttrs) src;
preferDefaultSchemaSource = true;
};

updateScript =
let
updateSource = gnome.updateScript {
packageName = "gtk";
attrPath = "gtk3";
freeze = true;
};
updatePatch = _experimental-update-script-combinators.copyAttrOutputToFile "gtk3.hardcodeGsettingsPatch" ./patches/3.0-hardcode-gsettings.patch;
in
_experimental-update-script-combinators.sequence [
updateSource
updatePatch
];

tests.pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
};

Expand Down
Loading
Loading