Merge pull request #1900 from centricular/abstract-extdeps

dependencies: Add a new class ExternalDependency
pull/1928/head
Jussi Pakkanen 8 years ago committed by GitHub
commit b8f02047be
  1. 2
      .appveyor.yml
  2. 2
      mesonbuild/backend/backends.py
  3. 32
      mesonbuild/backend/vs2010backend.py
  4. 4
      mesonbuild/backend/vs2017backend.py
  5. 2
      mesonbuild/build.py
  6. 2
      mesonbuild/compilers.py
  7. 5
      mesonbuild/dependencies/__init__.py
  8. 185
      mesonbuild/dependencies/base.py
  9. 111
      mesonbuild/dependencies/dev.py
  10. 80
      mesonbuild/dependencies/misc.py
  11. 16
      mesonbuild/dependencies/platform.py
  12. 220
      mesonbuild/dependencies/ui.py
  13. 3
      mesonbuild/interpreter.py
  14. 4
      mesonbuild/mesonmain.py
  15. 2
      mesonbuild/modules/gnome.py
  16. 29
      run_project_tests.py
  17. 18
      run_tests.py
  18. 156
      run_unittests.py
  19. 7
      test cases/frameworks/15 llvm/meson.build
  20. 10
      test cases/frameworks/16 sdl2/meson.build
  21. 33
      test cases/frameworks/16 sdl2/sdl2prog.c
  22. 6
      test cases/frameworks/4 qt/meson.build
  23. 12
      test cases/linuxlike/5 dependency versions/meson.build
  24. 9
      test cases/osx/4 framework/meson.build

@ -55,6 +55,8 @@ skip_commits:
install:
- cmd: set "ORIG_PATH=%PATH%"
# Boost 1.56.0: https://www.appveyor.com/docs/build-environment/#boost
#- cmd: set "BOOST_ROOT=C:\Libraries\boost"
# Use the x86 python only when building for x86 for the cpython tests.
# For all other archs (including, say, arm), use the x64 python.
- ps: (new-object net.webclient).DownloadFile('https://www.dropbox.com/s/bbzvepq85hv47x1/ninja.exe?dl=1', 'C:\projects\meson\ninja.exe')

@ -429,7 +429,7 @@ class Backend:
break
commands += ['--pkg', dep.name]
elif isinstance(dep, dependencies.ExternalLibrary):
commands += dep.get_lang_args('vala')
commands += dep.get_link_args('vala')
else:
commands += dep.get_compile_args()
# Qt needs -fPIC for executables

