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 similarity index 100% rename from test cases/frameworks/7 gnome/resources/data/res1.txt rename to test cases/frameworks/7 gnome/resources-data/res1.txt 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/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/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/main.c b/test cases/frameworks/7 gnome/resources/simple-main.c similarity index 89% rename from test cases/frameworks/7 gnome/resources/main.c rename to test cases/frameworks/7 gnome/resources/simple-main.c index b765468c3..3569901cb 100644 --- a/test cases/frameworks/7 gnome/resources/main.c +++ b/test cases/frameworks/7 gnome/resources/simple-main.c @@ -1,12 +1,12 @@ #include #include #include -#include"myresources.h" +#include"simple-resources.h" #define EXPECTED "This is a resource.\n" int main(int argc, char **argv) { - GResource *res = myres_get_resource(); + 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); 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 + +