diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 48dfb1152..e8d6beb77 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -504,19 +504,13 @@ class Backend(): libs.append(os.path.join(self.get_target_dir(t), f)) return libs - def eval_custom_target_command(self, target, absolute_paths=False): - if not absolute_paths: - ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output] - else: - ofilenames = [os.path.join(self.environment.get_build_dir(), self.get_target_dir(target), i) \ - for i in target.output] + def get_custom_target_sources(self, target): + ''' + Custom target sources can be of various object types; strings, File, + BuildTarget, even other CustomTargets. + Returns the path to them relative to the build root directory. + ''' srcs = [] - outdir = self.get_target_dir(target) - # Many external programs fail on empty arguments. - if outdir == '': - outdir = '.' - if absolute_paths: - outdir = os.path.join(self.environment.get_build_dir(), outdir) for i in target.get_sources(): if hasattr(i, 'held_object'): i = i.held_object @@ -530,9 +524,25 @@ class Backend(): fname = [os.path.join(self.get_target_private_dir(target), p) for p in i.get_outputs()] else: fname = [i.rel_to_builddir(self.build_to_src)] - if absolute_paths: - fname =[os.path.join(self.environment.get_build_dir(), f) for f in fname] + if target.absolute_paths: + fname = [os.path.join(self.environment.get_build_dir(), f) for f in fname] srcs += fname + return srcs + + def eval_custom_target_command(self, target, absolute_outputs=False): + # We only want the outputs to be absolute when using the VS backend + if not absolute_outputs: + ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output] + else: + ofilenames = [os.path.join(self.environment.get_build_dir(), self.get_target_dir(target), i) \ + for i in target.output] + srcs = self.get_custom_target_sources(target) + outdir = self.get_target_dir(target) + # Many external programs fail on empty arguments. + if outdir == '': + outdir = '.' + if target.absolute_paths: + outdir = os.path.join(self.environment.get_build_dir(), outdir) cmd = [] for i in target.command: if isinstance(i, build.Executable): @@ -545,9 +555,9 @@ class Backend(): i = os.path.join(self.get_target_dir(i), tmp) elif isinstance(i, mesonlib.File): i = i.rel_to_builddir(self.build_to_src) - if absolute_paths: + if target.absolute_paths: i = os.path.join(self.environment.get_build_dir(), i) - # FIXME: str types are blindly added and ignore the 'absolute_paths' argument + # FIXME: str types are blindly added ignoring 'target.absolute_paths' elif not isinstance(i, str): err_msg = 'Argument {0} is of unknown type {1}' raise RuntimeError(err_msg.format(str(i), str(type(i)))) @@ -593,7 +603,7 @@ class Backend(): ''.format(target.name, i) raise MesonException(msg) source = match.group(0) - if match.group(1) is None and not absolute_paths: + if match.group(1) is None and not target.absolute_paths: lead_dir = '' else: lead_dir = self.environment.get_build_dir() diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index acbf4a45b..332450fec 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -13,6 +13,7 @@ # limitations under the License. from . import backends +from .. import modules from .. import environment, mesonlib from .. import build from .. import mlog @@ -951,7 +952,7 @@ int dummy; else: srctype = othersgen # Duplicate outputs are disastrous - if f in srctype: + if f in srctype and srctype[f] is not gensrc: msg = 'Duplicate output {0!r} from {1!r} {2!r}; ' \ 'conflicts with {0!r} from {4!r} {3!r}' \ ''.format(f, type(gensrc).__name__, gensrc.name, @@ -1032,6 +1033,11 @@ int dummy; args += ['--pkg', d.name] elif isinstance(d, dependencies.ExternalLibrary): args += d.get_lang_args('vala') + # Detect gresources and add --gresources arguments for each + for (gres, gensrc) in other_src[1].items(): + if isinstance(gensrc, modules.GResourceTarget): + gres_xml, = self.get_custom_target_sources(gensrc) + args += ['--gresources=' + gres_xml] extra_args = [] for a in target.extra_args.get('vala', []): diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index d04345527..f3e9b4fe2 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -392,6 +392,9 @@ class Vs2010Backend(backends.Backend): root = self.create_basic_crap(target) action = ET.SubElement(root, 'ItemDefinitionGroup') customstep = ET.SubElement(action, 'CustomBuildStep') + # We need to always use absolute paths because our invocation is always + # from the target dir, not the build root. + target.absolute_paths = True (srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True) cmd_templ = '''"%s" '''*len(cmd) ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 106386cc6..1c3f4e803 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1218,7 +1218,7 @@ class CustomTarget: 'depfile' : True, } - def __init__(self, name, subdir, kwargs): + def __init__(self, name, subdir, kwargs, absolute_paths=False): self.name = name self.subdir = subdir self.dependencies = [] @@ -1227,6 +1227,8 @@ class CustomTarget: self.depfile = None self.process_kwargs(kwargs) self.extra_files = [] + # Whether to use absolute paths for all files on the commandline + self.absolute_paths = absolute_paths unknowns = [] for k in kwargs: if k not in CustomTarget.known_kwargs: diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 606719c42..73062014c 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -145,6 +145,8 @@ class PkgConfigDependency(Dependency): else: if not isinstance(self.version_reqs, (str, list)): raise DependencyException('Version argument must be string or list.') + if isinstance(self.version_reqs, str): + self.version_reqs = [self.version_reqs] (self.is_found, not_found, found) = \ version_compare_many(self.modversion, self.version_reqs) if not self.is_found: @@ -169,6 +171,11 @@ class PkgConfigDependency(Dependency): # Fetch the libraries and library paths needed for using this self._set_libs() + def __repr__(self): + s = '<{0} {1}: {2} {3}>' + return s.format(self.__class__.__name__, self.name, self.is_found, + self.version_reqs) + def _call_pkgbin(self, args): p, out = Popen_safe([self.pkgbin] + args, env=os.environ)[0:2] return (p.returncode, out.strip()) diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index e69de29bb..8986d7a5c 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -0,0 +1,34 @@ +from .. import build +from .. import dependencies + +_found_programs = {} + +def find_program(program_name, target_name): + if program_name in _found_programs: + return _found_programs[program_name] + program = dependencies.ExternalProgram(program_name) + if not program.found(): + m = "Target {!r} can't be generated as {!r} could not be found" + raise MesonException(m.format(target_name, program_name)) + _found_programs[program_name] = program + return program + +class GResourceTarget(build.CustomTarget): + def __init__(self, name, subdir, kwargs): + super().__init__(name, subdir, kwargs) + +class GResourceHeaderTarget(build.CustomTarget): + def __init__(self, name, subdir, kwargs): + super().__init__(name, subdir, kwargs) + +class GirTarget(build.CustomTarget): + def __init__(self, name, subdir, kwargs): + super().__init__(name, subdir, kwargs) + +class TypelibTarget(build.CustomTarget): + def __init__(self, name, subdir, kwargs): + super().__init__(name, subdir, kwargs) + +class VapiTarget(build.CustomTarget): + def __init__(self, name, subdir, kwargs): + super().__init__(name, subdir, kwargs) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 6d05b4e9d..e291c98c7 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -25,6 +25,7 @@ from .. import dependencies from .. import mlog from .. import mesonlib from .. import interpreter +from . import find_program, GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget # gresource compilation is broken due to the way # the resource compiler and Ninja clash about it @@ -45,19 +46,13 @@ def gir_has_extra_lib_arg(): _gir_has_extra_lib_arg = False try: - scanner_options = subprocess.check_output(['g-ir-scanner', '--help']).decode() - _gir_has_extra_lib_arg = '--extra-library' in scanner_options - except (FileNotFound, subprocess.CalledProcessError): + g_ir_scanner = find_program('g-ir-scanner', '').get_command() + opts = Popen_safe(g_ir_scanner + ['--help'], stderr=subprocess.STDOUT)[1] + _gir_has_extra_lib_arg = '--extra-library' in opts + except (MesonException, FileNotFoundError, subprocess.CalledProcessError): pass return _gir_has_extra_lib_arg -def find_program(program_name, target_name): - program = dependencies.ExternalProgram(program_name) - if not program.found(): - raise MesonException('%s can\'t be generated as %s could not be found' % ( - target_name, program_name)) - return program - class GnomeModule: @staticmethod @@ -161,7 +156,7 @@ can not be used with the current version of glib-compiled-resources, due to depfile = kwargs['output'] + '.d' kwargs['depfile'] = depfile kwargs['command'] = copy.copy(cmd) + ['--dependency-file', '@DEPFILE@'] - target_c = build.CustomTarget(name, state.subdir, kwargs) + target_c = GResourceTarget(name, state.subdir, kwargs) if gresource: # Only one target for .gresource files return [target_c] @@ -177,7 +172,7 @@ can not be used with the current version of glib-compiled-resources, due to h_kwargs['install'] = install_header h_kwargs['install_dir'] = kwargs.get('install_dir', state.environment.coredata.get_builtin_option('includedir')) - target_h = build.CustomTarget(args[0] + '_h', state.subdir, h_kwargs) + target_h = GResourceHeaderTarget(args[0] + '_h', state.subdir, h_kwargs) return [target_c, target_h] def _get_gresource_dependencies(self, state, input_file, source_dirs, dependencies): @@ -798,7 +793,7 @@ can not be used with the current version of glib-compiled-resources, due to install_header = False for arg, value in kwargs.items(): if arg == 'sources': - sources = [value] + sources + raise AssertionError("sources should've already been handled") elif arg == 'c_template': c_template = value if 'template' in kwargs: @@ -882,7 +877,8 @@ can not be used with the current version of glib-compiled-resources, due to 'command': cmd } custom_kwargs.update(kwargs) - return build.CustomTarget(output, state.subdir, custom_kwargs) + return build.CustomTarget(output, state.subdir, custom_kwargs, + absolute_paths=True) def genmarshal(self, state, args, kwargs): if len(args) != 1: @@ -1087,15 +1083,3 @@ can not be used with the current version of glib-compiled-resources, due to def initialize(): return GnomeModule() - -class GirTarget(build.CustomTarget): - def __init__(self, name, subdir, kwargs): - super().__init__(name, subdir, kwargs) - -class TypelibTarget(build.CustomTarget): - def __init__(self, name, subdir, kwargs): - super().__init__(name, subdir, kwargs) - -class VapiTarget(build.CustomTarget): - def __init__(self, name, subdir, kwargs): - super().__init__(name, subdir, kwargs) diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py index ab285fb6b..63dfef8bf 100644 --- a/mesonbuild/modules/qt4.py +++ b/mesonbuild/modules/qt4.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, subprocess +import os from .. import mlog from .. import build from ..mesonlib import MesonException, Popen_safe diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index dca1ad61a..a0f79aa73 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -19,7 +19,7 @@ from .. import build from .. import compilers import datetime from .. import mlog -from ..modules import gnome +from . import GirTarget, TypelibTarget import os @@ -65,9 +65,9 @@ class RPMModule: to_delete.add('%%{buildroot}%%{_libdir}/%s' % target.get_filename()) mlog.warning('removing', mlog.bold(target.get_filename()), 'from package because packaging static libs not recommended') - elif isinstance(target, gnome.GirTarget) and target.should_install(): + elif isinstance(target, GirTarget) and target.should_install(): files_devel.add('%%{_datadir}/gir-1.0/%s' % target.get_filename()[0]) - elif isinstance(target, gnome.TypelibTarget) and target.should_install(): + elif isinstance(target, TypelibTarget) and target.should_install(): files.add('%%{_libdir}/girepository-1.0/%s' % target.get_filename()[0]) for header in state.headers: if len(header.get_install_subdir()) > 0: diff --git a/test cases/vala/14 target glib version and gresources/gres/meson.build b/test cases/vala/14 target glib version and gresources/gres/meson.build new file mode 100644 index 000000000..f222a0095 --- /dev/null +++ b/test cases/vala/14 target glib version and gresources/gres/meson.build @@ -0,0 +1,3 @@ +res = gnome.compile_resources('testui', + 'test-resources.xml', + source_dir : '.') diff --git a/test cases/vala/14 target glib version and gresources/gres/test-resources.xml b/test cases/vala/14 target glib version and gresources/gres/test-resources.xml new file mode 100644 index 000000000..4e8a73f61 --- /dev/null +++ b/test cases/vala/14 target glib version and gresources/gres/test-resources.xml @@ -0,0 +1,6 @@ + + + + test.ui + + diff --git a/test cases/vala/14 target glib version and gresources/gres/test.ui b/test cases/vala/14 target glib version and gresources/gres/test.ui new file mode 100644 index 000000000..f90d941ff --- /dev/null +++ b/test cases/vala/14 target glib version and gresources/gres/test.ui @@ -0,0 +1,19 @@ + + + + + diff --git a/test cases/vala/14 target glib version and gresources/meson.build b/test cases/vala/14 target glib version and gresources/meson.build new file mode 100644 index 000000000..dee26278d --- /dev/null +++ b/test cases/vala/14 target glib version and gresources/meson.build @@ -0,0 +1,12 @@ +project('test glib target version and gresources', 'c', 'vala') + +gnome = import('gnome') + +glib = dependency('glib-2.0', version : '>=2.38') +gtk = dependency('gtk+-3.0') + +subdir('gres') + +e = executable('gtktemplate', 'test.vala', res, dependencies : [glib, gtk]) +# No X on the CI, so disable this for now +#test('test-target-glib', e) diff --git a/test cases/vala/14 target glib version and gresources/test.vala b/test cases/vala/14 target glib version and gresources/test.vala new file mode 100644 index 000000000..79ef47db0 --- /dev/null +++ b/test cases/vala/14 target glib version and gresources/test.vala @@ -0,0 +1,37 @@ +using Gtk; +using GLib; + +[GtkTemplate (ui = "/org/Meson/test.ui")] +public class TestWidget : Box { + public string text { + get { return entry.text; } + set { entry.text = value; } + } + + [GtkChild] + private Entry entry; + + public TestWidget (string text) { + this.text = text; + } +} + +void main(string[] args) { + Gtk.init (ref args); + var win = new Window(); + win.destroy.connect (Gtk.main_quit); + + var widget = new TestWidget ("SOME TEXT HERE"); + + win.add (widget); + win.show_all (); + + /* Exit immediately */ + Timeout.add_full (Priority.DEFAULT_IDLE, 1, () => + { + Gtk.main_quit(); + return false; + }); + + Gtk.main (); +} diff --git a/test cases/vala/15 static vapi in source tree/meson.build b/test cases/vala/15 static vapi in source tree/meson.build new file mode 100644 index 000000000..44bea484a --- /dev/null +++ b/test cases/vala/15 static vapi in source tree/meson.build @@ -0,0 +1,13 @@ +project('static vapi', 'c', 'vala') + +glib = dependency('glib-2.0') + +conf = configuration_data() +conf.set_quoted('VERSION', '1.0.0') +config_h = configure_file(output : 'config.h', + configuration : conf) + +e = executable('static-vapi', 'vapi/config.vapi', 'test.vala', + dependencies : glib) + +test('test-config', e) diff --git a/test cases/vala/15 static vapi in source tree/test.vala b/test cases/vala/15 static vapi in source tree/test.vala new file mode 100644 index 000000000..06f2d593c --- /dev/null +++ b/test cases/vala/15 static vapi in source tree/test.vala @@ -0,0 +1,6 @@ +using GLib; +using Config; + +public int main (string[] args) { + return GLib.strcmp(VERSION, "1.0.0"); +} diff --git a/test cases/vala/15 static vapi in source tree/vapi/config.vapi b/test cases/vala/15 static vapi in source tree/vapi/config.vapi new file mode 100644 index 000000000..71bcc6ea4 --- /dev/null +++ b/test cases/vala/15 static vapi in source tree/vapi/config.vapi @@ -0,0 +1,4 @@ +[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")] +namespace Config { + public const string VERSION; +} diff --git a/test cases/vala/8 generated sources/dependency-generated/enum-types.c.template b/test cases/vala/8 generated sources/dependency-generated/enum-types.c.template new file mode 100644 index 000000000..5ecdd2df2 --- /dev/null +++ b/test cases/vala/8 generated sources/dependency-generated/enum-types.c.template @@ -0,0 +1,43 @@ +/*** BEGIN file-header ***/ + +#include "enum-types.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +#include "@filename@" +/*** END file-production ***/ + + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +/*** END value-tail ***/ + +/*** BEGIN file-tail ***/ + +/*** END file-tail ***/ diff --git a/test cases/vala/8 generated sources/dependency-generated/enum-types.h.template b/test cases/vala/8 generated sources/dependency-generated/enum-types.h.template new file mode 100644 index 000000000..e5478ab43 --- /dev/null +++ b/test cases/vala/8 generated sources/dependency-generated/enum-types.h.template @@ -0,0 +1,26 @@ +/*** BEGIN file-header ***/ + +#ifndef __EXAMPLE_ENUMS_TYPES_H__ +#define __EXAMPLE_ENUMS_TYPES_H__ + +#include +#include "enums.h" + +G_BEGIN_DECLS +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type (void) G_GNUC_CONST; +#define EXAMPLE_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* __EXAMPLE_ENUMS_TYPES_H__ */ +/*** END file-tail ***/ diff --git a/test cases/vala/8 generated sources/dependency-generated/enums.h b/test cases/vala/8 generated sources/dependency-generated/enums.h new file mode 100644 index 000000000..3fb621eb8 --- /dev/null +++ b/test cases/vala/8 generated sources/dependency-generated/enums.h @@ -0,0 +1,15 @@ +#ifndef __EXAMPLE_ENUMS_H__ +#define __EXAMPLE_ENUMS_H__ + +G_BEGIN_DECLS + +typedef enum { + EXAMPLE_VERBOSITY_ERRORS, + EXAMPLE_VERBOSITY_MINIMAL, + EXAMPLE_VERBOSITY_DETAILED, + EXAMPLE_VERBOSITY_DEBUG, +} ExampleVerbosity; + +G_END_DECLS + +#endif /* __EXAMPLE_ENUMS_H__ */ diff --git a/test cases/vala/8 generated sources/dependency-generated/lib.vala b/test cases/vala/8 generated sources/dependency-generated/lib.vala new file mode 100644 index 000000000..3fcbb7fa7 --- /dev/null +++ b/test cases/vala/8 generated sources/dependency-generated/lib.vala @@ -0,0 +1,3 @@ +int whatever() { + return 0; +} diff --git a/test cases/vala/8 generated sources/dependency-generated/main.vala b/test cases/vala/8 generated sources/dependency-generated/main.vala new file mode 100644 index 000000000..33c14ce1d --- /dev/null +++ b/test cases/vala/8 generated sources/dependency-generated/main.vala @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/test cases/vala/8 generated sources/dependency-generated/meson.build b/test cases/vala/8 generated sources/dependency-generated/meson.build new file mode 100644 index 000000000..a1895b4f7 --- /dev/null +++ b/test cases/vala/8 generated sources/dependency-generated/meson.build @@ -0,0 +1,44 @@ +# Test that dependencies with their own generated sources don't +# confuse the Vala build instruction generator. + +# Test case for https://github.com/mesonbuild/meson/issues/1084 + +gnome = import('gnome') + +gobject = dependency('gobject-2.0') + +enums = gnome.mkenums('enum-types', + sources: 'enums.h', + c_template: 'enum-types.c.template', + h_template: 'enum-types.h.template', +) + +libcommon = library('common', + enums[0], enums[1], + dependencies: gobject) + +common_dep = declare_dependency( + # This is required so that whoever depends on this also depends + # on the generated header; that won't happen implicitly. + # See: https://github.com/mesonbuild/meson/issues/1084 + sources: enums[1], + link_with: libcommon, +) + +libplover_vala = library('plover', + 'lib.vala', + dependencies: [common_dep, gobject] +) + +plover_dep = declare_dependency( + link_with: libplover_vala, + dependencies: common_dep +) + +vala_prog = executable('hello', + 'main.vala', + link_with: libplover_vala, + # There's no need to specify common_dep here since plover_dep pulls it + # in, but it should be harmless to do so. + dependencies: [common_dep, plover_dep, gobject] +) diff --git a/test cases/vala/8 generated sources/dependency-generated/null.c b/test cases/vala/8 generated sources/dependency-generated/null.c new file mode 100644 index 000000000..8337712ea --- /dev/null +++ b/test cases/vala/8 generated sources/dependency-generated/null.c @@ -0,0 +1 @@ +// diff --git a/test cases/vala/8 generated sources/meson.build b/test cases/vala/8 generated sources/meson.build index 6e03404b7..277c943c1 100644 --- a/test cases/vala/8 generated sources/meson.build +++ b/test cases/vala/8 generated sources/meson.build @@ -6,3 +6,4 @@ cd.set('x', 'y') subdir('src') subdir('tools') subdir('onlygen') +subdir('dependency-generated')