@ -29,26 +29,24 @@ from ..environment import Environment
def autodetect_vs_version(build):
vs_version = os.getenv('VisualStudioVersion', None)
if vs_version:
if vs_version == '14.0':
from mesonbuild.backend.vs2015backend import Vs2015Backend
return Vs2015Backend(build)
if vs_version == '15.0':
from mesonbuild.backend.vs2017backend import Vs2017Backend
return Vs2017Backend(build)
raise MesonException('Could not detect Visual Studio (unknown Visual Studio version: "{}")!\n'
'Please specify the exact backend to use.'.format(vs_version))
vs_install_dir = os.getenv('VSINSTALLDIR', None)
if not vs_install_dir:
raise MesonException('Could not detect Visual Studio (neither VisualStudioVersion nor VSINSTALLDIR set in '
'environment)!\nPlease specify the exact backend to use.')
if not vs_version and not vs_install_dir:
raise MesonException('Could not detect Visual Studio: VisualStudioVersion and VSINSTALLDIR are unset!\n'
'Are we inside a Visual Studio build environment? '
'You can also try specifying the exact backend to use.')
# VisualStudioVersion is set since Visual Studio 12.0, but sometimes
# vcvarsall.bat doesn't set it, so also use VSINSTALLDIR
if vs_version == '14.0' or 'Visual Studio 14' in vs_install_dir:
from mesonbuild.backend.vs2015backend import Vs2015Backend
return Vs2015Backend(build)
if vs_version == '15.0' or 'Visual Studio 17' in vs_install_dir or \
'Visual Studio\\2017' in vs_install_dir:
from mesonbuild.backend.vs2017backend import Vs2017Backend
return Vs2017Backend(build)
if 'Visual Studio 10.0' in vs_install_dir:
return Vs2010Backend(build)
raise MesonException('Could not detect Visual Studio (unknown VSINSTALLDIR: "{}")!\n'
'Please specify the exact backend to use.'.format(vs_install_dir))
raise MesonException('Could not detect Visual Studio using VisualStudioVersion: {!r} or VSINSTALLDIR: {!r}!\n'
'Please specify the exact backend to use.'.format(vs_version, vs_install_dir))
def split_o_flags_args(args):
"""

@ -24,4 +24,6 @@ class Vs2017Backend(Vs2010Backend):
self.platform_toolset = 'v141'
self.vs_version = '2017'
# WindowsSDKVersion should be set by command prompt.
self.windows_target_platform_version = os.getenv('WindowsSDKVersion', None).rstrip('\\')
sdk_version = os.environ.get('WindowsSDKVersion', None)
if sdk_version:
self.windows_target_platform_version = sdk_version.rstrip('\\')

@ -806,7 +806,7 @@ class BuildTarget(Target):
self.external_deps.append(extpart)
# Deps of deps.
self.add_deps(dep.ext_deps)
elif isinstance(dep, dependencies.Dependency):
elif isinstance(dep, dependencies.ExternalDependency):
self.external_deps.append(dep)
self.process_sourcelist(dep.get_sources())
elif isinstance(dep, BuildTarget):

@ -1773,7 +1773,7 @@ class ValaCompiler(Compiler):
for d in extra_dirs:
vapi = os.path.join(d, libname + '.vapi')
if os.path.isfile(vapi):
return vapi
return [vapi]
mlog.debug('Searched {!r} and {!r} wasn\'t found'.format(extra_dirs, libname))
return None

@ -13,8 +13,9 @@
# limitations under the License.
from .base import ( # noqa: F401
Dependency, DependencyException, DependencyMethods, ExternalProgram, ExternalLibrary, ExtraFrameworkDependency,
InternalDependency, PkgConfigDependency, find_external_dependency, get_dep_identifier, packages)
Dependency, DependencyException, DependencyMethods, ExternalProgram,
ExternalDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency,
PkgConfigDependency, find_external_dependency, get_dep_identifier, packages)
from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency
from .misc import BoostDependency, Python3Dependency, ThreadDependency
from .platform import AppleFrameworks

@ -52,10 +52,17 @@ class DependencyMethods(Enum):
class Dependency:
def __init__(self, type_name, kwargs):
self.name = "null"
self.language = None
self.version = 'none'
self.language = None # None means C-like
self.is_found = False
self.type_name = type_name
method = DependencyMethods(kwargs.get('method', 'auto'))
self.compile_args = []
self.link_args = []
self.sources = []
method = kwargs.get('method', 'auto')
if method not in [e.value for e in DependencyMethods]:
raise DependencyException('method {!r} is invalid'.format(method))
method = DependencyMethods(method)
# 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.
@ -64,7 +71,7 @@ class Dependency:
elif method in self.get_methods():
self.methods = [method]
else:
raise MesonException(
raise DependencyException(
'Unsupported detection method: {}, allowed methods are {}'.format(
method.value,
mlog.format_list(map(lambda x: x.value, [DependencyMethods.AUTO] + self.get_methods()))))
@ -74,10 +81,10 @@ class Dependency:
return s.format(self.__class__.__name__, self.name, self.is_found)
def get_compile_args(self):
return []
return self.compile_args
def get_link_args(self):
return []
return self.link_args
def found(self):
return self.is_found
@ -85,7 +92,7 @@ class Dependency:
def get_sources(self):
"""Source files that need to be added to the target.
As an example, gtest-all.cc when using GTest."""
return []
return self.sources
def get_methods(self):
return [DependencyMethods.AUTO]
@ -93,6 +100,9 @@ class Dependency:
def get_name(self):
return self.name
def get_version(self):
return self.version
def get_exe_args(self, compiler):
return []
@ -100,7 +110,7 @@ class Dependency:
return False
def get_pkgconfig_variable(self, variable_name):
raise MesonException('Tried to get a pkg-config variable from a non-pkgconfig dependency.')
raise NotImplementedError('{!r} is not a pkgconfig dependency'.format(self.name))
class InternalDependency(Dependency):
@ -115,41 +125,52 @@ class InternalDependency(Dependency):
self.sources = sources
self.ext_deps = ext_deps
def get_compile_args(self):
return self.compile_args
def get_link_args(self):
return self.link_args
class ExternalDependency(Dependency):
def __init__(self, type_name, environment, language, kwargs):
super().__init__(type_name, kwargs)
self.env = environment
self.name = type_name # default
self.is_found = False
self.language = language
if language and language not in self.env.coredata.compilers:
m = self.name.capitalize() + ' requires a {} compiler'
raise DependencyException(m.format(language.capitalize()))
self.version_reqs = kwargs.get('version', None)
self.required = kwargs.get('required', True)
self.silent = kwargs.get('silent', False)
self.static = kwargs.get('static', False)
if not isinstance(self.static, bool):
raise DependencyException('Static keyword must be boolean')
# Is this dependency for cross-compilation?
if 'native' in kwargs and self.env.is_cross_build():
self.want_cross = not kwargs['native']
else:
self.want_cross = self.env.is_cross_build()
# Set the compiler that will be used by this dependency
# This is only used for configuration checks
if self.want_cross:
compilers = self.env.coredata.cross_compilers
else:
compilers = self.env.coredata.compilers
self.compiler = compilers.get(self.language or 'c', None)
def get_version(self):
return self.version
def get_compiler(self):
return self.compiler
class PkgConfigDependency(Dependency):
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.
class_pkgbin = None
def __init__(self, name, environment, kwargs):
Dependency.__init__(self, 'pkgconfig', kwargs)
super().__init__('pkgconfig', environment, None, kwargs)
self.name = name
self.is_libtool = False
self.version_reqs = kwargs.get('version', None)
self.required = kwargs.get('required', True)
self.static = kwargs.get('static', False)
self.silent = kwargs.get('silent', False)
if not isinstance(self.static, bool):
raise DependencyException('Static keyword must be boolean')
# Store a copy of the pkg-config path on the object itself so it is
# stored in the pickled coredata and recovered.
self.pkgbin = None
self.cargs = []
self.libs = []
if 'native' in kwargs and environment.is_cross_build():
self.want_cross = not kwargs['native']
else:
self.want_cross = environment.is_cross_build()
self.name = name
self.modversion = 'none'
# When finding dependencies for cross-compiling, we don't care about
# the 'native' pkg-config
@ -175,7 +196,6 @@ class PkgConfigDependency(Dependency):
else:
self.pkgbin = PkgConfigDependency.class_pkgbin
self.is_found = False
if not self.pkgbin:
if self.required:
raise DependencyException('Pkg-config not found.')
@ -187,7 +207,7 @@ class PkgConfigDependency(Dependency):
mlog.debug('Determining dependency {!r} with pkg-config executable '
'{!r}'.format(name, self.pkgbin))
ret, self.modversion = self._call_pkgbin(['--modversion', name])
ret, self.version = self._call_pkgbin(['--modversion', name])
if ret != 0:
if self.required:
raise DependencyException('{} dependency {!r} not found'
@ -202,10 +222,10 @@ class PkgConfigDependency(Dependency):
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)
version_compare_many(self.version, self.version_reqs)
if not self.is_found:
found_msg += [mlog.red('NO'),
'found {!r} but need:'.format(self.modversion),
'found {!r} but need:'.format(self.version),
', '.join(["'{}'".format(e) for e in not_found])]
if found:
found_msg += ['; matched:',
@ -214,9 +234,9 @@ class PkgConfigDependency(Dependency):
mlog.log(*found_msg)
if self.required:
m = 'Invalid version of dependency, need {!r} {!r} found {!r}.'
raise DependencyException(m.format(name, not_found, self.modversion))
raise DependencyException(m.format(name, not_found, self.version))
return
found_msg += [mlog.green('YES'), self.modversion]
found_msg += [mlog.green('YES'), self.version]
# Fetch cargs to be used while using this dependency
self._set_cargs()
# Fetch the libraries and library paths needed for using this
@ -240,7 +260,7 @@ class PkgConfigDependency(Dependency):
if ret != 0:
raise DependencyException('Could not generate cargs for %s:\n\n%s' %
(self.name, out))
self.cargs = out.split()
self.compile_args = out.split()
def _set_libs(self):
libcmd = [self.name, '--libs']
@ -250,7 +270,7 @@ class PkgConfigDependency(Dependency):
if ret != 0:
raise DependencyException('Could not generate libs for %s:\n\n%s' %
(self.name, out))
self.libs = []
self.link_args = []
for lib in out.split():
if lib.endswith(".la"):
shared_libname = self.extract_libtool_shlib(lib)
@ -264,7 +284,7 @@ class PkgConfigDependency(Dependency):
'library path' % lib)
lib = shared_lib
self.is_libtool = True
self.libs.append(lib)
self.link_args.append(lib)
def get_pkgconfig_variable(self, variable_name):
ret, out = self._call_pkgbin(['--variable=' + variable_name, self.name])
@ -278,18 +298,6 @@ class PkgConfigDependency(Dependency):
mlog.debug('Got pkgconfig variable %s : %s' % (variable_name, variable))
return variable
def get_modversion(self):
return self.modversion
def get_version(self):
return self.modversion
def get_compile_args(self):
return self.cargs
def get_link_args(self):
return self.libs
def get_methods(self):
return [DependencyMethods.PKGCONFIG]
@ -319,9 +327,6 @@ class PkgConfigDependency(Dependency):
mlog.log('Found Pkg-config:', mlog.red('NO'))
return pkgbin
def found(self):
return self.is_found
def extract_field(self, la_file, fieldname):
with open(la_file) as f:
for line in f:
@ -500,52 +505,37 @@ class ExternalProgram:
return self.name
class ExternalLibrary(Dependency):
# TODO: Add `language` support to all Dependency objects so that languages
# can be exposed for dependencies that support that (i.e., not pkg-config)
def __init__(self, name, link_args, language, silent=False):
super().__init__('external', {})
class ExternalLibrary(ExternalDependency):
def __init__(self, name, link_args, environment, language, silent=False):
super().__init__('external', environment, language, {})
self.name = name
self.language = language
self.is_found = False
self.link_args = []
self.lang_args = []
if link_args:
self.is_found = True
if not isinstance(link_args, list):
link_args = [link_args]
self.lang_args = {language: link_args}
# We special-case Vala for now till the Dependency object gets
# proper support for exposing the language it was written in.
# Without this, vala-specific link args will end up in the C link
# args list if you link to a Vala library.
# This hack use to be in CompilerHolder.find_library().
if language != 'vala':
self.link_args = link_args
self.link_args = link_args
if not silent:
if self.is_found:
mlog.log('Library', mlog.bold(name), 'found:', mlog.green('YES'))
else:
mlog.log('Library', mlog.bold(name), 'found:', mlog.red('NO'))
def found(self):
return self.is_found
def get_name(self):
return self.name
def get_link_args(self):
def get_link_args(self, language=None):
'''
External libraries detected using a compiler must only be used with
compatible code. For instance, Vala libraries (.vapi files) cannot be
used with C code, and not all Rust library types can be linked with
C-like code. Note that C++ libraries *can* be linked with C code with
a C++ linker (and vice-versa).
'''
if self.language == 'vala' and language != 'vala':
return []
return self.link_args
def get_lang_args(self, lang):
if lang in self.lang_args:
return self.lang_args[lang]
return []
class ExtraFrameworkDependency(Dependency):
def __init__(self, name, required, path, kwargs):
Dependency.__init__(self, 'extraframeworks', kwargs)
class ExtraFrameworkDependency(ExternalDependency):
def __init__(self, name, required, path, env, lang, kwargs):
super().__init__('extraframeworks', env, lang, kwargs)
self.name = None
self.required = required
self.detect(name, path)
@ -570,6 +560,7 @@ class ExtraFrameworkDependency(Dependency):
continue
self.path = p
self.name = d
self.is_found = True
return
if not self.found() and self.required:
raise DependencyException('Framework dependency %s not found.' % (name, ))
@ -584,9 +575,6 @@ class ExtraFrameworkDependency(Dependency):
return ['-F' + self.path, '-framework', self.name.split('.')[0]]
return []
def found(self):
return self.name is not None
def get_version(self):
return 'unknown'
@ -611,7 +599,7 @@ def get_dep_identifier(name, kwargs, want_cross):
return identifier
def find_external_dependency(name, environment, kwargs):
def find_external_dependency(name, env, kwargs):
required = kwargs.get('required', True)
if not isinstance(required, bool):
raise DependencyException('Keyword "required" must be a boolean.')
@ -619,20 +607,20 @@ def find_external_dependency(name, environment, kwargs):
raise DependencyException('Keyword "method" must be a string.')
lname = name.lower()
if lname in packages:
dep = packages[lname](environment, kwargs)
dep = packages[lname](env, kwargs)
if required and not dep.found():
raise DependencyException('Dependency "%s" not found' % name)
return dep
pkg_exc = None
pkgdep = None
try:
pkgdep = PkgConfigDependency(name, environment, kwargs)
pkgdep = PkgConfigDependency(name, env, kwargs)
if pkgdep.found():
return pkgdep
except Exception as e:
pkg_exc = e
if mesonlib.is_osx():
fwdep = ExtraFrameworkDependency(name, required, None, kwargs)
fwdep = ExtraFrameworkDependency(name, False, None, env, None, kwargs)
if required and not fwdep.found():
m = 'Dependency {!r} not found, tried Extra Frameworks ' \
'and Pkg-Config:\n\n' + str(pkg_exc)
@ -642,16 +630,3 @@ def find_external_dependency(name, environment, kwargs):
raise pkg_exc
mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
return pkgdep
def dependency_get_compiler(language, environment, kwargs):
if 'native' in kwargs and environment.is_cross_build():
want_cross = not kwargs['native']
else:
want_cross = environment.is_cross_build()
if want_cross:
compilers = environment.coredata.cross_compilers
else:
compilers = environment.coredata.compilers
return compilers.get(language, None)

@ -22,29 +22,20 @@ import shutil
from .. import mlog
from .. import mesonlib
from ..mesonlib import version_compare, Popen_safe
from .base import Dependency, DependencyException, PkgConfigDependency, dependency_get_compiler
from .base import DependencyException, ExternalDependency, PkgConfigDependency
class GTestDependency(Dependency):
class GTestDependency(ExternalDependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'gtest', kwargs)
self.env = environment
super().__init__('gtest', environment, 'cpp', kwargs)
self.main = kwargs.get('main', False)
self.name = 'gtest'
self.include_dir = '/usr/include'
self.src_dirs = ['/usr/src/gtest/src', '/usr/src/googletest/googletest/src']
self.cpp_compiler = dependency_get_compiler('cpp', environment, kwargs)
if self.cpp_compiler is None:
raise DependencyException('Tried to use gtest but a C++ compiler is not defined.')
self.detect()
def found(self):
return self.is_found
def detect(self):
gtest_detect = self.cpp_compiler.find_library("gtest", self.env, [])
gtest_main_detect = self.cpp_compiler.find_library("gtest_main", self.env, [])
if gtest_detect and gtest_main_detect:
self.version = '1.something_maybe'
gtest_detect = self.compiler.find_library("gtest", self.env, [])
gtest_main_detect = self.compiler.find_library("gtest_main", self.env, [])
if gtest_detect and (not self.main or gtest_main_detect):
self.is_found = True
self.compile_args = []
self.link_args = gtest_detect
@ -64,7 +55,6 @@ class GTestDependency(Dependency):
else:
mlog.log('Dependency GTest found:', mlog.red('NO'))
self.is_found = False
return self.is_found
def detect_srcdir(self):
for s in self.src_dirs:
@ -78,37 +68,17 @@ class GTestDependency(Dependency):
return True
return False
def get_compile_args(self):
arr = []
if self.include_dir != '/usr/include':
arr.append('-I' + self.include_dir)
if hasattr(self, 'src_include_dir'):
arr.append('-I' + self.src_include_dir)
return arr
def get_link_args(self):
return self.link_args
def get_version(self):
return '1.something_maybe'
def get_sources(self):
return self.sources
def need_threads(self):
return True
class GMockDependency(Dependency):
class GMockDependency(ExternalDependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'gmock', kwargs)
super().__init__('gmock', environment, 'cpp', kwargs)
self.version = '1.something_maybe'
# GMock may be a library or just source.
# Work with both.
self.name = 'gmock'
cpp_compiler = dependency_get_compiler('cpp', environment, kwargs)
if cpp_compiler is None:
raise DependencyException('Tried to use gmock but a C++ compiler is not defined.')
gmock_detect = cpp_compiler.find_library("gmock", environment, [])
gmock_detect = self.compiler.find_library("gmock", self.env, [])
if gmock_detect:
self.is_found = True
self.compile_args = []
@ -133,29 +103,12 @@ class GMockDependency(Dependency):
self.sources = [all_src]
mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)')
return
mlog.log('Dependency GMock found:', mlog.red('NO'))
self.is_found = False
def get_version(self):
return '1.something_maybe'
def get_compile_args(self):
return self.compile_args
def get_sources(self):
return self.sources
def get_link_args(self):
return self.link_args
def found(self):
return self.is_found
class LLVMDependency(Dependency):
"""LLVM dependency.
class LLVMDependency(ExternalDependency):
"""
LLVM uses a special tool, llvm-config, which has arguments for getting
c args, cxx args, and ldargs as well as version.
"""
@ -182,15 +135,11 @@ class LLVMDependency(Dependency):
__cpp_blacklist = {'-DNDEBUG'}
def __init__(self, environment, kwargs):
super().__init__('llvm-config', 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.
self.language = 'cpp'
self.cargs = []
self.libs = []
super().__init__('llvm-config', environment, 'cpp', kwargs)
self.modules = []
required = kwargs.get('required', True)
# FIXME: Support multiple version requirements ala PkgConfigDependency
req_version = kwargs.get('version', None)
if self.llvmconfig is None:
self.check_llvmconfig(req_version)
@ -201,14 +150,14 @@ class LLVMDependency(Dependency):
else:
mlog.log("No llvm-config found; can't detect dependency")
mlog.log('Dependency LLVM found:', mlog.red('NO'))
if required:
if self.required:
raise DependencyException('Dependency LLVM not found')
return
p, out, err = Popen_safe([self.llvmconfig, '--version'])
if p.returncode != 0:
mlog.debug('stdout: {}\nstderr: {}'.format(out, err))
if required:
if self.required:
raise DependencyException('Dependency LLVM not found')
return
else:
@ -220,12 +169,13 @@ class LLVMDependency(Dependency):
[self.llvmconfig, '--libs', '--ldflags', '--system-libs'])[:2]
if p.returncode != 0:
raise DependencyException('Could not generate libs for LLVM.')
self.libs = shlex.split(out)
self.link_args = shlex.split(out)
p, out = Popen_safe([self.llvmconfig, '--cppflags'])[:2]
if p.returncode != 0:
raise DependencyException('Could not generate includedir for LLVM.')
self.cargs = list(mesonlib.OrderedSet(shlex.split(out)).difference(self.__cpp_blacklist))
cargs = mesonlib.OrderedSet(shlex.split(out))
self.compile_args = list(cargs.difference(self.__cpp_blacklist))
p, out = Popen_safe([self.llvmconfig, '--components'])[:2]
if p.returncode != 0:
@ -237,21 +187,12 @@ class LLVMDependency(Dependency):
if mod not in self.modules:
mlog.log('LLVM module', mod, 'found:', mlog.red('NO'))
self.is_found = False
if required:
if self.required:
raise DependencyException(
'Could not find required LLVM Component: {}'.format(mod))
else:
mlog.log('LLVM module', mod, 'found:', mlog.green('YES'))
def get_version(self):
return self.version
def get_compile_args(self):
return self.cargs
def get_link_args(self):
return self.libs
@classmethod
def check_llvmconfig(cls, version_req):
"""Try to find the highest version of llvm-config."""
@ -261,6 +202,8 @@ class LLVMDependency(Dependency):
out = out.strip()
if p.returncode != 0:
continue
# FIXME: As soon as some llvm-config is found, version checks
# in further dependnecy() calls will be ignored
if version_req:
if version_compare(out, version_req, strict=True):
if cls.__best_found and version_compare(out, '<={}'.format(cls.__best_found), strict=True):
@ -288,8 +231,12 @@ class LLVMDependency(Dependency):
class ValgrindDependency(PkgConfigDependency):
def __init__(self, environment, kwargs):
PkgConfigDependency.__init__(self, 'valgrind', environment, kwargs)
'''
Consumers of Valgrind usually only need the compile args and do not want to
link to its (static) libraries.
'''
def __init__(self, env, kwargs):
super().__init__('valgrind', env, None, kwargs)
def get_link_args(self):
return []

