diff options
-rw-r--r-- | docs/markdown/Dependencies.md | 28 | ||||
-rw-r--r-- | docs/markdown/Reference-manual.md | 4 | ||||
-rw-r--r-- | docs/markdown/snippets/config-tool-variable-method.md | 11 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 156 | ||||
-rw-r--r-- | mesonbuild/dependencies/dev.py | 105 | ||||
-rw-r--r-- | mesonbuild/dependencies/misc.py | 109 | ||||
-rw-r--r-- | mesonbuild/dependencies/ui.py | 182 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 10 | ||||
-rwxr-xr-x | run_unittests.py | 2 | ||||
-rw-r--r-- | test cases/common/165 config tool variable/meson.build | 31 | ||||
-rw-r--r-- | test cases/frameworks/16 sdl2/meson.build | 5 | ||||
-rw-r--r-- | test cases/frameworks/19 pcap/meson.build | 4 | ||||
-rw-r--r-- | test cases/frameworks/20 cups/meson.build | 5 | ||||
-rw-r--r-- | test cases/frameworks/21 libwmf/meson.build | 7 |
14 files changed, 376 insertions, 283 deletions
diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 8e780d6..dbd21aa 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -178,32 +178,22 @@ the list of sources for the target. The `modules` keyword of `dependency` works just like it does with Boost. It tells which subparts of Qt the program uses. -## Pcap +## Dependencies using config tools -The pcap library does not ship with pkg-config at the time or writing -but instead it has its own `pcap-config` util. Meson will use it -automatically: +CUPS, LLVM, PCAP, WxWidgets, libwmf, and GnuStep either do not provide +pkg-config modules or additionally can be detected via a config tool +(cups-config, llvm-config, etc). Meson has native support for these tools, and +then can be found like other dependencies: ```meson pcap_dep = dependency('pcap', version : '>=1.0') -``` - -## CUPS - -The cups library does not ship with pkg-config at the time or writing -but instead it has its own `cups-config` util. Meson will use it -automatically: - -```meson cups_dep = dependency('cups', version : '>=1.4') +llvm_dep = dependency('llvm', version : '>=4.0') ``` -## LibWMF - -The libwmf library does not ship with pkg-config at the time or writing -but instead it has its own `libwmf-config` util. Meson will use it -automatically: +Some of these tools (like wmf and cups) provide both pkg-config and config +tools support. You can force one or another via the method keyword: ```meson -libwmf_dep = dependency('libwmf', version : '>=0.2.8') +wmf_dep = dependency('wmf', method : 'config-tool') ``` diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 0f00203..424ab58 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1614,6 +1614,10 @@ an external dependency with the following methods: pkg-config variable specified, or, if invoked on a non pkg-config dependency, error out + - `get_configtool_variable(varname)` (*Added 0.44.0*) will get the + command line argument from the config tool (with `--` prepended), or, + if invoked on a non config-tool dependency, error out. + - `type_name()` which returns a string describing the type of the dependency, the most common values are `internal` for deps created with `declare_dependencies` and `pkgconfig` for system dependencies diff --git a/docs/markdown/snippets/config-tool-variable-method.md b/docs/markdown/snippets/config-tool-variable-method.md new file mode 100644 index 0000000..7725982 --- /dev/null +++ b/docs/markdown/snippets/config-tool-variable-method.md @@ -0,0 +1,11 @@ +# Config-Tool based dependencies gained a method to get arbitrary options + +A number of dependencies (CUPS, LLVM, pcap, WxWidgets, GnuStep) use a config +tool instead of pkg-config. As of this version they now have a +`get_configtool_variable` method, which is analogous to the +`get_pkgconfig_variable` for pkg config. + +```meson +dep_llvm = dependency('LLVM') +llvm_inc_dir = dep_llvm.get_configtool_variable('includedir') +``` diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 15b47bc..682182c 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -20,11 +20,14 @@ import sys import stat import shlex import shutil +import textwrap from enum import Enum from .. import mlog from .. import mesonlib -from ..mesonlib import MesonException, Popen_safe, version_compare_many, listify +from ..mesonlib import ( + MesonException, Popen_safe, version_compare_many, version_compare, listify +) # These must be defined in this file to avoid cyclical references. @@ -43,18 +46,17 @@ class DependencyMethods(Enum): QMAKE = 'qmake' # Just specify the standard link arguments, assuming the operating system provides the library. SYSTEM = 'system' - # Detect using sdl2-config - SDLCONFIG = 'sdlconfig' - # Detect using pcap-config - PCAPCONFIG = 'pcap-config' - # Detect using cups-config - CUPSCONFIG = 'cups-config' - # Detect using libwmf-config - LIBWMFCONFIG = 'libwmf-config' # This is only supported on OSX - search the frameworks directory by name. EXTRAFRAMEWORK = 'extraframework' # Detect using the sysconfig module. SYSCONFIG = 'sysconfig' + # Specify using a "program"-config style tool + CONFIG_TOOL = 'config-tool' + # For backewards compatibility + SDLCONFIG = 'sdlconfig' + CUPSCONFIG = 'cups-config' + PCAPCONFIG = 'pcap-config' + LIBWMFCONFIG = 'libwmf-config' class Dependency: @@ -72,6 +74,16 @@ class Dependency: raise DependencyException('method {!r} is invalid'.format(method)) method = DependencyMethods(method) + # This sets per-too config methods which are deprecated to to the new + # generic CONFIG_TOOL value. + if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG, + DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]: + mlog.warning(textwrap.dedent("""\ + Configuration method {} has been deprecated in favor of + 'config-tool'. This will be removed in a future version of + meson.""".format(method))) + method = DependencyMethods.CONFIG_TOOL + # Set the detection method. If the method is set to auto, use any available method. # If method is set to a specific string, allow only that detection method. if method == DependencyMethods.AUTO: @@ -82,7 +94,7 @@ class Dependency: raise DependencyException( 'Unsupported detection method: {}, allowed methods are {}'.format( method.value, - mlog.format_list(map(lambda x: x.value, [DependencyMethods.AUTO] + self.get_methods())))) + mlog.format_list([x.value for x in [DependencyMethods.AUTO] + self.get_methods()]))) def __repr__(self): s = '<{0} {1}: {2}>' @@ -120,6 +132,9 @@ class Dependency: def get_pkgconfig_variable(self, variable_name): raise NotImplementedError('{!r} is not a pkgconfig dependency'.format(self.name)) + def get_configtool_variable(self, variable_name): + raise NotImplementedError('{!r} is not a config-tool dependency'.format(self.name)) + class InternalDependency(Dependency): def __init__(self, version, incdirs, compile_args, link_args, libraries, sources, ext_deps): @@ -167,6 +182,127 @@ class ExternalDependency(Dependency): return self.compiler +class ConfigToolDependency(ExternalDependency): + + """Class representing dependencies found using a config tool.""" + + tools = None + tool_name = None + + def __init__(self, name, environment, language, kwargs): + super().__init__('config-tool', environment, language, kwargs) + self.name = name + self.tools = listify(kwargs.get('tools', self.tools)) + + req_version = kwargs.get('version', None) + tool, version = self.find_config(req_version) + self.config = tool + self.is_found = self.report_config(version, req_version) + if not self.is_found: + self.config = None + return + self.version = version + + @classmethod + def factory(cls, name, environment, language, kwargs, tools, tool_name): + """Constructor for use in dependencies that can be found multiple ways. + + In addition to the standard constructor values, this constructor sets + the tool_name and tools values of the instance. + """ + # This deserves some explanation, because metaprogramming is hard. + # This uses type() to create a dynamic subclass of ConfigToolDependency + # with the tools and tool_name class attributes set, this class is then + # instantiated and returned. The reduce function (method) is also + # attached, since python's pickle module won't be able to do anything + # with this dynamically generated class otherwise. + def reduce(_): + return (cls.factory, + (name, environment, language, kwargs, tools, tool_name)) + sub = type('{}Dependency'.format(name.capitalize()), (cls, ), + {'tools': tools, 'tool_name': tool_name, '__reduce__': reduce}) + + return sub(name, environment, language, kwargs) + + def find_config(self, versions=None): + """Helper method that searchs for config tool binaries in PATH and + returns the one that best matches the given version requirements. + """ + if not isinstance(versions, list) and versions is not None: + versions = listify(versions) + + best_match = (None, None) + for tool in self.tools: + try: + p, out = Popen_safe([tool, '--version'])[:2] + except (FileNotFoundError, PermissionError): + continue + if p.returncode != 0: + continue + + out = out.strip() + # Some tools, like pcap-config don't supply a version, but also + # dont fail with --version, in that case just assume that there is + # only one verison and return it. + if not out: + return (tool, 'none') + if versions: + is_found = version_compare_many(out, versions)[0] + # This allows returning a found version without a config tool, + # which is useful to inform the user that you found version x, + # but y was required. + if not is_found: + tool = None + if best_match[1]: + if version_compare(out, '> {}'.format(best_match[1])): + best_match = (tool, out) + else: + best_match = (tool, out) + + return best_match + + def report_config(self, version, req_version): + """Helper method to print messages about the tool.""" + if self.config is None: + if version is not None: + mlog.log('found {} {!r} but need:'.format(self.tool_name, version), + req_version) + else: + mlog.log("No {} found; can't detect dependency".format(self.tool_name)) + mlog.log('Dependency {} found:'.format(self.name), mlog.red('NO')) + if self.required: + raise DependencyException('Dependency {} not found'.format(self.name)) + return False + mlog.log('Found {}:'.format(self.tool_name), mlog.bold(shutil.which(self.config)), + '({})'.format(version)) + mlog.log('Dependency {} found:'.format(self.name), mlog.green('YES')) + return True + + def get_config_value(self, args, stage): + p, out, err = Popen_safe([self.config] + args) + if p.returncode != 0: + if self.required: + raise DependencyException( + 'Could not generate {} for {}.\n{}'.format( + stage, self.name, err)) + return [] + return shlex.split(out) + + def get_methods(self): + return [DependencyMethods.AUTO, DependencyMethods.CONFIG_TOOL] + + def get_configtool_variable(self, variable_name): + p, out, _ = Popen_safe([self.config, '--{}'.format(variable_name)]) + if p.returncode != 0: + if self.required: + raise DependencyException( + 'Could not get variable "{}" for dependency {}'.format( + variable_name, self.name)) + variable = out.strip() + mlog.debug('Got config-tool variable {} : {}'.format(variable_name, variable)) + return variable + + class PkgConfigDependency(ExternalDependency): # The class's copy of the pkg-config path. Avoids having to search for it # multiple times in the same Meson invocation. diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index a161495..d15d5da 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -17,14 +17,16 @@ import os import re -import shlex import shutil from .. import mlog from .. import mesonlib from ..mesonlib import version_compare, Popen_safe, stringlistify, extract_as_list -from .base import DependencyException, ExternalDependency, PkgConfigDependency -from .base import strip_system_libdirs +from .base import ( + DependencyException, ExternalDependency, PkgConfigDependency, + strip_system_libdirs, ConfigToolDependency, +) + class GTestDependency(ExternalDependency): def __init__(self, environment, kwargs): @@ -109,7 +111,7 @@ class GMockDependency(ExternalDependency): self.is_found = False -class LLVMDependency(ExternalDependency): +class LLVMDependency(ConfigToolDependency): """ LLVM uses a special tool, llvm-config, which has arguments for getting c args, cxx args, and ldargs as well as version. @@ -121,7 +123,7 @@ class LLVMDependency(ExternalDependency): # not be moved to the beginning of the list. The only difference between # llvm-config-6.0 and llvm-config-devel is that the former is used by # Debian and the latter is used by FreeBSD. - llvm_config_bins = [ + tools = [ 'llvm-config', # base 'llvm-config-5.0', 'llvm-config50', # latest stable release 'llvm-config-4.0', 'llvm-config40', # old stable releases @@ -132,61 +134,31 @@ class LLVMDependency(ExternalDependency): 'llvm-config-3.5', 'llvm-config35', 'llvm-config-6.0', 'llvm-config-devel', # development snapshot ] + tool_name = 'llvm-config' __cpp_blacklist = {'-DNDEBUG'} def __init__(self, environment, kwargs): # It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0 # the C linker works fine if only using the C API. - super().__init__('llvm-config', environment, 'cpp', kwargs) + super().__init__('config-tool', environment, 'cpp', kwargs) self.provided_modules = [] self.required_modules = set() - self.llvmconfig = None - self.static = kwargs.get('static', False) - self.__best_found = None - # FIXME: Support multiple version requirements ala PkgConfigDependency - req_version = kwargs.get('version', None) - self.check_llvmconfig(req_version) - if self.llvmconfig is None: - if self.__best_found is not None: - mlog.log('found {!r} but need:'.format(self.__best_found), - req_version) - else: - mlog.log("No llvm-config found; can't detect dependency") - mlog.log('Dependency LLVM found:', mlog.red('NO')) - if self.required: - raise DependencyException('Dependency LLVM not found') + if not self.is_found: return - - p, out, err = Popen_safe([self.llvmconfig, '--version']) - if p.returncode != 0: - mlog.debug('stdout: {}\nstderr: {}'.format(out, err)) - if self.required: - raise DependencyException('Dependency LLVM not found') - mlog.log('Dependency LLVM found:', mlog.red('NO')) - return - - mlog.log('Dependency LLVM found:', mlog.green('YES')) - self.is_found = True + self.static = kwargs.get('static', False) # Currently meson doesn't really attempt to handle pre-release versions, # so strip the 'svn' off the end, since it will probably cuase problems # for users who want the patch version. - self.version = out.strip().rstrip('svn') - - p, out, err = Popen_safe([self.llvmconfig, '--components']) - if p.returncode != 0: - raise DependencyException('Could not generate modules for LLVM:\n' + err) - self.provided_modules = shlex.split(out) + self.version = self.version.rstrip('svn') + self.provided_modules = self.get_config_value(['--components'], 'modules') modules = stringlistify(extract_as_list(kwargs, 'modules')) self.check_components(modules) opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules')) self.check_components(opt_modules, required=False) - p, out, err = Popen_safe([self.llvmconfig, '--cppflags']) - if p.returncode != 0: - raise DependencyException('Could not generate includedir for LLVM:\n' + err) - cargs = mesonlib.OrderedSet(shlex.split(out)) + cargs = set(self.get_config_value(['--cppflags'], 'compile_args')) self.compile_args = list(cargs.difference(self.__cpp_blacklist)) if version_compare(self.version, '>= 3.9'): @@ -198,11 +170,9 @@ class LLVMDependency(ExternalDependency): def _set_new_link_args(self): """How to set linker args for LLVM versions >= 3.9""" link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared'] - p, out, err = Popen_safe( - [self.llvmconfig, '--libs', '--ldflags'] + link_args + list(self.required_modules)) - if p.returncode != 0: - raise DependencyException('Could not generate libs for LLVM:\n' + err) - self.link_args = shlex.split(out) + self.link_args = self.get_config_value( + ['--libs', '--ldflags'] + link_args + list(self.required_modules), + 'link_args') def _set_old_link_args(self): """Setting linker args for older versions of llvm. @@ -213,20 +183,15 @@ class LLVMDependency(ExternalDependency): of course we do. """ if self.static: - p, out, err = Popen_safe( - [self.llvmconfig, '--libs', '--ldflags', '--system-libs'] + list(self.required_modules)) - if p.returncode != 0: - raise DependencyException('Could not generate libs for LLVM:\n' + err) - self.link_args = shlex.split(out) + self.link_args = self.get_config_value( + ['--libs', '--ldflags', '--system-libs'] + list(self.required_modules), + 'link_args') else: # llvm-config will provide arguments for static linking, so we get # to figure out for ourselves what to link with. We'll do that by # checking in the directory provided by --libdir for a library # called libLLVM-<ver>.(so|dylib|dll) - p, out, err = Popen_safe([self.llvmconfig, '--libdir']) - if p.returncode != 0: - raise DependencyException('Could not generate libs for LLVM:\n' + err) - libdir = out.strip() + libdir = self.get_config_value(['--libdir'], 'link_args')[0] expected_name = 'libLLVM-{}'.format(self.version) re_name = re.compile(r'{}.(so|dll|dylib)'.format(expected_name)) @@ -259,34 +224,6 @@ class LLVMDependency(ExternalDependency): self.required_modules.add(mod) mlog.log('LLVM module', mod, 'found:', mlog.green('YES')) - def check_llvmconfig(self, version_req): - """Try to find the highest version of llvm-config.""" - for llvmconfig in self.llvm_config_bins: - try: - p, out = Popen_safe([llvmconfig, '--version'])[0:2] - out = out.strip() - if p.returncode != 0: - continue - if version_req: - if version_compare(out, version_req, strict=True): - if self.__best_found and version_compare( - out, '<={}'.format(self.__best_found), strict=True): - continue - self.__best_found = out - self.llvmconfig = llvmconfig - else: - # If no specific version is requested use the first version - # found, since that should be the best. - self.__best_found = out - self.llvmconfig = llvmconfig - break - except (FileNotFoundError, PermissionError): - pass - if self.__best_found: - mlog.log('Found llvm-config:', - mlog.bold(shutil.which(self.llvmconfig)), - '({})'.format(out.strip())) - def need_threads(self): return True diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 4a023e4..e966597 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -1,4 +1,4 @@ -# Copyright 2013-2017 The Meson development team +# Copyright 2013-2017 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,8 +26,11 @@ from .. import mesonlib from ..mesonlib import Popen_safe, extract_as_list from ..environment import detect_cpu_family -from .base import DependencyException, DependencyMethods -from .base import ExternalDependency, ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency +from .base import ( + DependencyException, DependencyMethods, ExternalDependency, + ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency, + ConfigToolDependency, +) # On windows 3 directory layouts are supported: # * The default layout (versioned) installed: @@ -687,9 +690,9 @@ class Python3Dependency(ExternalDependency): class PcapDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('pcap', environment, None, kwargs) + kwargs['required'] = False if DependencyMethods.PKGCONFIG in self.methods: try: - kwargs['required'] = False pcdep = PkgConfigDependency('pcap', environment, kwargs) if pcdep.found(): self.type_name = 'pkgconfig' @@ -700,25 +703,26 @@ class PcapDependency(ExternalDependency): return except Exception as e: mlog.debug('Pcap not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.PCAPCONFIG in self.methods: - pcapconf = shutil.which('pcap-config') - if pcapconf: - stdo = Popen_safe(['pcap-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['pcap-config', '--libs'])[1] - self.link_args = stdo.strip().split() - self.version = self.get_pcap_lib_version() - self.is_found = True - mlog.log('Dependency', mlog.bold('pcap'), 'found:', - mlog.green('YES'), '(%s)' % pcapconf) - return - mlog.debug('Could not find pcap-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'pcap', environment, None, kwargs, ['pcap-config'], 'pcap-config') + if ctdep.found(): + self.config = ctdep.config + self.type_name = 'config-tool' + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.link_args = ctdep.get_config_value(['--libs'], 'link_args') + self.version = self.get_pcap_lib_version() + self.is_found = True + return + except Exception as e: + mlog.debug('Pcap not found via pcap-config. Trying next, error was:', str(e)) def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.PCAPCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.PCAPCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] def get_pcap_lib_version(self): return self.compiler.get_return_value('pcap_lib_version', 'string', @@ -728,9 +732,9 @@ class PcapDependency(ExternalDependency): class CupsDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('cups', environment, None, kwargs) + kwargs['required'] = False if DependencyMethods.PKGCONFIG in self.methods: try: - kwargs['required'] = False pcdep = PkgConfigDependency('cups', environment, kwargs) if pcdep.found(): self.type_name = 'pkgconfig' @@ -741,20 +745,20 @@ class CupsDependency(ExternalDependency): return except Exception as e: mlog.debug('cups not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.CUPSCONFIG in self.methods: - cupsconf = shutil.which('cups-config') - if cupsconf: - stdo = Popen_safe(['cups-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['cups-config', '--libs'])[1] - self.link_args = stdo.strip().split() - stdo = Popen_safe(['cups-config', '--version'])[1] - self.version = stdo.strip().split() - self.is_found = True - mlog.log('Dependency', mlog.bold('cups'), 'found:', - mlog.green('YES'), '(%s)' % cupsconf) - return - mlog.debug('Could not find cups-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'cups', environment, None, kwargs, ['cups-config'], 'cups-config') + if ctdep.found(): + self.config = ctdep.config + self.type_name = 'config-tool' + self.version = ctdep.version + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.link_args = ctdep.get_config_value(['--libs'], 'link_args') + self.is_found = True + return + except Exception as e: + mlog.debug('cups not found via cups-config. Trying next, error was:', str(e)) if DependencyMethods.EXTRAFRAMEWORK in self.methods: if mesonlib.is_osx(): fwdep = ExtraFrameworkDependency('cups', False, None, self.env, @@ -769,9 +773,9 @@ class CupsDependency(ExternalDependency): def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] class LibWmfDependency(ExternalDependency): @@ -790,26 +794,27 @@ class LibWmfDependency(ExternalDependency): return except Exception as e: mlog.debug('LibWmf not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.LIBWMFCONFIG in self.methods: - libwmfconf = shutil.which('libwmf-config') - if libwmfconf: - stdo = Popen_safe(['libwmf-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['libwmf-config', '--libs'])[1] - self.link_args = stdo.strip().split() - stdo = Popen_safe(['libwmf-config', '--version'])[1] - self.version = stdo.strip() - self.is_found = True - mlog.log('Dependency', mlog.bold('libwmf'), 'found:', - mlog.green('YES'), '(%s)' % libwmfconf) - return - mlog.debug('Could not find libwmf-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config') + if ctdep.found(): + self.config = ctdep.config + self.type_name = 'config-too' + self.version = ctdep.version + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.link_args = ctdep.get_config_value(['--libs'], 'link_args') + self.is_found = True + return + except Exception as e: + mlog.debug('cups not found via libwmf-config. Trying next, error was:', str(e)) def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.LIBWMFCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.LIBWMFCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] + # Generated with boost_names.py BOOST_LIBS = [ diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 837149c..dd04580 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -23,13 +23,16 @@ from collections import OrderedDict from .. import mlog from .. import mesonlib -from ..mesonlib import MesonException, Popen_safe, version_compare -from ..mesonlib import extract_as_list, for_windows +from ..mesonlib import ( + MesonException, Popen_safe, extract_as_list, for_windows, + version_compare_many +) from ..environment import detect_cpu from .base import DependencyException, DependencyMethods from .base import ExternalDependency, ExternalProgram from .base import ExtraFrameworkDependency, PkgConfigDependency +from .base import ConfigToolDependency class GLDependency(ExternalDependency): @@ -70,49 +73,48 @@ class GLDependency(ExternalDependency): return [DependencyMethods.PKGCONFIG] -class GnuStepDependency(ExternalDependency): +class GnuStepDependency(ConfigToolDependency): + + tools = ['gnustep-config'] + tool_name = 'gnustep-config' + def __init__(self, environment, kwargs): super().__init__('gnustep', environment, 'objc', kwargs) + if not self.is_found: + return self.modules = kwargs.get('modules', []) - self.detect() - - def detect(self): - self.confprog = 'gnustep-config' + self.compile_args = self.filter_args( + self.get_config_value(['--objc-flags'], 'compile_args')) + self.link_args = self.weird_filter(self.get_config_value( + ['--gui-libs' if 'gui' in self.modules else '--base-libs'], + 'link_args')) + + def find_config(self, versions=None): + tool = self.tools[0] try: - gp = Popen_safe([self.confprog, '--help'])[0] + p, out = Popen_safe([tool, '--help'])[:2] except (FileNotFoundError, PermissionError): - mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)') - return - if gp.returncode != 0: - mlog.log('Dependency GnuStep found:', mlog.red('NO')) - return - if 'gui' in self.modules: - arg = '--gui-libs' - else: - arg = '--base-libs' - fp, flagtxt, flagerr = Popen_safe([self.confprog, '--objc-flags']) - if fp.returncode != 0: - raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr)) - args = flagtxt.split() - self.compile_args = self.filter_args(args) - fp, libtxt, liberr = Popen_safe([self.confprog, arg]) - if fp.returncode != 0: - raise DependencyException('Error getting objc-lib args: %s %s' % (libtxt, liberr)) - self.link_args = self.weird_filter(libtxt.split()) - self.version = self.detect_version() - self.is_found = True - mlog.log('Dependency', mlog.bold('GnuStep'), 'found:', - mlog.green('YES'), self.version) + return (None, None) + if p.returncode != 0: + return (None, None) + self.config = tool + found_version = self.detect_version() + if versions and not version_compare_many(found_version, versions)[0]: + return (None, found_version) + + return (tool, found_version) def weird_filter(self, elems): - """When building packages, the output of the enclosing Make -is sometimes mixed among the subprocess output. I have no idea -why. As a hack filter out everything that is not a flag.""" + """When building packages, the output of the enclosing Make is + sometimes mixed among the subprocess output. I have no idea why. As a + hack filter out everything that is not a flag. + """ return [e for e in elems if e.startswith('-')] def filter_args(self, args): - """gnustep-config returns a bunch of garbage args such - as -O2 and so on. Drop everything that is not needed.""" + """gnustep-config returns a bunch of garbage args such as -O2 and so + on. Drop everything that is not needed. + """ result = [] for f in args: if f.startswith('-D') \ @@ -124,8 +126,8 @@ why. As a hack filter out everything that is not a flag.""" return result def detect_version(self): - gmake = self.get_variable('GNUMAKE') - makefile_dir = self.get_variable('GNUSTEP_MAKEFILES') + gmake = self.get_config_value(['--variable=GNUMAKE'], 'variable')[0] + makefile_dir = self.get_config_value(['--variable=GNUSTEP_MAKEFILES'], 'variable')[0] # This Makefile has the GNUStep version set base_make = os.path.join(makefile_dir, 'Additional', 'base.make') # Print the Makefile variable passed as the argument. For instance, if @@ -145,13 +147,6 @@ why. As a hack filter out everything that is not a flag.""" version = '1' return version - def get_variable(self, var): - p, o, e = Popen_safe([self.confprog, '--variable=' + var]) - if p.returncode != 0 and self.required: - raise DependencyException('{!r} for variable {!r} failed to run' - ''.format(self.confprog, var)) - return o.strip() - class QtBaseDependency(ExternalDependency): def __init__(self, name, env, kwargs): @@ -380,9 +375,9 @@ class Qt5Dependency(QtBaseDependency): class SDL2Dependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('sdl2', environment, None, kwargs) + kwargs['required'] = False if DependencyMethods.PKGCONFIG in self.methods: try: - kwargs['required'] = False pcdep = PkgConfigDependency('sdl2', environment, kwargs) if pcdep.found(): self.type_name = 'pkgconfig' @@ -393,20 +388,20 @@ class SDL2Dependency(ExternalDependency): return except Exception as e: mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.SDLCONFIG in self.methods: - sdlconf = shutil.which('sdl2-config') - if sdlconf: - stdo = Popen_safe(['sdl2-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['sdl2-config', '--libs'])[1] - self.link_args = stdo.strip().split() - stdo = Popen_safe(['sdl2-config', '--version'])[1] - self.version = stdo.strip() - self.is_found = True - mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'), - self.version, '(%s)' % sdlconf) - return - mlog.debug('Could not find sdl2-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'sdl2', environment, None, kwargs, ['sdl2-config'], 'sdl2-config') + if ctdep.found(): + self.type_name = 'config-tool' + self.config = ctdep.config + self.version = ctdep.version + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.links_args = ctdep.get_config_value(['--libs'], 'link_args') + self.is_found = True + return + except Exception as e: + mlog.debug('SDL 2 not found via sdl2-config. Trying next, error was:', str(e)) if DependencyMethods.EXTRAFRAMEWORK in self.methods: if mesonlib.is_osx(): fwdep = ExtraFrameworkDependency('sdl2', False, None, self.env, @@ -421,54 +416,25 @@ class SDL2Dependency(ExternalDependency): def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] -class WxDependency(ExternalDependency): - wx_found = None +class WxDependency(ConfigToolDependency): + + tools = ['wx-config-3.0', 'wx-config'] + tool_name = 'wx-config' def __init__(self, environment, kwargs): - super().__init__('wx', environment, None, kwargs) - self.version = 'none' - if WxDependency.wx_found is None: - self.check_wxconfig() - else: - self.wxc = WxDependency.wx_found - if not WxDependency.wx_found: - mlog.log("Neither wx-config-3.0 nor wx-config found; can't detect dependency") + super().__init__('WxWidgets', environment, None, kwargs) + if not self.is_found: return - - # FIXME: This should print stdout and stderr using mlog.debug - p, out = Popen_safe([self.wxc, '--version'])[0:2] - if p.returncode != 0: - mlog.log('Dependency wxwidgets found:', mlog.red('NO')) - else: - self.version = out.strip() - # FIXME: Support multiple version reqs like PkgConfigDependency - version_req = kwargs.get('version', None) - if version_req is not None: - if not version_compare(self.version, version_req, strict=True): - mlog.log('Wxwidgets version %s does not fullfill requirement %s' % - (self.version, version_req)) - return - mlog.log('Dependency wxwidgets found:', mlog.green('YES')) - self.is_found = True - self.requested_modules = self.get_requested(kwargs) - # wx-config seems to have a cflags as well but since it requires C++, - # this should be good, at least for now. - p, out = Popen_safe([self.wxc, '--cxxflags'])[0:2] - # FIXME: this error should only be raised if required is true - if p.returncode != 0: - raise DependencyException('Could not generate cargs for wxwidgets.') - self.compile_args = out.split() - - # FIXME: this error should only be raised if required is true - p, out = Popen_safe([self.wxc, '--libs'] + self.requested_modules)[0:2] - if p.returncode != 0: - raise DependencyException('Could not generate libs for wxwidgets.') - self.link_args = out.split() + self.requested_modules = self.get_requested(kwargs) + # wx-config seems to have a cflags as well but since it requires C++, + # this should be good, at least for now. + self.compile_args = self.get_config_value(['--cxxflags'], 'compile_args') + self.link_args = self.get_config_value(['--libs'], 'link_args') def get_requested(self, kwargs): if 'modules' not in kwargs: @@ -479,20 +445,6 @@ class WxDependency(ExternalDependency): raise DependencyException('wxwidgets module argument is not a string') return candidates - def check_wxconfig(self): - for wxc in ['wx-config-3.0', 'wx-config']: - try: - p, out = Popen_safe([wxc, '--version'])[0:2] - if p.returncode == 0: - mlog.log('Found wx-config:', mlog.bold(shutil.which(wxc)), - '(%s)' % out.strip()) - self.wxc = wxc - WxDependency.wx_found = wxc - return - except (FileNotFoundError, PermissionError): - pass - WxDependency.wxconfig_found = False - mlog.log('Found wx-config:', mlog.red('NO')) class VulkanDependency(ExternalDependency): def __init__(self, environment, kwargs): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 77f3105..b301fee 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -274,6 +274,7 @@ class DependencyHolder(InterpreterObject, ObjectHolder): 'type_name': self.type_name_method, 'version': self.version_method, 'get_pkgconfig_variable': self.pkgconfig_method, + 'get_configtool_variable': self.configtool_method, }) def type_name_method(self, args, kwargs): @@ -296,6 +297,15 @@ class DependencyHolder(InterpreterObject, ObjectHolder): raise InterpreterException('Variable name must be a string.') return self.held_object.get_pkgconfig_variable(varname) + def configtool_method(self, args, kwargs): + args = listify(args) + if len(args) != 1: + raise InterpreterException('get_configtool_variable takes exactly one argument.') + varname = args[0] + if not isinstance(varname, str): + raise InterpreterException('Variable name must be a string.') + return self.held_object.get_configtool_variable(varname) + class InternalDependencyHolder(InterpreterObject, ObjectHolder): def __init__(self, dep): InterpreterObject.__init__(self) diff --git a/run_unittests.py b/run_unittests.py index be5ce47..8af872e 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1661,7 +1661,7 @@ class FailureTests(BasePlatformTests): raise unittest.SkipTest('wx-config or wx-config-3.0 found') self.assertMesonRaises("dependency('wxwidgets')", self.dnf) self.assertMesonOutputs("dependency('wxwidgets', required : false)", - "nor wx-config found") + "No wx-config found;") def test_wx_dependency(self): if not shutil.which('wx-config-3.0') and not shutil.which('wx-config'): diff --git a/test cases/common/165 config tool variable/meson.build b/test cases/common/165 config tool variable/meson.build new file mode 100644 index 0000000..0643042 --- /dev/null +++ b/test cases/common/165 config tool variable/meson.build @@ -0,0 +1,31 @@ +# Copyright © 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +project('config tool variable', 'cpp') + + +dep_llvm = dependency('llvm', required : false) +if not dep_llvm.found() + error('MESON_SKIP_TEST LLVM not installed.') +endif + +includedir = dep_llvm.get_configtool_variable('includedir') +includedir = join_paths(includedir, 'llvm') +if host_machine.system() == 'windows' + cmd = run_command(['dir', includedir]) +else + cmd = run_command(['ls', includedir]) +endif + +assert(cmd.returncode() == 0, 'did not run successfully') diff --git a/test cases/frameworks/16 sdl2/meson.build b/test cases/frameworks/16 sdl2/meson.build index c79bd46..61a34ef 100644 --- a/test cases/frameworks/16 sdl2/meson.build +++ b/test cases/frameworks/16 sdl2/meson.build @@ -6,5 +6,8 @@ e = executable('sdl2prog', 'sdl2prog.c', dependencies : sdl2_dep) test('sdl2test', e) -# Ensure that we can find it with sdl2-config too +# Ensure that we can find it with sdl2-config too, using the legacy method name configdep = dependency('sdl2', method : 'sdlconfig') + +# And the modern method name +configdep = dependency('sdl2', method : 'config-tool') diff --git a/test cases/frameworks/19 pcap/meson.build b/test cases/frameworks/19 pcap/meson.build index c505960..f02f411 100644 --- a/test cases/frameworks/19 pcap/meson.build +++ b/test cases/frameworks/19 pcap/meson.build @@ -8,3 +8,7 @@ assert(pcap_ver.split('.').length() > 1, 'pcap version is "@0@"'.format(pcap_ver e = executable('pcap_prog', 'pcap_prog.c', dependencies : pcap_dep) test('pcaptest', e) + +# Ensure discovery bia the configuration tools work also +pcap_dep = dependency('pcap', version : '>=1.0', method : 'pcap-config') +pcap_dep = dependency('pcap', version : '>=1.0', method : 'config-tool') diff --git a/test cases/frameworks/20 cups/meson.build b/test cases/frameworks/20 cups/meson.build index 6c9b6fe..11f6f63 100644 --- a/test cases/frameworks/20 cups/meson.build +++ b/test cases/frameworks/20 cups/meson.build @@ -5,3 +5,8 @@ cups_dep = dependency('cups', version : '>=1.4') e = executable('cups_prog', 'cups_prog.c', dependencies : cups_dep) test('cupstest', e) + +# ensure we can find the cups dependency via the legacy and modern config-tool +# options +dep = dependency('cups', version : '>=1.4', method : 'cups-config') +dep = dependency('cups', version : '>=1.4', method : 'config-tool') diff --git a/test cases/frameworks/21 libwmf/meson.build b/test cases/frameworks/21 libwmf/meson.build index a7d9263..b39d8f4 100644 --- a/test cases/frameworks/21 libwmf/meson.build +++ b/test cases/frameworks/21 libwmf/meson.build @@ -1,9 +1,14 @@ project('libwmf test', 'c') -libwmf_dep = dependency('libwmf', version : '>=3.0') +libwmf_dep = dependency('libwmf', version : '>= 0.2.8') libwmf_ver = libwmf_dep.version() assert(libwmf_ver.split('.').length() > 1, 'libwmf version is "@0@"'.format(libwmf_ver)) message('libwmf version is "@0@"'.format(libwmf_ver)) e = executable('libwmf_prog', 'libwmf_prog.c', dependencies : libwmf_dep) test('libwmftest', e) + +# Test using the method keyword: + +dependency('libwmf', method : 'config-tool') +dependency('libwmf', method : 'libwmf-config') |