diff options
-rw-r--r-- | mesonbuild/backend/backends.py | 2 | ||||
-rw-r--r-- | mesonbuild/build.py | 2 | ||||
-rw-r--r-- | mesonbuild/dependencies/__init__.py | 5 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 176 | ||||
-rw-r--r-- | mesonbuild/dependencies/dev.py | 109 | ||||
-rw-r--r-- | mesonbuild/dependencies/misc.py | 73 | ||||
-rw-r--r-- | mesonbuild/dependencies/platform.py | 16 | ||||
-rw-r--r-- | mesonbuild/dependencies/ui.py | 209 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 3 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 2 | ||||
-rwxr-xr-x | run_tests.py | 2 | ||||
-rw-r--r-- | test cases/linuxlike/5 dependency versions/meson.build | 12 | ||||
-rw-r--r-- | test cases/osx/4 framework/meson.build | 9 |
13 files changed, 226 insertions, 394 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 3044ce6..05d6e03 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -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 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2c55ed4..ba30fec 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -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): diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index ec11152..3d41a2b 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -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 diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 139ff39..d727021 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -52,9 +52,13 @@ 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 + self.compile_args = [] + self.link_args = [] + self.sources = [] method = DependencyMethods(kwargs.get('method', 'auto')) # Set the detection method. If the method is set to auto, use any available method. @@ -74,10 +78,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 +89,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 +97,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 +107,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 +122,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-com,pilation? + 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 +193,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 +204,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 +219,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 +231,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 +257,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 +267,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 +281,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 +295,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 +324,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 +502,39 @@ 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 +559,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 +574,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 +598,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 +606,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, required, None, env, 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 +629,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) diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 76d6691..8d64379 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -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.""" @@ -288,8 +229,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 [] diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 6a76ba6..1356ec8 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -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,7 +121,7 @@ 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): @@ -147,17 +138,10 @@ class BoostDependency(Dependency): 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 - 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 +149,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 +164,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 +238,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 +270,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 +284,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 +307,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 +343,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 +361,3 @@ class Python3Dependency(Dependency): return [DependencyMethods.PKGCONFIG, DependencyMethods.EXTRAFRAMEWORK] else: return [DependencyMethods.PKGCONFIG] - - def get_version(self): - return self.version diff --git a/mesonbuild/dependencies/platform.py b/mesonbuild/dependencies/platform.py index cd46412..95ab727 100644 --- a/mesonbuild/dependencies/platform.py +++ b/mesonbuild/dependencies/platform.py @@ -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() diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 3174176..7b276cc 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -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,7 +303,7 @@ 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): @@ -340,8 +314,8 @@ class QtBaseDependency(Dependency): 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 +327,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 +367,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 +388,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 +400,15 @@ 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, 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,14 +416,12 @@ 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: @@ -490,15 +433,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,13 +451,13 @@ 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' @@ -529,18 +471,6 @@ class WxDependency(Dependency): 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 +485,3 @@ class WxDependency(Dependency): pass WxDependency.wxconfig_found = False mlog.log('Found wx-config:', mlog.red('NO')) - - def found(self): - return self.is_found diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 621047c..afe4bf3 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -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): diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 6644ba7..e134acf 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -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): diff --git a/run_tests.py b/run_tests.py index 00c2595..1e70784 100755 --- a/run_tests.py +++ b/run_tests.py @@ -121,6 +121,8 @@ def should_run_linux_cross_tests(): class FakeEnvironment(object): def __init__(self): self.cross_info = None + self.coredata = lambda: None + self.coredata.compilers = {} def is_cross_build(self): return False diff --git a/test cases/linuxlike/5 dependency versions/meson.build b/test cases/linuxlike/5 dependency versions/meson.build index 5c2c262..ad513f2 100644 --- a/test cases/linuxlike/5 dependency versions/meson.build +++ b/test cases/linuxlike/5 dependency versions/meson.build @@ -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 diff --git a/test cases/osx/4 framework/meson.build b/test cases/osx/4 framework/meson.build index 8d93bf9..460b480 100644 --- a/test cases/osx/4 framework/meson.build +++ b/test cases/osx/4 framework/meson.build @@ -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) |