@ -23,25 +23,19 @@ from .. import mlog
from .. import mesonlib
from ..environment import detect_cpu_family
from .base import Dependency, DependencyException, DependencyMethods, ExtraFrameworkDependency, PkgConfigDependency
from .base import DependencyException, DependencyMethods
from .base import ExternalDependency, ExtraFrameworkDependency, PkgConfigDependency
class BoostDependency(Dependency):
class BoostDependency(ExternalDependency):
# Some boost libraries have different names for
# their sources and libraries. This dict maps
# between the two.
name2lib = {'test': 'unit_test_framework'}
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'boost', kwargs)
self.name = 'boost'
self.environment = environment
super().__init__('boost', environment, 'cpp', kwargs)
self.libdir = ''
self.static = kwargs.get('static', False)
if 'native' in kwargs and environment.is_cross_build():
self.want_cross = not kwargs['native']
else:
self.want_cross = environment.is_cross_build()
try:
self.boost_root = os.environ['BOOST_ROOT']
if not os.path.isabs(self.boost_root):
@ -72,7 +66,7 @@ class BoostDependency(Dependency):
self.detect_version()
self.requested_modules = self.get_requested(kwargs)
module_str = ', '.join(self.requested_modules)
if self.version is not None:
if self.is_found:
self.detect_src_modules()
self.detect_lib_modules()
self.validate_requested()
@ -83,9 +77,6 @@ class BoostDependency(Dependency):
mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info)
else:
mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
if 'cpp' not in self.environment.coredata.compilers:
raise DependencyException('Tried to use Boost but a C++ compiler is not defined.')
self.cpp_compiler = self.environment.coredata.compilers['cpp']
def detect_win_root(self):
globtext = 'c:\\local\\boost_*'
@ -130,13 +121,13 @@ class BoostDependency(Dependency):
# names in order to handle cases like cross-compiling where we
# might have a different sysroot.
if not include_dir.endswith(('/usr/include', '/usr/local/include')):
args.append("".join(self.cpp_compiler.get_include_args(include_dir, True)))
args.append("".join(self.compiler.get_include_args(include_dir, True)))
return args
def get_requested(self, kwargs):
candidates = kwargs.get('modules', [])
if isinstance(candidates, str):
return [candidates]
if not isinstance(candidates, list):
candidates = [candidates]
for c in candidates:
if not isinstance(c, str):
raise DependencyException('Boost module argument is not a string.')
@ -145,19 +136,13 @@ class BoostDependency(Dependency):
def validate_requested(self):
for m in self.requested_modules:
if m not in self.src_modules:
raise DependencyException('Requested Boost module "%s" not found.' % m)
def found(self):
return self.version is not None
def get_version(self):
return self.version
msg = 'Requested Boost module {!r} not found'
raise DependencyException(msg.format(m))
def detect_version(self):
try:
ifile = open(os.path.join(self.boost_inc_subdir, 'version.hpp'))
except FileNotFoundError:
self.version = None
return
with ifile:
for line in ifile:
@ -165,8 +150,8 @@ class BoostDependency(Dependency):
ver = line.split()[-1]
ver = ver[1:-1]
self.version = ver.replace('_', '.')
self.is_found = True
return
self.version = None
def detect_src_modules(self):
for entry in os.listdir(self.boost_inc_subdir):
@ -180,7 +165,7 @@ class BoostDependency(Dependency):
return self.detect_lib_modules_nix()
def detect_lib_modules_win(self):
arch = detect_cpu_family(self.environment.coredata.compilers)
arch = detect_cpu_family(self.env.coredata.compilers)
# Guess the libdir
if arch == 'x86':
gl = 'lib32*'
@ -254,10 +239,10 @@ class BoostDependency(Dependency):
module = BoostDependency.name2lib.get(module, module)
libname = 'boost_' + module
# The compiler's library detector is the most reliable so use that first.
default_detect = self.cpp_compiler.find_library(libname, self.environment, [])
default_detect = self.compiler.find_library(libname, self.env, [])
if default_detect is not None:
if module == 'unit_testing_framework':
emon_args = self.cpp_compiler.find_library('boost_test_exec_monitor')
emon_args = self.compiler.find_library('boost_test_exec_monitor')
else:
emon_args = None
args += default_detect
@ -286,9 +271,9 @@ class BoostDependency(Dependency):
return 'thread' in self.requested_modules
class ThreadDependency(Dependency):
class ThreadDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('threads', {})
super().__init__('threads', environment, None, {})
self.name = 'threads'
self.is_found = True
mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'))
@ -300,19 +285,18 @@ class ThreadDependency(Dependency):
return 'unknown'
class Python3Dependency(Dependency):
class Python3Dependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('python3', kwargs)
super().__init__('python3', environment, None, kwargs)
self.name = 'python3'
self.is_found = False
# We can only be sure that it is Python 3 at this point
self.version = '3'
if DependencyMethods.PKGCONFIG in self.methods:
try:
pkgdep = PkgConfigDependency('python3', environment, kwargs)
if pkgdep.found():
self.cargs = pkgdep.cargs
self.libs = pkgdep.libs
self.compile_args = pkgdep.get_compile_args()
self.link_args = pkgdep.get_link_args()
self.version = pkgdep.get_version()
self.is_found = True
return
@ -324,10 +308,11 @@ class Python3Dependency(Dependency):
elif mesonlib.is_osx() and DependencyMethods.EXTRAFRAMEWORK in self.methods:
# In OSX the Python 3 framework does not have a version
# number in its name.
fw = ExtraFrameworkDependency('python', False, None, kwargs)
fw = ExtraFrameworkDependency('python', False, None, self.env,
self.language, kwargs)
if fw.found():
self.cargs = fw.get_compile_args()
self.libs = fw.get_link_args()
self.compile_args = fw.get_compile_args()
self.link_args = fw.get_link_args()
self.is_found = True
if self.is_found:
mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'))
@ -359,23 +344,17 @@ class Python3Dependency(Dependency):
return
inc = sysconfig.get_path('include')
platinc = sysconfig.get_path('platinclude')
self.cargs = ['-I' + inc]
self.compile_args = ['-I' + inc]
if inc != platinc:
self.cargs.append('-I' + platinc)
self.compile_args.append('-I' + platinc)
# Nothing exposes this directly that I coulf find
basedir = sysconfig.get_config_var('base')
vernum = sysconfig.get_config_var('py_version_nodot')
self.libs = ['-L{}/libs'.format(basedir),
'-lpython{}'.format(vernum)]
self.link_args = ['-L{}/libs'.format(basedir),
'-lpython{}'.format(vernum)]
self.version = sysconfig.get_config_var('py_version_short')
self.is_found = True
def get_compile_args(self):
return self.cargs
def get_link_args(self):
return self.libs
def get_methods(self):
if mesonlib.is_windows():
return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSCONFIG]
@ -383,6 +362,3 @@ class Python3Dependency(Dependency):
return [DependencyMethods.PKGCONFIG, DependencyMethods.EXTRAFRAMEWORK]
else:
return [DependencyMethods.PKGCONFIG]
def get_version(self):
return self.version

