From 25f13067c2aae06748654b9a48869c0da17ea07d Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Sat, 17 Sep 2016 13:23:45 +0100 Subject: gnome: allow use of generated files with compile_resources() This commit adds a 'dependencies' keyword to the gnome.compile_resources() function, which allows your resource blob to depend on files generated at build-time from custom_target() or configure_file() targets. My current use case for this is source data that gets processed with Gettext translation tools before being compiled into the resource blob. This feature only works with GLib version 2.48.2 and above. So the compile_resources() function now detects GLib version and raises an error if the version of GLib being used is too old. The compile_resources() test case is now split into two, so that the existing one can continue to run on systems with old GLib versions (such as Ubuntu Xenial, which the automated tests on travisci.org use), but where new enough GLib is available we also test generating gresource content. The existing warning about glib-compile-resources is now only printed if GLib version is older than 2.50.0 because is fixed in the 2.50.0 release. --- mesonbuild/modules/gnome.py | 97 ++++++++++++++++++++-- test cases/frameworks/7 gnome/meson.build | 1 + .../frameworks/7 gnome/resources-data/meson.build | 16 ++++ .../frameworks/7 gnome/resources-data/res1.txt | 1 + .../frameworks/7 gnome/resources-data/res3.txt.in | 1 + .../7 gnome/resources-data/subdir/meson.build | 8 ++ .../7 gnome/resources-data/subdir/res2.txt | 1 + .../7 gnome/resources-data/subdir/res4.txt.in | 1 + .../frameworks/7 gnome/resources/data/res1.txt | 1 - .../frameworks/7 gnome/resources/generated-main.c | 27 ++++++ .../7 gnome/resources/generated.gresource.xml | 9 ++ test cases/frameworks/7 gnome/resources/main.c | 27 ------ .../frameworks/7 gnome/resources/meson.build | 34 ++++++-- .../7 gnome/resources/myresource.gresource.xml | 3 + .../frameworks/7 gnome/resources/simple-main.c | 27 ++++++ .../7 gnome/resources/simple.gresource.xml | 7 ++ 16 files changed, 222 insertions(+), 39 deletions(-) create mode 100644 test cases/frameworks/7 gnome/resources-data/meson.build create mode 100644 test cases/frameworks/7 gnome/resources-data/res1.txt create mode 100644 test cases/frameworks/7 gnome/resources-data/res3.txt.in create mode 100644 test cases/frameworks/7 gnome/resources-data/subdir/meson.build create mode 100644 test cases/frameworks/7 gnome/resources-data/subdir/res2.txt create mode 100644 test cases/frameworks/7 gnome/resources-data/subdir/res4.txt.in delete mode 100644 test cases/frameworks/7 gnome/resources/data/res1.txt create mode 100644 test cases/frameworks/7 gnome/resources/generated-main.c create mode 100644 test cases/frameworks/7 gnome/resources/generated.gresource.xml delete mode 100644 test cases/frameworks/7 gnome/resources/main.c create mode 100644 test cases/frameworks/7 gnome/resources/simple-main.c create mode 100644 test cases/frameworks/7 gnome/resources/simple.gresource.xml diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index be111ea7d..80bc9b6b2 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -22,22 +22,36 @@ from ..mesonlib import MesonException from .. import dependencies from .. import mlog from .. import mesonlib +from .. import interpreter +native_glib_version = None girwarning_printed = False gresource_warning_printed = False class GnomeModule: + def get_native_glib_version(self, state): + global native_glib_version + if native_glib_version is None: + glib_dep = dependencies.PkgConfigDependency( + 'glib-2.0', state.environment, {'native': True}) + native_glib_version = glib_dep.get_modversion() + return native_glib_version + def __print_gresources_warning(self): global gresource_warning_printed if not gresource_warning_printed: - mlog.log('Warning, glib compiled dependencies will not work reliably until this upstream issue is fixed:', + mlog.log('Warning, GLib compiled dependencies do not work fully ' + 'with versions of GLib older than 2.50.0.\n' + 'See the following upstream issue:', mlog.bold('https://bugzilla.gnome.org/show_bug.cgi?id=745754')) gresource_warning_printed = True return [] def compile_resources(self, state, args, kwargs): - self.__print_gresources_warning() + if mesonlib.version_compare(self.get_native_glib_version(state), + '< 2.50.0'): + self.__print_gresources_warning() cmd = ['glib-compile-resources', '@INPUT@'] @@ -51,6 +65,19 @@ class GnomeModule: if len(args) < 2: raise MesonException('Not enough arguments; The name of the resource and the path to the XML file are required') + dependencies = kwargs.pop('dependencies', []) + if not isinstance(dependencies, list): + dependencies = [dependencies] + + if mesonlib.version_compare(self.get_native_glib_version(state), + '< 2.48.2'): + if len(dependencies) > 0: + raise MesonException( + 'The "dependencies" argument of gnome.compile_resources() ' + 'can only be used with glib-compile-resources version ' + '2.48.2 or newer, due to ' + '') + ifile = args[1] if isinstance(ifile, mesonlib.File): ifile = os.path.join(ifile.subdir, ifile.fname) @@ -58,12 +85,20 @@ class GnomeModule: ifile = os.path.join(state.subdir, ifile) else: raise RuntimeError('Unreachable code.') - kwargs['depend_files'] = self.get_gresource_dependencies(state, ifile, source_dirs) + + kwargs['depend_files'] = self.get_gresource_dependencies( + state, ifile, source_dirs, dependencies) for source_dir in source_dirs: sourcedir = os.path.join(state.build_to_src, state.subdir, source_dir) cmd += ['--sourcedir', sourcedir] + if len(dependencies) > 0: + # Add the build variant of each sourcedir if we have any + # generated dependencies. + sourcedir = os.path.join(state.subdir, source_dir) + cmd += ['--sourcedir', sourcedir] + if 'c_name' in kwargs: cmd += ['--c-name', kwargs.pop('c_name')] cmd += ['--generate', '--target', '@OUTPUT@'] @@ -78,9 +113,17 @@ class GnomeModule: target_h = build.CustomTarget(args[0] + '_h', state.subdir, kwargs) return [target_c, target_h] - def get_gresource_dependencies(self, state, input_file, source_dirs): + def get_gresource_dependencies(self, state, input_file, source_dirs, dependencies): self.__print_gresources_warning() + for dep in dependencies: + if not isinstance(dep, interpreter.CustomTargetHolder) and not \ + isinstance(dep, mesonlib.File): + raise MesonException( + 'Unexpected dependency type for gnome.compile_resources() ' + '"dependencies" argument. Please pass the output of ' + 'custom_target() or configure_file().') + cmd = ['glib-compile-resources', input_file, '--generate-dependencies'] @@ -95,7 +138,51 @@ class GnomeModule: mlog.log(mlog.bold('Warning:'), 'glib-compile-resources has failed to get the dependencies for {}'.format(cmd[1])) raise subprocess.CalledProcessError(pc.returncode, cmd) - return stdout.split('\n')[:-1] + dep_files = stdout.split('\n')[:-1] + + # In generate-dependencies mode, glib-compile-resources doesn't raise + # an error for missing resources but instead prints whatever filename + # was listed in the input file. That's good because it means we can + # handle resource files that get generated as part of the build, as + # follows. + # + # If there are multiple generated resource files with the same basename + # then this code will get confused. + + def exists_in_srcdir(f): + return os.path.exists(os.path.join(state.environment.get_source_dir(), f)) + missing_dep_files = [f for f in dep_files if not exists_in_srcdir(f)] + + for missing in missing_dep_files: + found = False + missing_basename = os.path.basename(missing) + + for dep in dependencies: + if isinstance(dep, mesonlib.File): + if dep.fname == missing_basename: + found = True + dep_files.remove(missing) + dep_files.append(dep) + break + elif isinstance(dep, interpreter.CustomTargetHolder): + if dep.held_object.get_basename() == missing_basename: + found = True + dep_files.remove(missing) + dep_files.append( + mesonlib.File( + is_built=True, + subdir=dep.held_object.get_subdir(), + fname=dep.held_object.get_basename())) + break + + if not found: + raise MesonException( + 'Resource "%s" listed in "%s" was not found. If this is a ' + 'generated file, pass the target that generates it to ' + 'gnome.compile_resources() using the "dependencies" ' + 'keyword argument.' % (missing, input_file)) + + return dep_files def get_link_args(self, state, lib, depends=None): link_command = ['-l%s' % lib.name] diff --git a/test cases/frameworks/7 gnome/meson.build b/test cases/frameworks/7 gnome/meson.build index 2c2e953d9..a771e71b7 100644 --- a/test cases/frameworks/7 gnome/meson.build +++ b/test cases/frameworks/7 gnome/meson.build @@ -9,6 +9,7 @@ gir = dependency('gobject-introspection-1.0') gmod = dependency('gmodule-2.0') add_global_arguments('-DMESON_TEST', language : 'c') +subdir('resources-data') subdir('resources') subdir('gir') subdir('schemas') diff --git a/test cases/frameworks/7 gnome/resources-data/meson.build b/test cases/frameworks/7 gnome/resources-data/meson.build new file mode 100644 index 000000000..6343c0ea9 --- /dev/null +++ b/test cases/frameworks/7 gnome/resources-data/meson.build @@ -0,0 +1,16 @@ +subdir('subdir') + +fake_generator_script = ''' +import os, sys +assert os.path.exists(sys.argv[1]), "File %s not found" % sys.argv[1] +print("This is a generated resource") +''' + +# Generate file res3.txt from file res3.txt.in. This is then included +# in a GResource file, driven by resources/meson.build. +res3_txt = custom_target('res3.txt', + input: 'res3.txt.in', + output: 'res3.txt', + command: ['python3', '-c', fake_generator_script, '@INPUT@'], + capture: true, +) diff --git a/test cases/frameworks/7 gnome/resources-data/res1.txt b/test cases/frameworks/7 gnome/resources-data/res1.txt new file mode 100644 index 000000000..e10afea83 --- /dev/null +++ b/test cases/frameworks/7 gnome/resources-data/res1.txt @@ -0,0 +1 @@ +This is a resource. diff --git a/test cases/frameworks/7 gnome/resources-data/res3.txt.in b/test cases/frameworks/7 gnome/resources-data/res3.txt.in new file mode 100644 index 000000000..077a8e301 --- /dev/null +++ b/test cases/frameworks/7 gnome/resources-data/res3.txt.in @@ -0,0 +1 @@ +This content is ignored, but Meson doesn't need to know that. diff --git a/test cases/frameworks/7 gnome/resources-data/subdir/meson.build b/test cases/frameworks/7 gnome/resources-data/subdir/meson.build new file mode 100644 index 000000000..b41300f6d --- /dev/null +++ b/test cases/frameworks/7 gnome/resources-data/subdir/meson.build @@ -0,0 +1,8 @@ +cdata = configuration_data() +cdata.set('NOISE', 'BARK') + +res4_txt = configure_file( + input: 'res4.txt.in', + output: 'res4.txt', + configuration: cdata +) diff --git a/test cases/frameworks/7 gnome/resources-data/subdir/res2.txt b/test cases/frameworks/7 gnome/resources-data/subdir/res2.txt new file mode 100644 index 000000000..d297899bb --- /dev/null +++ b/test cases/frameworks/7 gnome/resources-data/subdir/res2.txt @@ -0,0 +1 @@ +This is a resource in a subdirectory. diff --git a/test cases/frameworks/7 gnome/resources-data/subdir/res4.txt.in b/test cases/frameworks/7 gnome/resources-data/subdir/res4.txt.in new file mode 100644 index 000000000..c0ec6f2a2 --- /dev/null +++ b/test cases/frameworks/7 gnome/resources-data/subdir/res4.txt.in @@ -0,0 +1 @@ +@NOISE@ @NOISE@ @NOISE@ diff --git a/test cases/frameworks/7 gnome/resources/data/res1.txt b/test cases/frameworks/7 gnome/resources/data/res1.txt deleted file mode 100644 index e10afea83..000000000 --- a/test cases/frameworks/7 gnome/resources/data/res1.txt +++ /dev/null @@ -1 +0,0 @@ -This is a resource. diff --git a/test cases/frameworks/7 gnome/resources/generated-main.c b/test cases/frameworks/7 gnome/resources/generated-main.c new file mode 100644 index 000000000..fc9efbdae --- /dev/null +++ b/test cases/frameworks/7 gnome/resources/generated-main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include"generated-resources.h" + +#define EXPECTED "This is a generated resource.\n" + +int main(int argc, char **argv) { + GResource *res = generated_resources_get_resource(); + GError *err = NULL; + GBytes *data = g_resources_lookup_data("/com/example/myprog/res3.txt", + G_RESOURCE_LOOKUP_FLAGS_NONE, &err); + + if(data == NULL) { + fprintf(stderr, "Data lookup failed: %s\n", err->message); + return 1; + } + if(strcmp(g_bytes_get_data(data, NULL), EXPECTED) != 0) { + fprintf(stderr, "Resource contents are wrong:\n %s\n", + (const char*)g_bytes_get_data(data, NULL)); + return 1; + } + fprintf(stdout, "All ok.\n"); + g_bytes_unref(data); + g_resource_unref(res); + return 0; +} diff --git a/test cases/frameworks/7 gnome/resources/generated.gresource.xml b/test cases/frameworks/7 gnome/resources/generated.gresource.xml new file mode 100644 index 000000000..7a242d702 --- /dev/null +++ b/test cases/frameworks/7 gnome/resources/generated.gresource.xml @@ -0,0 +1,9 @@ + + + + res1.txt + subdir/res2.txt + res3.txt + subdir/res4.txt + + diff --git a/test cases/frameworks/7 gnome/resources/main.c b/test cases/frameworks/7 gnome/resources/main.c deleted file mode 100644 index b765468c3..000000000 --- a/test cases/frameworks/7 gnome/resources/main.c +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include -#include -#include"myresources.h" - -#define EXPECTED "This is a resource.\n" - -int main(int argc, char **argv) { - GResource *res = myres_get_resource(); - GError *err = NULL; - GBytes *data = g_resources_lookup_data("/com/example/myprog/res1.txt", - G_RESOURCE_LOOKUP_FLAGS_NONE, &err); - - if(data == NULL) { - fprintf(stderr, "Data lookup failed: %s\n", err->message); - return 1; - } - if(strcmp(g_bytes_get_data(data, NULL), EXPECTED) != 0) { - fprintf(stderr, "Resource contents are wrong:\n %s\n", - (const char*)g_bytes_get_data(data, NULL)); - return 1; - } - fprintf(stdout, "All ok.\n"); - g_bytes_unref(data); - g_resource_unref(res); - return 0; -} diff --git a/test cases/frameworks/7 gnome/resources/meson.build b/test cases/frameworks/7 gnome/resources/meson.build index 937dc478d..5762a8c6b 100644 --- a/test cases/frameworks/7 gnome/resources/meson.build +++ b/test cases/frameworks/7 gnome/resources/meson.build @@ -1,7 +1,29 @@ -myres = gnome.compile_resources('myresources', 'myresource.gresource.xml', -source_dir : 'data', -c_name : 'myres') +# There are two tests here, because the 2nd one depends on a version of +# GLib (2.48.2) that is very recent at the time of writing. -resexe = executable('resprog', 'main.c', myres, -dependencies : gio) -test('resource test', resexe) +simple_resources = gnome.compile_resources('simple-resources', + 'simple.gresource.xml', + source_dir : '../resources-data', + c_name : 'simple_resources') + +simple_res_exe = executable('simple-resources-test', + 'simple-main.c', simple_resources, + dependencies: gio) +test('simple resource test', simple_res_exe) + +if glib.version() >= '2.48.2' + # This test cannot pass if GLib version is older than 2.48.2. + # Meson will raise an error if the user tries to use the 'dependencies' + # argument and the version of GLib is too old for generated resource + # dependencies to work correctly. + generated_resources = gnome.compile_resources('generated-resources', + 'generated.gresource.xml', + source_dir : '../resources-data', + c_name : 'generated_resources', + dependencies : [res3_txt, res4_txt]) + + generated_res_exe = executable('generated-resources-test', + 'generated-main.c', generated_resources, + dependencies: gio) + test('generated resource test', generated_res_exe) +endif diff --git a/test cases/frameworks/7 gnome/resources/myresource.gresource.xml b/test cases/frameworks/7 gnome/resources/myresource.gresource.xml index b44c8798b..7a242d702 100644 --- a/test cases/frameworks/7 gnome/resources/myresource.gresource.xml +++ b/test cases/frameworks/7 gnome/resources/myresource.gresource.xml @@ -2,5 +2,8 @@ res1.txt + subdir/res2.txt + res3.txt + subdir/res4.txt diff --git a/test cases/frameworks/7 gnome/resources/simple-main.c b/test cases/frameworks/7 gnome/resources/simple-main.c new file mode 100644 index 000000000..3569901cb --- /dev/null +++ b/test cases/frameworks/7 gnome/resources/simple-main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include"simple-resources.h" + +#define EXPECTED "This is a resource.\n" + +int main(int argc, char **argv) { + GResource *res = simple_resources_get_resource(); + GError *err = NULL; + GBytes *data = g_resources_lookup_data("/com/example/myprog/res1.txt", + G_RESOURCE_LOOKUP_FLAGS_NONE, &err); + + if(data == NULL) { + fprintf(stderr, "Data lookup failed: %s\n", err->message); + return 1; + } + if(strcmp(g_bytes_get_data(data, NULL), EXPECTED) != 0) { + fprintf(stderr, "Resource contents are wrong:\n %s\n", + (const char*)g_bytes_get_data(data, NULL)); + return 1; + } + fprintf(stdout, "All ok.\n"); + g_bytes_unref(data); + g_resource_unref(res); + return 0; +} diff --git a/test cases/frameworks/7 gnome/resources/simple.gresource.xml b/test cases/frameworks/7 gnome/resources/simple.gresource.xml new file mode 100644 index 000000000..6e5591051 --- /dev/null +++ b/test cases/frameworks/7 gnome/resources/simple.gresource.xml @@ -0,0 +1,7 @@ + + + + res1.txt + subdir/res2.txt + + -- cgit v1.3