diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2018-08-27 21:29:57 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-27 21:29:57 +0300 |
commit | 899b0aae9f9afacccfd5963e84df6634e9835b5c (patch) | |
tree | 41d7575d4d7c9a825715cd1243de4a129aea933a | |
parent | ac07ae7d41e82663c955363e5124404fe9410262 (diff) | |
parent | 7fff8318f537400249f191eda373c716e5ba2bee (diff) | |
download | meson-899b0aae9f9afacccfd5963e84df6634e9835b5c.zip meson-899b0aae9f9afacccfd5963e84df6634e9835b5c.tar.gz meson-899b0aae9f9afacccfd5963e84df6634e9835b5c.tar.bz2 |
Merge pull request #4035 from jon-turney/factor-out-version-check
Apply dependency(version:) check for all dependency types
24 files changed, 145 insertions, 67 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 6cf7552..6d2b2da 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -380,11 +380,12 @@ otherwise. This function supports the following keyword arguments: - `static` tells the dependency provider to try to get static libraries instead of dynamic ones (note that this is not supported by all dependency backends) -- `version`, specifies the required version, a string containing a +- `version` specifies the required version, a string containing a comparison operator followed by the version string, examples include `>1.0.0`, `<=2.3.5` or `3.1.4` for exact matching. (*Added 0.37.0*) You can also specify multiple restrictions by passing a list to this keyword argument, such as: `['>=3.14.0', '<=4.1.0']`. + These requirements are never met if the version is unknown. - other [library-specific](Dependencies.md#dependencies-with-custom-lookup-functionality) keywords may also be accepted (e.g. `modules` specifies submodules to use for @@ -1986,7 +1987,9 @@ an external dependency with the following methods: with `declare_dependency()` and `pkgconfig` for system dependencies obtained with Pkg-config. - - `version()` is the version number as a string, for example `1.2.8` + - `version()` is the version number as a string, for example `1.2.8`. + `unknown` if the dependency provider doesn't support determining the + version. - `partial_dependency(compile_args : false, link_args : false, links : false, includes : false, source : false)` (*added 0.46.0*) returns diff --git a/docs/markdown/snippets/dependency_version.md b/docs/markdown/snippets/dependency_version.md new file mode 100644 index 0000000..4bbf346 --- /dev/null +++ b/docs/markdown/snippets/dependency_version.md @@ -0,0 +1,14 @@ +## `dependency(version:)` now applies to all dependency types + +Previously, version constraints were only enforced for dependencies found using +the pkg-config dependency provider. These constraints now apply to dependencies +found using any dependency provider. + +Some combinations of dependency, host and method do not currently support +discovery of the version. In these cases, the dependency will not be found if a +version constraint is applied, otherwise the `version()` method for the +dependency object will return `'unknown'`. + +(If discovering the version in one of these combinations is important to you, +and a method exists to determine the version in that case, please file an issue +with as much information as possible.) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 94ae217..04c4701 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -98,7 +98,7 @@ class Dependency: def __init__(self, type_name, kwargs): self.name = "null" - self.version = 'none' + self.version = None self.language = None # None means C-like self.is_found = False self.type_name = type_name @@ -138,7 +138,10 @@ class Dependency: return self.name def get_version(self): - return self.version + if self.version: + return self.version + else: + return 'unknown' def get_exe_args(self, compiler): return [] @@ -218,6 +221,8 @@ class ExternalDependency(Dependency): self.is_found = False self.language = language self.version_reqs = kwargs.get('version', None) + if isinstance(self.version_reqs, str): + self.version_reqs = [self.version_reqs] self.required = kwargs.get('required', True) self.silent = kwargs.get('silent', False) self.static = kwargs.get('static', False) @@ -275,6 +280,41 @@ class ExternalDependency(Dependency): def log_tried(self): return '' + # Check if dependency version meets the requirements + def _check_version(self): + if not self.is_found: + return + + if self.version_reqs: + # an unknown version can never satisfy any requirement + if not self.version: + found_msg = ['Dependency', mlog.bold(self.name), 'found:'] + found_msg += [mlog.red('NO'), 'unknown version, but need:', + self.version_reqs] + mlog.log(*found_msg) + + if self.required: + m = 'Unknown version of dependency {!r}, but need {!r}.' + raise DependencyException(m.format(self.name, self.version_reqs)) + + else: + (self.is_found, not_found, found) = \ + version_compare_many(self.version, self.version_reqs) + if not self.is_found: + found_msg = ['Dependency', mlog.bold(self.name), 'found:'] + found_msg += [mlog.red('NO'), + 'found {!r} but need:'.format(self.version), + ', '.join(["'{}'".format(e) for e in not_found])] + if found: + found_msg += ['; matched:', + ', '.join(["'{}'".format(e) for e in found])] + mlog.log(*found_msg) + + if self.required: + m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' + raise DependencyException(m.format(self.name, not_found, self.version)) + return + class NotFoundDependency(Dependency): def __init__(self, environment): @@ -375,7 +415,7 @@ class ConfigToolDependency(ExternalDependency): # don't fail with --version, in that case just assume that there is # only one version and return it. if not out: - return (tool, 'none') + return (tool, None) if versions: is_found = version_compare_many(out, versions)[0] # This allows returning a found version without a config tool, @@ -480,20 +520,6 @@ class PkgConfigDependency(ExternalDependency): ret, self.version = self._call_pkgbin(['--modversion', name]) if ret != 0: return - if self.version_reqs is None: - self.is_found = True - 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.version, self.version_reqs) - if not self.is_found: - if self.required: - m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' - raise DependencyException(m.format(name, not_found, self.version)) - return try: # Fetch cargs to be used while using this dependency @@ -509,6 +535,8 @@ class PkgConfigDependency(ExternalDependency): self.is_found = False self.reason = e + self.is_found = True + def __repr__(self): s = '<{0} {1}: {2} {3}>' return s.format(self.__class__.__name__, self.name, self.is_found, @@ -862,22 +890,6 @@ class DubDependency(ExternalDependency): self.pkg = package break - # Check if package version meets the requirements - if self.version_reqs is None: - self.is_found = True - 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.version, self.version_reqs) - if not self.is_found: - if self.required: - m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' - raise DependencyException(m.format(name, not_found, self.version)) - return - if self.pkg['targetFileName'].endswith('.a'): self.static = True @@ -897,9 +909,7 @@ class DubDependency(ExternalDependency): for file in res: self.link_args.append(file) - if not found: - self.is_found = False - return + self.is_found = found def get_compiler(self): return self.compiler @@ -1241,9 +1251,6 @@ class ExtraFrameworkDependency(ExternalDependency): self.is_found = True return - def get_version(self): - return 'unknown' - def log_info(self): return os.path.join(self.path, self.name) @@ -1291,6 +1298,8 @@ def find_external_dependency(name, env, kwargs): lname = name.lower() if lname not in _packages_accept_language and 'language' in kwargs: raise DependencyException('%s dependency does not accept "language" keyword argument' % (name, )) + if not isinstance(kwargs.get('version', ''), (str, list)): + raise DependencyException('Keyword "Version" must be string or list.') # display the dependency name with correct casing display_name = display_name_map.get(lname, lname) @@ -1313,6 +1322,7 @@ def find_external_dependency(name, env, kwargs): # try this dependency method try: d = c() + d._check_version() pkgdep.append(d) except Exception as e: mlog.debug(str(e)) @@ -1333,7 +1343,7 @@ def find_external_dependency(name, env, kwargs): if info: info = ', ' + info - mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), d.version + info) + mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), (d.version if d.version else '') + info) return d diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 0cd3c2b..0876391 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -35,7 +35,6 @@ class GTestDependency(ExternalDependency): self.detect() def detect(self): - self.version = '1.something_maybe' gtest_detect = self.clib_compiler.find_library("gtest", self.env, []) gtest_main_detect = self.clib_compiler.find_library("gtest_main", self.env, []) if gtest_detect and (not self.main or gtest_main_detect): @@ -85,7 +84,6 @@ class GTestDependency(ExternalDependency): class GMockDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('gmock', environment, 'cpp', kwargs) - self.version = '1.something_maybe' # GMock may be a library or just source. # Work with both. gmock_detect = self.clib_compiler.find_library("gmock", self.env, []) diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 78ce51b..014be84 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -180,7 +180,7 @@ class MPIDependency(ExternalDependency): if version: version = version.group(0) else: - version = 'none' + version = None return version, cargs, libs @@ -197,7 +197,7 @@ class MPIDependency(ExternalDependency): return args = shlex.split(o) - version = 'none' + version = None return version, args, args @@ -222,11 +222,11 @@ class MPIDependency(ExternalDependency): else: return if self.language == 'fortran': - return ('none', + return (None, ['-I' + incdir, '-I' + os.path.join(incdir, post)], [os.path.join(libdir, 'msmpi.lib'), os.path.join(libdir, 'msmpifec.lib')]) else: - return ('none', + return (None, ['-I' + incdir, '-I' + os.path.join(incdir, post)], [os.path.join(libdir, 'msmpi.lib')]) @@ -267,17 +267,13 @@ class OpenMPDependency(ExternalDependency): class ThreadDependency(ExternalDependency): def __init__(self, environment, kwargs): - super().__init__('threads', environment, None, {}) + super().__init__('threads', environment, None, kwargs) self.name = 'threads' self.is_found = True - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES')) def need_threads(self): return True - def get_version(self): - return 'unknown' - class Python3Dependency(ExternalDependency): def __init__(self, environment, kwargs): @@ -447,8 +443,11 @@ class PcapDependency(ExternalDependency): @staticmethod def get_pcap_lib_version(ctdep): - return ctdep.clib_compiler.get_return_value('pcap_lib_version', 'string', - '#include <pcap.h>', ctdep.env, [], [ctdep]) + v = ctdep.clib_compiler.get_return_value('pcap_lib_version', 'string', + '#include <pcap.h>', ctdep.env, [], [ctdep]) + v = re.sub(r'libpcap version ', '', v) + v = re.sub(r' -- Apple version.*$', '', v) + return v class CupsDependency(ExternalDependency): diff --git a/mesonbuild/dependencies/platform.py b/mesonbuild/dependencies/platform.py index 5f89ccb..0c05156 100644 --- a/mesonbuild/dependencies/platform.py +++ b/mesonbuild/dependencies/platform.py @@ -33,8 +33,4 @@ class AppleFrameworks(ExternalDependency): for f in self.frameworks: self.link_args += ['-framework', f] - def found(self): - return mesonlib.is_osx() - - def get_version(self): - return 'unknown' + self.is_found = mesonlib.is_osx() diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index c877f51..f19a76d 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -44,14 +44,12 @@ class GLDependency(ExternalDependency): # FIXME: Use AppleFrameworks dependency self.link_args = ['-framework', 'OpenGL'] # FIXME: Detect version using self.clib_compiler - self.version = '1' return if mesonlib.is_windows(): self.is_found = True # FIXME: Use self.clib_compiler.find_library() self.link_args = ['-lopengl32'] # FIXME: Detect version using self.clib_compiler - self.version = '1' return @classmethod @@ -63,7 +61,7 @@ class GLDependency(ExternalDependency): candidates.append(functools.partial(PkgConfigDependency, 'gl', environment, kwargs)) if DependencyMethods.SYSTEM in methods: - candidates.append(functools.partial(GLDependency), environment, kwargs) + candidates.append(functools.partial(GLDependency, environment, kwargs)) return candidates @@ -224,7 +222,7 @@ class QtBaseDependency(ExternalDependency): self.compile_args = [] self.link_args = [] self.from_text = mlog.format_list(methods) - self.version = 'none' + self.version = None def compilers_detect(self): "Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH" @@ -557,7 +555,6 @@ class VulkanDependency(ExternalDependency): # TODO: find a way to retrieve the version from the sdk? # Usually it is a part of the path to it (but does not have to be) - self.version = '1' return else: # simply try to guess it, usually works on linux @@ -565,7 +562,6 @@ class VulkanDependency(ExternalDependency): if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', environment): self.type_name = 'system' self.is_found = True - self.version = 1 # TODO for lib in libs: self.link_args.append(lib) return diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 04e963a..4fbb837 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2822,7 +2822,7 @@ external dependencies (including libraries) must go to "dependencies".''') return False found = dep.version_method([], {}) # Don't do a version check if the dependency is not found and not required - if found == 'none' and not required: + if not dep.found_method([], {}) and not required: subproj_path = os.path.join(self.subproject_dir, dirname) mlog.log('Dependency', mlog.bold(name), 'from subproject', mlog.bold(subproj_path), 'found:', mlog.red('NO'), '(cached)') diff --git a/test cases/common/190 openmp/meson.build b/test cases/common/190 openmp/meson.build index a05ca59..eb270ab 100644 --- a/test cases/common/190 openmp/meson.build +++ b/test cases/common/190 openmp/meson.build @@ -38,3 +38,6 @@ if add_languages('fortran', required : false) test('OpenMP Fortran', execpp, env : env) endif + +# Check we can apply a version constraint +dependency('openmp', version: '>=@0@'.format(openmp.version())) diff --git a/test cases/failing/81 framework dependency with version/meson.build b/test cases/failing/81 framework dependency with version/meson.build new file mode 100644 index 0000000..714ad3b --- /dev/null +++ b/test cases/failing/81 framework dependency with version/meson.build @@ -0,0 +1,4 @@ +project('framework dependency with version') +# do individual frameworks have a meaningful version to test? And multiple frameworks might be listed... +# otherwise we're not on OSX and this will definitely fail +dep = dependency('appleframeworks', modules: 'foundation', version: '>0') diff --git a/test cases/failing/82 gl dependency with version/meson.build b/test cases/failing/82 gl dependency with version/meson.build new file mode 100644 index 0000000..3014d43 --- /dev/null +++ b/test cases/failing/82 gl dependency with version/meson.build @@ -0,0 +1,9 @@ +project('gl dependency with version', 'c') + +host_system = host_machine.system() +if host_system != 'windows' and host_system != 'darwin' + error('Test only fails on Windows and OSX') +endif + +# gl dependency found via system method doesn't have a meaningful version to check +dep = dependency('gl', method: 'system', version: '>0') diff --git a/test cases/failing/83 threads dependency with version/meson.build b/test cases/failing/83 threads dependency with version/meson.build new file mode 100644 index 0000000..6023fae --- /dev/null +++ b/test cases/failing/83 threads dependency with version/meson.build @@ -0,0 +1,3 @@ +project('threads dependency with version', 'c') +# threads dependency doesn't have a meaningful version to check +dep = dependency('threads', version: '>0') diff --git a/test cases/failing/84 gtest dependency with version/meson.build b/test cases/failing/84 gtest dependency with version/meson.build new file mode 100644 index 0000000..5115f27 --- /dev/null +++ b/test cases/failing/84 gtest dependency with version/meson.build @@ -0,0 +1,3 @@ +project('gtest dependency with version', ['c', 'cpp']) +# discovering gtest version is not yet implemented +dep = dependency('gtest', version: '>0') diff --git a/test cases/frameworks/1 boost/meson.build b/test cases/frameworks/1 boost/meson.build index 9399598..d1e1da4 100644 --- a/test cases/frameworks/1 boost/meson.build +++ b/test cases/frameworks/1 boost/meson.build @@ -33,3 +33,6 @@ test('Boost nomod', nomodexe) test('Boost extralib test', extralibexe) subdir('partial_dep') + +# check we can apply a version constraint +dependency('boost', version: '>=@0@'.format(dep.version())) diff --git a/test cases/frameworks/15 llvm/meson.build b/test cases/frameworks/15 llvm/meson.build index b5505eb..e05fddd 100644 --- a/test cases/frameworks/15 llvm/meson.build +++ b/test cases/frameworks/15 llvm/meson.build @@ -41,3 +41,6 @@ foreach static : [true, false] ) endif endforeach + +# Check we can apply a version constraint +dependency('llvm', version: '>=@0@'.format(d.version())) diff --git a/test cases/frameworks/16 sdl2/meson.build b/test cases/frameworks/16 sdl2/meson.build index 1bbf09f..fd90e36 100644 --- a/test cases/frameworks/16 sdl2/meson.build +++ b/test cases/frameworks/16 sdl2/meson.build @@ -15,3 +15,7 @@ configdep = dependency('sdl2', method : 'sdlconfig') # And the modern method name configdep = dependency('sdl2', method : 'config-tool') + +# Check we can apply a version constraint +dependency('sdl2', version: '>=@0@'.format(sdl2_dep.version()), method: 'pkg-config') +dependency('sdl2', version: '>=@0@'.format(sdl2_dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/17 mpi/meson.build b/test cases/frameworks/17 mpi/meson.build index f3eacac..1085d40 100644 --- a/test cases/frameworks/17 mpi/meson.build +++ b/test cases/frameworks/17 mpi/meson.build @@ -42,3 +42,6 @@ if uburesult.returncode() != 0 and add_languages('fortran', required : false) test('MPI Fortran', exef) endif + +# Check we can apply a version constraint +dependency('mpi', version: '>=@0@'.format(mpic.version())) diff --git a/test cases/frameworks/18 vulkan/meson.build b/test cases/frameworks/18 vulkan/meson.build index e98854e..5cfe89f 100644 --- a/test cases/frameworks/18 vulkan/meson.build +++ b/test cases/frameworks/18 vulkan/meson.build @@ -8,3 +8,6 @@ endif e = executable('vulkanprog', 'vulkanprog.c', dependencies : vulkan_dep) test('vulkantest', e) + +# Check we can apply a version constraint +dependency('vulkan', version: '>=@0@'.format(vulkan_dep.version())) diff --git a/test cases/frameworks/19 pcap/meson.build b/test cases/frameworks/19 pcap/meson.build index eb6fc2c..051e49e 100644 --- a/test cases/frameworks/19 pcap/meson.build +++ b/test cases/frameworks/19 pcap/meson.build @@ -16,3 +16,7 @@ test('pcaptest', e) # Ensure discovery via 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') + +# Check we can apply a version constraint +dependency('pcap', version: '>=@0@'.format(pcap_dep.version()), method: 'pkg-config', required: false) +dependency('pcap', version: '>=@0@'.format(pcap_dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/20 cups/meson.build b/test cases/frameworks/20 cups/meson.build index 9040de6..d50c4a8 100644 --- a/test cases/frameworks/20 cups/meson.build +++ b/test cases/frameworks/20 cups/meson.build @@ -14,3 +14,7 @@ test('cupstest', e) # options dep = dependency('cups', version : '>=1.4', method : 'cups-config') dep = dependency('cups', version : '>=1.4', method : 'config-tool') + +# check we can apply a version constraint +dependency('cups', version: '>=@0@'.format(dep.version()), method: 'pkg-config', required: false) +dependency('cups', version: '>=@0@'.format(dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/21 libwmf/meson.build b/test cases/frameworks/21 libwmf/meson.build index 1fdce2e..ab0ebf6 100644 --- a/test cases/frameworks/21 libwmf/meson.build +++ b/test cases/frameworks/21 libwmf/meson.build @@ -17,3 +17,7 @@ test('libwmftest', e) dependency('libwmf', method : 'config-tool') dependency('libwmf', method : 'libwmf-config') + +# Check we can apply a version constraint +dependency('libwmf', version: '>=@0@'.format(libwmf_dep.version()), method: 'pkg-config', required: false) +dependency('libwmf', version: '>=@0@'.format(libwmf_dep.version()), method: 'config-tool') diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index 16fe564..1d7ff4e 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -103,5 +103,9 @@ foreach qt : ['qt4', 'qt5'] if qt == 'qt5' subdir('subfolder') endif + + # Check we can apply a version constraint + dependency(qt, modules: qt_modules, version: '>=@0@'.format(qtdep.version()), method : get_option('method')) + endif endforeach diff --git a/test cases/frameworks/9 wxwidgets/meson.build b/test cases/frameworks/9 wxwidgets/meson.build index d815a2d..0c7ecaa 100644 --- a/test cases/frameworks/9 wxwidgets/meson.build +++ b/test cases/frameworks/9 wxwidgets/meson.build @@ -12,4 +12,8 @@ if wxd.found() wx_stc = dependency('wxwidgets', version : '>=3.0.0', modules : ['std', 'stc']) stc_exe = executable('wxstc', 'wxstc.cpp', dependencies : wx_stc) test('wxstctest', stc_exe) + + # Check we can apply a version constraint + dependency('wxwidgets', version: '>=@0@'.format(wxd.version())) + endif diff --git a/test cases/python3/2 extmodule/meson.build b/test cases/python3/2 extmodule/meson.build index 0ecc813..4916a69 100644 --- a/test cases/python3/2 extmodule/meson.build +++ b/test cases/python3/2 extmodule/meson.build @@ -14,6 +14,10 @@ if py3_dep.found() py3, args : files('blaster.py'), env : ['PYTHONPATH=' + pypathdir]) + + # Check we can apply a version constraint + dependency('python3', version: '>=@0@'.format(py3_dep.version())) + else error('MESON_SKIP_TEST: Python3 libraries not found, skipping test.') endif |