diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 7462bd8c0..271c6b4d4 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -1148,7 +1148,15 @@ def get_dep_identifier(name, kwargs): modlist = [modlist] for module in modlist: elements.append(module) - return '/'.join(elements) + '/main' + str(kwargs.get('main', False)) + '/static' + str(kwargs.get('static', False)) + # We use a tuple because we need a non-mutable structure to use as the key + # of a dictionary and a string has potential for name collisions + identifier = tuple(elements) + identifier += ('main', kwargs.get('main', False)) + identifier += ('static', kwargs.get('static', False)) + if 'fallback' in kwargs: + f = kwargs.get('fallback') + identifier += ('fallback', f[0], f[1]) + return identifier def find_external_dependency(name, environment, kwargs): required = kwargs.get('required', True) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index ba943e385..47ed61c1b 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1188,7 +1188,7 @@ class Interpreter(): @noPosargs def func_declare_dependency(self, node, args, kwargs): - version = kwargs.get('version', 'undefined') + version = kwargs.get('version', self.project_version) if not isinstance(version, str): raise InterpreterException('Version must be a string.') incs = kwargs.get('include_directories', []) @@ -1358,7 +1358,7 @@ class Interpreter(): if 'version' in kwargs: pv = subi.project_version wanted = kwargs['version'] - if not mesonlib.version_compare(pv, wanted): + if pv == 'undefined' or not mesonlib.version_compare(pv, wanted): raise InterpreterException('Subproject %s version is %s but %s required.' % (dirname, pv, wanted)) self.active_projectname = current_active mlog.log('\nSubproject', mlog.bold(dirname), 'finished.') @@ -1621,16 +1621,28 @@ class Interpreter(): self.validate_arguments(args, 1, [str]) name = args[0] identifier = dependencies.get_dep_identifier(name, kwargs) + # Check if we've already searched for and found this dep + cached_dep = None if identifier in self.coredata.deps: - dep = self.coredata.deps[identifier] + cached_dep = self.coredata.deps[identifier] + if 'version' in kwargs: + wanted = kwargs['version'] + found = cached_dep.get_version() + if not found or not mesonlib.version_compare(found, wanted): + # Cached dep has the wrong version. Check if an external + # dependency or a fallback dependency provides it. + cached_dep = None + if cached_dep: + dep = cached_dep else: - dep = dependencies.Dependency() # Returns always false for dep.found() - if not dep.found(): + # We need to actually search for this dep try: dep = dependencies.find_external_dependency(name, self.environment, kwargs) except dependencies.DependencyException: if 'fallback' in kwargs: - return self.dependency_fallback(kwargs) + dep = self.dependency_fallback(kwargs) + self.coredata.deps[identifier] = dep.held_object + return dep raise self.coredata.deps[identifier] = dep return DependencyHolder(dep) @@ -1641,8 +1653,16 @@ class Interpreter(): if len(fbinfo) != 2: raise InterpreterException('Fallback info must have exactly two items.') dirname, varname = fbinfo - self.do_subproject(dirname, kwargs) - return self.subprojects[dirname].get_variable_method([varname], {}) + self.do_subproject(dirname, {}) + dep = self.subprojects[dirname].get_variable_method([varname], {}) + # Check if the version of the declared dependency matches what we want + if 'version' in kwargs: + wanted = kwargs['version'] + found = dep.version_method([], {}) + if found == 'undefined' or not mesonlib.version_compare(found, wanted): + m = 'Subproject "{0}" dependency "{1}" version is "{2}" but "{3}" is required.' + raise InterpreterException(m.format(dirname, varname, found, wanted)) + return dep def func_executable(self, node, args, kwargs): return self.build_target(node, args, kwargs, ExecutableHolder) diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 584b3b289..837f78ac3 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -132,7 +132,7 @@ numpart = re.compile('[0-9.]+') def version_compare(vstr1, vstr2): match = numpart.match(vstr1.strip()) if match is None: - raise MesonException('Unconparable version string %s.' % vstr1) + raise MesonException('Uncomparable version string %s.' % vstr1) vstr1 = match.group(0) if vstr2.startswith('>='): cmpop = operator.ge diff --git a/test cases/linuxlike/5 dependency versions/meson.build b/test cases/linuxlike/5 dependency versions/meson.build new file mode 100644 index 000000000..abe3ea670 --- /dev/null +++ b/test cases/linuxlike/5 dependency versions/meson.build @@ -0,0 +1,28 @@ +project('dep versions', 'c') + +# Find external dependency without version +zlib = dependency('zlib') +# Find external dependency with version +zlibver = dependency('zlib', version : '>1.0') +assert(zlib.version() == zlibver.version(), 'zlib versions did not match!') +# Find external dependency with conflicting version +zlibver = dependency('zlib', version : '<1.0', required : false) +assert(zlibver.found() == false, 'zlib <1.0 should not be found!') + +# Find internal dependency without version +somelibver = dependency('somelib', + fallback : ['somelibnover', 'some_dep']) +# Find an internal dependency again with the same name and a specific version +somelib = dependency('somelib', + version : '== 0.1', + fallback : ['somelib', 'some_dep']) +# Find an internal dependency again with the same name and incompatible version +somelibver = dependency('somelib', + version : '>= 0.3', + fallback : ['somelibver', 'some_dep']) +# Find somelib again, but with a fallback that will fail +somelibfail = dependency('somelib', + version : '>= 0.2', + required : false, + fallback : ['somelibfail', 'some_dep']) +assert(somelibfail.found() == false, 'somelibfail found via wrong fallback') diff --git a/test cases/linuxlike/5 dependency versions/subprojects/somelib/lib.c b/test cases/linuxlike/5 dependency versions/subprojects/somelib/lib.c new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/linuxlike/5 dependency versions/subprojects/somelib/meson.build b/test cases/linuxlike/5 dependency versions/subprojects/somelib/meson.build new file mode 100644 index 000000000..049c58b2d --- /dev/null +++ b/test cases/linuxlike/5 dependency versions/subprojects/somelib/meson.build @@ -0,0 +1,8 @@ +# Define version only in project, should get inherited by declare_dependency +project('some', 'c', version : '0.1') + +somelib = shared_library('some', 'lib.c') +someinc = include_directories('.') + +some_dep = declare_dependency(link_with : somelib, + include_directories : someinc) diff --git a/test cases/linuxlike/5 dependency versions/subprojects/somelibnover/lib.c b/test cases/linuxlike/5 dependency versions/subprojects/somelibnover/lib.c new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/linuxlike/5 dependency versions/subprojects/somelibnover/meson.build b/test cases/linuxlike/5 dependency versions/subprojects/somelibnover/meson.build new file mode 100644 index 000000000..826bb3cb4 --- /dev/null +++ b/test cases/linuxlike/5 dependency versions/subprojects/somelibnover/meson.build @@ -0,0 +1,8 @@ +project('some', 'c') + +somelib = shared_library('some', 'lib.c') +someinc = include_directories('.') + +# Define version only in declare_dependency +some_dep = declare_dependency(link_with : somelib, + include_directories : someinc) diff --git a/test cases/linuxlike/5 dependency versions/subprojects/somelibver/lib.c b/test cases/linuxlike/5 dependency versions/subprojects/somelibver/lib.c new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/linuxlike/5 dependency versions/subprojects/somelibver/meson.build b/test cases/linuxlike/5 dependency versions/subprojects/somelibver/meson.build new file mode 100644 index 000000000..ad9f24356 --- /dev/null +++ b/test cases/linuxlike/5 dependency versions/subprojects/somelibver/meson.build @@ -0,0 +1,9 @@ +project('some', 'c') + +somelib = shared_library('some', 'lib.c') +someinc = include_directories('.') + +# Define version only in declare_dependency +some_dep = declare_dependency(link_with : somelib, + include_directories : someinc, + version : '0.3')