@ -17,25 +17,21 @@
from .. import mesonlib
from .base import Dependency, DependencyException
from .base import ExternalDependency, DependencyException
class AppleFrameworks(Dependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'appleframeworks', kwargs)
class AppleFrameworks(ExternalDependency):
def __init__(self, env, kwargs):
super().__init__('appleframeworks', env, None, kwargs)
modules = kwargs.get('modules', [])
if isinstance(modules, str):
modules = [modules]
if not modules:
raise DependencyException("AppleFrameworks dependency requires at least one module.")
self.frameworks = modules
def get_link_args(self):
args = []
# FIXME: Use self.compiler to check if the frameworks are available
for f in self.frameworks:
args.append('-framework')
args.append(f)
return args
self.link_args += ['-framework', f]
def found(self):
return mesonlib.is_osx()

@ -26,24 +26,22 @@ from .. import mesonlib
from ..mesonlib import MesonException, Popen_safe, version_compare
from ..environment import for_windows
from .base import (Dependency, DependencyException, DependencyMethods,
ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency)
from .base import DependencyException, DependencyMethods
from .base import ExternalDependency, ExternalProgram
from .base import ExtraFrameworkDependency, PkgConfigDependency
class GLDependency(Dependency):
class GLDependency(ExternalDependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'gl', kwargs)
self.is_found = False
self.cargs = []
self.linkargs = []
super().__init__('gl', environment, None, kwargs)
if DependencyMethods.PKGCONFIG in self.methods:
try:
pcdep = PkgConfigDependency('gl', environment, kwargs)
if pcdep.found():
self.type_name = 'pkgconfig'
self.is_found = True
self.cargs = pcdep.get_compile_args()
self.linkargs = pcdep.get_link_args()
self.compile_args = pcdep.get_compile_args()
self.link_args = pcdep.get_link_args()
self.version = pcdep.get_version()
return
except Exception:
@ -51,21 +49,19 @@ class GLDependency(Dependency):
if DependencyMethods.SYSTEM in self.methods:
if mesonlib.is_osx():
self.is_found = True
self.linkargs = ['-framework', 'OpenGL']
self.version = '1' # FIXME
# FIXME: Use AppleFrameworks dependency
self.link_args = ['-framework', 'OpenGL']
# FIXME: Detect version using self.compiler
self.version = '1'
return
if mesonlib.is_windows():
self.is_found = True
self.linkargs = ['-lopengl32']
self.version = '1' # FIXME: unfixable?
# FIXME: Use self.compiler.find_library()
self.link_args = ['-lopengl32']
# FIXME: Detect version using self.compiler
self.version = '1'
return
def get_link_args(self):
return self.linkargs
def get_version(self):
return self.version
def get_methods(self):
if mesonlib.is_osx() or mesonlib.is_windows():
return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM]
@ -73,10 +69,9 @@ class GLDependency(Dependency):
return [DependencyMethods.PKGCONFIG]
class GnuStepDependency(Dependency):
class GnuStepDependency(ExternalDependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'gnustep', kwargs)
self.required = kwargs.get('required', True)
super().__init__('gnustep', environment, 'objc', kwargs)
self.modules = kwargs.get('modules', [])
self.detect()
@ -85,11 +80,9 @@ class GnuStepDependency(Dependency):
try:
gp = Popen_safe([self.confprog, '--help'])[0]
except (FileNotFoundError, PermissionError):
self.args = None
mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)')
return
if gp.returncode != 0:
self.args = None
mlog.log('Dependency GnuStep found:', mlog.red('NO'))
return
if 'gui' in self.modules:
@ -100,12 +93,13 @@ class GnuStepDependency(Dependency):
if fp.returncode != 0:
raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr))
args = flagtxt.split()
self.args = self.filter_arsg(args)
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.libs = self.weird_filter(libtxt.split())
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)
@ -115,7 +109,7 @@ 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_arsg(self, args):
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."""
result = []
@ -157,25 +151,10 @@ why. As a hack filter out everything that is not a flag."""
''.format(self.confprog, var))
return o.strip()
def found(self):
return self.args is not None
def get_version(self):
return self.version
def get_compile_args(self):
if self.args is None:
return []
return self.args
def get_link_args(self):
return self.libs
class QtBaseDependency(Dependency):
class QtBaseDependency(ExternalDependency):
def __init__(self, name, env, kwargs):
Dependency.__init__(self, name, kwargs)
self.name = name
super().__init__(name, env, 'cpp', kwargs)
self.qtname = name.capitalize()
self.qtver = name[-1]
if self.qtver == "4":
@ -184,16 +163,7 @@ class QtBaseDependency(Dependency):
self.qtpkgname = self.qtname
self.root = '/usr'
self.bindir = None
self.silent = kwargs.get('silent', False)
# We store the value of required here instead of passing it on to
# PkgConfigDependency etc because we want to try the qmake-based
# fallback as well.
self.required = kwargs.pop('required', True)
kwargs['required'] = False
mods = kwargs.get('modules', [])
self.cargs = []
self.largs = []
self.is_found = False
if isinstance(mods, str):
mods = [mods]
if not mods:
@ -207,16 +177,16 @@ class QtBaseDependency(Dependency):
methods = []
# Prefer pkg-config, then fallback to `qmake -query`
if DependencyMethods.PKGCONFIG in self.methods:
self._pkgconfig_detect(mods, env, kwargs)
self._pkgconfig_detect(mods, kwargs)
methods.append('pkgconfig')
if not self.is_found and DependencyMethods.QMAKE in self.methods:
from_text = self._qmake_detect(mods, env, kwargs)
from_text = self._qmake_detect(mods, kwargs)
methods.append('qmake-' + self.name)
methods.append('qmake')
if not self.is_found:
# Reset compile args and link args
self.cargs = []
self.largs = []
self.compile_args = []
self.link_args = []
from_text = '(checked {})'.format(mlog.format_list(methods))
self.version = 'none'
if self.required:
@ -244,24 +214,27 @@ class QtBaseDependency(Dependency):
rcc = ExternalProgram('rcc-' + self.name, silent=True)
return moc, uic, rcc
def _pkgconfig_detect(self, mods, env, kwargs):
def _pkgconfig_detect(self, mods, kwargs):
# We set the value of required to False so that we can try the
# qmake-based fallback if pkg-config fails.
kwargs['required'] = False
modules = OrderedDict()
for module in mods:
modules[module] = PkgConfigDependency(self.qtpkgname + module, env, kwargs)
self.is_found = True
modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env, kwargs)
for m in modules.values():
if not m.found():
self.is_found = False
return
self.cargs += m.get_compile_args()
self.largs += m.get_link_args()
self.version = m.modversion
self.compile_args += m.get_compile_args()
self.link_args += m.get_link_args()
self.is_found = True
self.version = m.version
# Try to detect moc, uic, rcc
if 'Core' in modules:
core = modules['Core']
else:
corekwargs = {'required': 'false', 'silent': 'true'}
core = PkgConfigDependency(self.qtpkgname + 'Core', env, corekwargs)
core = PkgConfigDependency(self.qtpkgname + 'Core', self.env, corekwargs)
# Used by self.compilers_detect()
self.bindir = self.get_pkgconfig_host_bins(core)
if not self.bindir:
@ -270,16 +243,16 @@ class QtBaseDependency(Dependency):
if prefix:
self.bindir = os.path.join(prefix, 'bin')
def _find_qmake(self, qmake, env):
def _find_qmake(self, qmake):
# Even when cross-compiling, if we don't get a cross-info qmake, we
# fallback to using the qmake in PATH because that's what we used to do
if env.is_cross_build():
qmake = env.cross_info.config['binaries'].get('qmake', qmake)
if self.env.is_cross_build():
qmake = self.env.cross_info.config['binaries'].get('qmake', qmake)
return ExternalProgram(qmake, silent=True)
def _qmake_detect(self, mods, env, kwargs):
def _qmake_detect(self, mods, kwargs):
for qmake in ('qmake-' + self.name, 'qmake'):
self.qmake = self._find_qmake(qmake, env)
self.qmake = self._find_qmake(qmake)
if not self.qmake.found():
continue
# Check that the qmake is for qt5
@ -293,6 +266,7 @@ class QtBaseDependency(Dependency):
break
else:
# Didn't find qmake :(
self.is_found = False
return
self.version = re.search(self.qtver + '(\.\d+)+', stdo).group(0)
# Query library path, header path, and binary path
@ -308,15 +282,15 @@ class QtBaseDependency(Dependency):
if mesonlib.is_osx():
return self._framework_detect(qvars, mods, kwargs)
incdir = qvars['QT_INSTALL_HEADERS']
self.cargs.append('-I' + incdir)
self.compile_args.append('-I' + incdir)
libdir = qvars['QT_INSTALL_LIBS']
# Used by self.compilers_detect()
self.bindir = self.get_qmake_host_bins(qvars)
self.is_found = True
for module in mods:
mincdir = os.path.join(incdir, 'Qt' + module)
self.cargs.append('-I' + mincdir)
if for_windows(env.is_cross_build(), env):
self.compile_args.append('-I' + mincdir)
if for_windows(self.env.is_cross_build(), self.env):
libfile = os.path.join(libdir, self.qtpkgname + module + '.lib')
if not os.path.isfile(libfile):
# MinGW can link directly to .dll
@ -329,19 +303,20 @@ class QtBaseDependency(Dependency):
if not os.path.isfile(libfile):
self.is_found = False
break
self.largs.append(libfile)
self.link_args.append(libfile)
return qmake
def _framework_detect(self, qvars, modules, kwargs):
libdir = qvars['QT_INSTALL_LIBS']
for m in modules:
fname = 'Qt' + m
fwdep = ExtraFrameworkDependency(fname, kwargs.get('required', True), libdir, kwargs)
fwdep = ExtraFrameworkDependency(fname, False, libdir, self.env,
self.language, kwargs)
self.cargs.append('-F' + libdir)
if fwdep.found():
self.is_found = True
self.cargs += fwdep.get_compile_args()
self.largs += fwdep.get_link_args()
self.compile_args += fwdep.get_compile_args()
self.link_args += fwdep.get_link_args()
# Used by self.compilers_detect()
self.bindir = self.get_qmake_host_bins(qvars)
@ -353,24 +328,9 @@ class QtBaseDependency(Dependency):
else:
return qvars['QT_INSTALL_BINS']
def get_version(self):
return self.version
def get_compile_args(self):
return self.cargs
def get_sources(self):
return []
def get_link_args(self):
return self.largs
def get_methods(self):
return [DependencyMethods.PKGCONFIG, DependencyMethods.QMAKE]
def found(self):
return self.is_found
def get_exe_args(self, compiler):
# Originally this was -fPIE but nowadays the default
# for upstream and distros seems to be -reduce-relocations
@ -408,20 +368,18 @@ class Qt5Dependency(QtBaseDependency):
# There are three different ways of depending on SDL2:
# sdl2-config, pkg-config and OSX framework
class SDL2Dependency(Dependency):
class SDL2Dependency(ExternalDependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'sdl2', kwargs)
self.is_found = False
self.cargs = []
self.linkargs = []
super().__init__('sdl2', environment, None, kwargs)
if DependencyMethods.PKGCONFIG in self.methods:
try:
kwargs['required'] = False
pcdep = PkgConfigDependency('sdl2', environment, kwargs)
if pcdep.found():
self.type_name = 'pkgconfig'
self.is_found = True
self.cargs = pcdep.get_compile_args()
self.linkargs = pcdep.get_link_args()
self.compile_args = pcdep.get_compile_args()
self.link_args = pcdep.get_link_args()
self.version = pcdep.get_version()
return
except Exception as e:
@ -431,9 +389,9 @@ class SDL2Dependency(Dependency):
sdlconf = shutil.which('sdl2-config')
if sdlconf:
stdo = Popen_safe(['sdl2-config', '--cflags'])[1]
self.cargs = stdo.strip().split()
self.compile_args = stdo.strip().split()
stdo = Popen_safe(['sdl2-config', '--libs'])[1]
self.linkargs = stdo.strip().split()
self.link_args = stdo.strip().split()
stdo = Popen_safe(['sdl2-config', '--version'])[1]
self.version = stdo.strip()
self.is_found = True
@ -443,27 +401,16 @@ class SDL2Dependency(Dependency):
mlog.debug('Could not find sdl2-config binary, trying next.')
if DependencyMethods.EXTRAFRAMEWORK in self.methods:
if mesonlib.is_osx():
fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True), None, kwargs)
fwdep = ExtraFrameworkDependency('sdl2', False, None, self.env,
self.language, kwargs)
if fwdep.found():
self.is_found = True
self.cargs = fwdep.get_compile_args()
self.linkargs = fwdep.get_link_args()
self.compile_args = fwdep.get_compile_args()
self.link_args = fwdep.get_link_args()
self.version = '2' # FIXME
return
mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO'))
def get_compile_args(self):
return self.cargs
def get_link_args(self):
return self.linkargs
def found(self):
return self.is_found
def get_version(self):
return self.version
def get_methods(self):
if mesonlib.is_osx():
return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG, DependencyMethods.EXTRAFRAMEWORK]
@ -471,18 +418,15 @@ class SDL2Dependency(Dependency):
return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG]
class WxDependency(Dependency):
class WxDependency(ExternalDependency):
wx_found = None
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'wx', kwargs)
self.is_found = False
# FIXME: use version instead of modversion
self.modversion = 'none'
super().__init__('wx', environment, None, kwargs)
self.version = 'none'
if WxDependency.wx_found is None:
self.check_wxconfig()
if not WxDependency.wx_found:
# FIXME: this message could be printed after Dependncy found
mlog.log("Neither wx-config-3.0 nor wx-config found; can't detect dependency")
return
@ -490,15 +434,14 @@ class WxDependency(Dependency):
p, out = Popen_safe([self.wxc, '--version'])[0:2]
if p.returncode != 0:
mlog.log('Dependency wxwidgets found:', mlog.red('NO'))
self.cargs = []
self.libs = []
else:
self.modversion = out.strip()
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.modversion, version_req, strict=True):
if not version_compare(self.version, version_req, strict=True):
mlog.log('Wxwidgets version %s does not fullfill requirement %s' %
(self.modversion, version_req))
(self.version, version_req))
return
mlog.log('Dependency wxwidgets found:', mlog.green('YES'))
self.is_found = True
@ -509,38 +452,26 @@ class WxDependency(Dependency):
# FIXME: this error should only be raised if required is true
if p.returncode != 0:
raise DependencyException('Could not generate cargs for wxwidgets.')
self.cargs = out.split()
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.libs = out.split()
self.link_args = out.split()
def get_requested(self, kwargs):
modules = 'modules'
if modules not in kwargs:
return []
candidates = kwargs[modules]
if isinstance(candidates, str):
return [candidates]
if not isinstance(candidates, list):
candidates = [candidates]
for c in candidates:
if not isinstance(c, str):
raise DependencyException('wxwidgets module argument is not a string.')
raise DependencyException('wxwidgets module argument is not a string')
return candidates
def get_modversion(self):
return self.modversion
def get_version(self):
return self.modversion
def get_compile_args(self):
return self.cargs
def get_link_args(self):
return self.libs
def check_wxconfig(self):
for wxc in ['wx-config-3.0', 'wx-config']:
try:
@ -555,6 +486,3 @@ class WxDependency(Dependency):
pass
WxDependency.wxconfig_found = False
mlog.log('Found wx-config:', mlog.red('NO'))
def found(self):
return self.is_found

@ -973,7 +973,8 @@ class CompilerHolder(InterpreterObject):
if required and not linkargs:
l = self.compiler.language.capitalize()
raise InterpreterException('{} library {!r} not found'.format(l, libname))
lib = dependencies.ExternalLibrary(libname, linkargs, self.compiler.language)
lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
self.compiler.language)
return ExternalLibraryHolder(lib)
def has_argument_method(self, args, kwargs):

@ -312,7 +312,11 @@ def run(mainfile, args):
else:
mlog.log(mlog.red('\nMeson encountered an error:'))
mlog.log(e)
if os.environ.get('MESON_FORCE_BACKTRACE'):
raise
else:
if os.environ.get('MESON_FORCE_BACKTRACE'):
raise
traceback.print_exc()
return 1
return 0

@ -68,7 +68,7 @@ class GnomeModule(ExtensionModule):
if native_glib_version is None:
glib_dep = PkgConfigDependency('glib-2.0', state.environment,
{'native': True})
native_glib_version = glib_dep.get_modversion()
native_glib_version = glib_dep.get_version()
return native_glib_version
def __print_gresources_warning(self, state):

@ -34,7 +34,7 @@ import time
import multiprocessing
import concurrent.futures as conc
import re
from run_unittests import get_fake_options
from run_unittests import get_fake_options, run_configure_inprocess
from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
from run_tests import ensure_backend_detects_changes
@ -249,18 +249,6 @@ def log_text_file(logfile, testdir, stdo, stde):
executor.shutdown()
raise StopException()
def run_configure_inprocess(commandlist):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
old_stderr = sys.stderr
sys.stderr = mystderr = StringIO()
try:
returncode = mesonmain.run(commandlist[0], commandlist[1:])
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
return returncode, mystdout.getvalue(), mystderr.getvalue()
def run_test_inprocess(testdir):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
@ -383,7 +371,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
return TestResult(validate_install(testdir, install_dir, compiler), BuildStep.validate, stdo, stde, mesonlog, gen_time, build_time, test_time)
def gather_tests(testdir):
tests = [t.replace('\\', '/').split('/', 2)[2] for t in glob(os.path.join(testdir, '*'))]
tests = [t.replace('\\', '/').split('/', 2)[2] for t in glob(testdir + '/*')]
testlist = [(int(t.split()[0]), t) for t in tests]
testlist.sort()
tests = [os.path.join(testdir, t[1]) for t in testlist]
@ -437,7 +425,6 @@ def detect_tests_to_run():
('platform-windows', 'windows', not mesonlib.is_windows() and not mesonlib.is_cygwin()),
('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()),
('framework', 'frameworks', mesonlib.is_osx() or mesonlib.is_windows() or mesonlib.is_cygwin()),
('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()),
('C#', 'csharp', backend is not Backend.ninja or not shutil.which('mcs')),
('vala', 'vala', backend is not Backend.ninja or not shutil.which('valac')),
@ -446,9 +433,17 @@ def detect_tests_to_run():
('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or mesonlib.is_windows() or not have_objc_compiler()),
('fortran', 'fortran', backend is not Backend.ninja or not shutil.which('gfortran')),
('swift', 'swift', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('swiftc')),
('python3', 'python3', backend is not Backend.ninja or not shutil.which('python3')),
('python3', 'python3', backend is not Backend.ninja),
]
return [(name, gather_tests('test cases/' + subdir), skip) for name, subdir, skip in all_tests]
gathered_tests = [(name, gather_tests('test cases/' + subdir), skip) for name, subdir, skip in all_tests]
if mesonlib.is_windows():
# TODO: Set BOOST_ROOT in .appveyor.yml
gathered_tests += [('framework', ['test cases/frameworks/1 boost'], 'BOOST_ROOT' not in os.environ)]
elif mesonlib.is_osx() or mesonlib.is_cygwin():
gathered_tests += [('framework', gather_tests('test cases/frameworks'), True)]
else:
gathered_tests += [('framework', gather_tests('test cases/frameworks'), False)]
return gathered_tests
def run_tests(all_tests, log_name_base, extra_args):
global stop, executor, futures

@ -22,7 +22,9 @@ import subprocess
import tempfile
import platform
from mesonbuild import mesonlib
from mesonbuild import mesonmain
from mesonbuild.environment import detect_ninja
from io import StringIO
from enum import Enum
from glob import glob
@ -118,9 +120,23 @@ def get_fake_options(prefix):
def should_run_linux_cross_tests():
return shutil.which('arm-linux-gnueabihf-gcc-6') and not platform.machine().startswith('arm')
def run_configure_inprocess(commandlist):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
old_stderr = sys.stderr
sys.stderr = mystderr = StringIO()
try:
returncode = mesonmain.run(commandlist[0], commandlist[1:])
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
return returncode, mystdout.getvalue(), mystderr.getvalue()
class FakeEnvironment(object):
def __init__(self):
self.cross_info = None
self.coredata = lambda: None
self.coredata.compilers = {}
def is_cross_build(self):
return False
@ -162,7 +178,7 @@ if __name__ == '__main__':
os.environ.pop('platform')
# Run tests
print('Running unittests.\n')
units = ['InternalTests', 'AllPlatformTests']
units = ['InternalTests', 'AllPlatformTests', 'FailureTests']
if mesonlib.is_linux():
units += ['LinuxlikeTests']
if should_run_linux_cross_tests():

@ -25,16 +25,19 @@ import unittest
from configparser import ConfigParser
from glob import glob
from pathlib import PurePath
import mesonbuild.mlog
import mesonbuild.compilers
import mesonbuild.environment
import mesonbuild.mesonlib
from mesonbuild.mesonlib import is_windows, is_osx, is_cygwin
from mesonbuild.environment import Environment
from mesonbuild.dependencies import DependencyException
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
from run_tests import exe_suffix, get_fake_options, FakeEnvironment
from run_tests import get_builddir_target_args, get_backend_commands, Backend
from run_tests import ensure_backend_detects_changes
from run_tests import ensure_backend_detects_changes, run_configure_inprocess
def get_dynamic_section_entry(fname, entry):
@ -401,8 +404,8 @@ class BasePlatformTests(unittest.TestCase):
# Get the backend
# FIXME: Extract this from argv?
self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja'))
self.meson_command = [sys.executable, os.path.join(src_root, 'meson.py'),
'--backend=' + self.backend.name]
self.meson_args = [os.path.join(src_root, 'meson.py'), '--backend=' + self.backend.name]
self.meson_command = [sys.executable] + self.meson_args
self.mconf_command = [sys.executable, os.path.join(src_root, 'mesonconf.py')]
self.mintro_command = [sys.executable, os.path.join(src_root, 'mesonintrospect.py')]
self.mtest_command = [sys.executable, os.path.join(src_root, 'mesontest.py'), '-C', self.builddir]
@ -452,7 +455,7 @@ class BasePlatformTests(unittest.TestCase):
raise subprocess.CalledProcessError(p.returncode, command)
return output
def init(self, srcdir, extra_args=None, default_args=True):
def init(self, srcdir, extra_args=None, default_args=True, inprocess=False):
self.assertTrue(os.path.exists(srcdir))
if extra_args is None:
extra_args = []
@ -462,14 +465,27 @@ class BasePlatformTests(unittest.TestCase):
if default_args:
args += ['--prefix', self.prefix,
'--libdir', self.libdir]
try:
self._run(self.meson_command + args + extra_args)
except unittest.SkipTest:
raise unittest.SkipTest('Project requested skipping: ' + srcdir)
except:
self._print_meson_log()
raise
self.privatedir = os.path.join(self.builddir, 'meson-private')
if inprocess:
try:
out = run_configure_inprocess(self.meson_args + args + extra_args)[1]
except:
self._print_meson_log()
raise
finally:
# Close log file to satisfy Windows file locking
mesonbuild.mlog.shutdown()
mesonbuild.mlog.log_dir = None
mesonbuild.mlog.log_file = None
else:
try:
out = self._run(self.meson_command + args + extra_args)
except unittest.SkipTest:
raise unittest.SkipTest('Project requested skipping: ' + srcdir)
except:
self._print_meson_log()
raise
return out
def build(self, target=None, extra_args=None):
if extra_args is None:
@ -1196,6 +1212,124 @@ int main(int argc, char **argv) {
self.assertTrue(path.startswith('$ORIGIN'), msg=(each, path))
class FailureTests(BasePlatformTests):
'''
Tests that test failure conditions. Build files here should be dynamically
generated and static tests should go into `test cases/failing*`.
This is useful because there can be many ways in which a particular
function can fail, and creating failing tests for all of them is tedious
and slows down testing.
'''
dnf = "[Dd]ependency.*not found"
def setUp(self):
super().setUp()
self.srcdir = os.path.realpath(tempfile.mkdtemp())
self.mbuild = os.path.join(self.srcdir, 'meson.build')
def tearDown(self):
super().tearDown()
shutil.rmtree(self.srcdir)
def assertMesonRaises(self, contents, match, extra_args=None, langs=None):
'''
Assert that running meson configure on the specified @contents raises
a error message matching regex @match.
'''
if langs is None:
langs = []
with open(self.mbuild, 'w') as f:
f.write("project('failure test', 'c', 'cpp')\n")
for lang in langs:
f.write("add_languages('{}', required : false)\n".format(lang))
f.write(contents)
# Force tracebacks so we can detect them properly
os.environ['MESON_FORCE_BACKTRACE'] = '1'
with self.assertRaisesRegex(DependencyException, match, msg=contents):
# Must run in-process or we'll get a generic CalledProcessError
self.init(self.srcdir, extra_args=extra_args, inprocess=True)
def assertMesonOutputs(self, contents, match, extra_args=None, langs=None):
'''
Assert that running meson configure on the specified @contents outputs
something that matches regex @match.
'''
if langs is None:
langs = []
with open(self.mbuild, 'w') as f:
f.write("project('output test', 'c', 'cpp')\n")
for lang in langs:
f.write("add_languages('{}', required : false)\n".format(lang))
f.write(contents)
# Run in-process for speed and consistency with assertMesonRaises
out = self.init(self.srcdir, extra_args=extra_args, inprocess=True)
self.assertRegex(out, match)
def test_dependency(self):
if not shutil.which('pkg-config'):
raise unittest.SkipTest('pkg-config not found')
a = (("dependency('zlib', method : 'fail')", "'fail' is invalid"),
("dependency('zlib', static : '1')", "[Ss]tatic.*boolean"),
("dependency('zlib', version : 1)", "[Vv]ersion.*string or list"),
("dependency('zlib', required : 1)", "[Rr]equired.*boolean"),
("dependency('zlib', method : 1)", "[Mm]ethod.*string"),
("dependency('zlibfail')", self.dnf),)
for contents, match in a:
self.assertMesonRaises(contents, match)
def test_apple_frameworks_dependency(self):
if not is_osx():
raise unittest.SkipTest('only run on macOS')
self.assertMesonRaises("dependency('appleframeworks')",
"requires at least one module")
def test_sdl2_notfound_dependency(self):
# Want to test failure, so skip if available
if shutil.which('sdl2-config'):
raise unittest.SkipTest('sdl2-config found')
self.assertMesonRaises("dependency('sdl2', method : 'sdlconfig')", self.dnf)
self.assertMesonRaises("dependency('sdl2', method : 'pkg-config')", self.dnf)
def test_gnustep_notfound_dependency(self):
# Want to test failure, so skip if available
if shutil.which('gnustep-config'):
raise unittest.SkipTest('gnustep-config found')
self.assertMesonRaises("dependency('gnustep')",
"(requires a Objc compiler|{})".format(self.dnf),
langs = ['objc'])
def test_wx_notfound_dependency(self):
# Want to test failure, so skip if available
if shutil.which('wx-config-3.0') or shutil.which('wx-config'):
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")
def test_wx_dependency(self):
if not shutil.which('wx-config-3.0') and not shutil.which('wx-config'):
raise unittest.SkipTest('Neither wx-config nor wx-config-3.0 found')
self.assertMesonRaises("dependency('wxwidgets', modules : 1)",
"module argument is not a string")
def test_llvm_dependency(self):
self.assertMesonRaises("dependency('llvm', modules : 'fail')",
"(required.*fail|{})".format(self.dnf))
def test_boost_notfound_dependency(self):
# Can be run even if Boost is found or not
self.assertMesonRaises("dependency('boost', modules : 1)",
"module.*not a string")
self.assertMesonRaises("dependency('boost', modules : 'fail')",
"(fail.*not found|{})".format(self.dnf))
def test_boost_BOOST_ROOT_dependency(self):
# Test BOOST_ROOT; can be run even if Boost is found or not
os.environ['BOOST_ROOT'] = 'relative/path'
self.assertMesonRaises("dependency('boost')",
"(BOOST_ROOT.*absolute|{})".format(self.dnf))
class WindowsTests(BasePlatformTests):
'''
Tests that should run on Cygwin, MinGW, and MSVC

@ -7,4 +7,11 @@ llvm_dep = dependency(
required : true,
)
d = dependency('llvm', modules : 'not-found', required : false)
assert(d.found() == false, 'not-found llvm module found')
# XXX: Version checks are broken, see FIXME in LLVMDependency
#d = dependency('llvm', version : '<0.1', required : false)
#assert(d.found() == false, 'ancient llvm module found')
executable('sum', 'sum.c', dependencies : llvm_dep)

@ -0,0 +1,10 @@
project('sdl2 test', 'c')
sdl2_dep = dependency('sdl2', version : '>=2.0.0')
e = executable('sdl2prog', 'sdl2prog.c', dependencies : sdl2_dep)
test('sdl2test', e)
# Ensure that we can find it with sdl2-config too
configdep = dependency('sdl2', method : 'sdlconfig')

@ -0,0 +1,33 @@
/* vim: set sts=4 sw=4 et : */
#include <stdio.h>
#include <SDL_version.h>
int main(int argc, char *argv[]) {
SDL_version compiled;
SDL_version linked;
SDL_VERSION(&compiled);
SDL_GetVersion(&linked);
if (compiled.major != linked.major) {
fprintf(stderr, "Compiled major '%u' != linked major '%u'",
compiled.major, linked.major);
return -1;
}
if (compiled.minor != linked.minor) {
fprintf(stderr, "Compiled minor '%u' != linked minor '%u'",
compiled.minor, linked.minor);
return -2;
}
#if 0
/* Disabled because sometimes this is 'micro' and sometimes 'patch' */
if (compiled.micro != linked.micro) {
fprintf(stderr, "Compiled micro '%u' != linked micro '%u'",
compiled.micro, linked.micro);
return -3;
}
#endif
return 0;
}

@ -8,16 +8,22 @@ foreach qt : ['qt4', 'qt5']
if qt == 'qt5'
qt_modules += qt5_modules
endif
# Test that invalid modules are indeed not found
fakeqtdep = dependency(qt, modules : ['DefinitelyNotFound'], required : false, method : get_option('method'))
if fakeqtdep.found()
error('Invalid qt dep incorrectly found!')
endif
# Test that partially-invalid modules are indeed not found
fakeqtdep = dependency(qt, modules : ['Core', 'DefinitelyNotFound'], required : false, method : get_option('method'))
if fakeqtdep.found()
error('Invalid qt dep incorrectly found!')
endif
# Ensure that the "no-Core-module-specified" code branch is hit
nocoredep = dependency(qt, modules : ['Gui'], required : qt == 'qt5', method : get_option('method'))
# If qt4 modules are found, test that. qt5 is required.
qtdep = dependency(qt, modules : qt_modules, required : qt == 'qt5', method : get_option('method'))
if qtdep.found()

@ -90,9 +90,15 @@ if meson.is_cross_build()
assert(native_prefix != cross_prefix, 'native prefix == cross_prefix == ' + native_prefix)
endif
objc_found = add_languages('objc', required : false)
foreach d : ['sdl2', 'gnustep', 'wx', 'gl', 'python3', 'boost', 'gtest', 'gmock']
dep = dependency(d, required : false)
if dep.found()
dep.version()
if d == 'gnustep' and not objc_found
message('Skipping gnustep because no ObjC compiler found')
else
dep = dependency(d, required : false)
if dep.found()
dep.version()
endif
endif
endforeach

@ -10,8 +10,13 @@
project('xcode framework test', 'c', default_options : ['libdir=libtest'])
dep_libs = [dependency('appleframeworks', modules : ['OpenGL'], required : true)]
dep_main = [dependency('appleframeworks', modules : ['Foundation'], required : true)]
dep_libs = dependency('appleframeworks', modules : ['OpenGL'], required : false)
if not dep_libs.found()
error('OpenGL framework not found')
endif
assert(dep_libs.type_name() == 'appleframeworks', 'type_name is wrong')
dep_main = dependency('appleframeworks', modules : ['Foundation'])
stlib = static_library('stat', 'stat.c', install : true, dependencies: dep_libs)
exe = executable('prog', 'prog.c', install : true, dependencies: dep_main)

Loading…
Cancel
Save