blob: d035176919201a674ccc0f4a3e9ace05bd32d7bd [file] [log] [blame]
# Copyright 2013-2017 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file contains the detection logic for external dependencies useful for
# development purposes, such as testing, debugging, etc..
import os
import re
import shutil
from .. import mlog
from .. import mesonlib
from ..mesonlib import version_compare, Popen_safe, stringlistify, extract_as_list
from .base import (
DependencyException, ExternalDependency, PkgConfigDependency,
strip_system_libdirs, ConfigToolDependency,
)
class GTestDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('gtest', environment, 'cpp', kwargs)
self.main = kwargs.get('main', False)
self.src_dirs = ['/usr/src/gtest/src', '/usr/src/googletest/googletest/src']
self.detect()
def detect(self):
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
if self.main:
self.link_args += gtest_main_detect
self.sources = []
mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)')
elif self.detect_srcdir():
self.is_found = True
self.compile_args = ['-I' + self.src_include_dir]
self.link_args = []
if self.main:
self.sources = [self.all_src, self.main_src]
else:
self.sources = [self.all_src]
mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)')
else:
mlog.log('Dependency GTest found:', mlog.red('NO'))
self.is_found = False
def detect_srcdir(self):
for s in self.src_dirs:
if os.path.exists(s):
self.src_dir = s
self.all_src = mesonlib.File.from_absolute_file(
os.path.join(self.src_dir, 'gtest-all.cc'))
self.main_src = mesonlib.File.from_absolute_file(
os.path.join(self.src_dir, 'gtest_main.cc'))
self.src_include_dir = os.path.normpath(os.path.join(self.src_dir, '..'))
return True
return False
def need_threads(self):
return True
class GMockDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('gmock', environment, 'cpp', kwargs)
self.version = '1.something_maybe'
# GMock may be a library or just source.
# Work with both.
gmock_detect = self.compiler.find_library("gmock", self.env, [])
if gmock_detect:
self.is_found = True
self.compile_args = []
self.link_args = gmock_detect
self.sources = []
mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)')
return
for d in ['/usr/src/googletest/googlemock/src', '/usr/src/gmock/src', '/usr/src/gmock']:
if os.path.exists(d):
self.is_found = True
# Yes, we need both because there are multiple
# versions of gmock that do different things.
d2 = os.path.normpath(os.path.join(d, '..'))
self.compile_args = ['-I' + d, '-I' + d2]
self.link_args = []
all_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock-all.cc'))
main_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock_main.cc'))
if kwargs.get('main', False):
self.sources = [all_src, main_src]
else:
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
class LLVMDependency(ConfigToolDependency):
"""
LLVM uses a special tool, llvm-config, which has arguments for getting
c args, cxx args, and ldargs as well as version.
"""
# Ordered list of llvm-config binaries to try. Start with base, then try
# newest back to oldest (3.5 is arbitrary), and finally the devel version.
# Please note that llvm-config-6.0 is a development snapshot and it should
# not be moved to the beginning of the list. The only difference between
# llvm-config-6.0 and llvm-config-devel is that the former is used by
# Debian and the latter is used by FreeBSD.
tools = [
'llvm-config', # base
'llvm-config-5.0', 'llvm-config50', # latest stable release
'llvm-config-4.0', 'llvm-config40', # old stable releases
'llvm-config-3.9', 'llvm-config39',
'llvm-config-3.8', 'llvm-config38',
'llvm-config-3.7', 'llvm-config37',
'llvm-config-3.6', 'llvm-config36',
'llvm-config-3.5', 'llvm-config35',
'llvm-config-6.0', 'llvm-config-devel', # development snapshot
]
tool_name = 'llvm-config'
__cpp_blacklist = {'-DNDEBUG'}
def __init__(self, environment, kwargs):
# It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0
# the C linker works fine if only using the C API.
super().__init__('config-tool', environment, 'cpp', kwargs)
self.provided_modules = []
self.required_modules = set()
if not self.is_found:
return
self.static = kwargs.get('static', False)
self.provided_modules = self.get_config_value(['--components'], 'modules')
modules = stringlistify(extract_as_list(kwargs, 'modules'))
self.check_components(modules)
opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
self.check_components(opt_modules, required=False)
cargs = set(self.get_config_value(['--cppflags'], 'compile_args'))
self.compile_args = list(cargs.difference(self.__cpp_blacklist))
if version_compare(self.version, '>= 3.9'):
self._set_new_link_args()
else:
self._set_old_link_args()
self.link_args = strip_system_libdirs(environment, self.link_args)
def _set_new_link_args(self):
"""How to set linker args for LLVM versions >= 3.9"""
link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared']
self.link_args = self.get_config_value(
['--libs', '--ldflags'] + link_args + list(self.required_modules),
'link_args')
def _set_old_link_args(self):
"""Setting linker args for older versions of llvm.
Old versions of LLVM bring an extra level of insanity with them.
llvm-config will provide the correct arguments for static linking, but
not for shared-linnking, we have to figure those out ourselves, because
of course we do.
"""
if self.static:
self.link_args = self.get_config_value(
['--libs', '--ldflags', '--system-libs'] + list(self.required_modules),
'link_args')
else:
# llvm-config will provide arguments for static linking, so we get
# to figure out for ourselves what to link with. We'll do that by
# checking in the directory provided by --libdir for a library
# called libLLVM-<ver>.(so|dylib|dll)
libdir = self.get_config_value(['--libdir'], 'link_args')[0]
expected_name = 'libLLVM-{}'.format(self.version)
re_name = re.compile(r'{}.(so|dll|dylib)'.format(expected_name))
for file_ in os.listdir(libdir):
if re_name.match(file_):
self.link_args = ['-L{}'.format(libdir),
'-l{}'.format(os.path.splitext(file_.lstrip('lib'))[0])]
break
else:
raise DependencyException(
'Could not find a dynamically linkable library for LLVM.')
def check_components(self, modules, required=True):
"""Check for llvm components (modules in meson terms).
The required option is whether the module is required, not whether LLVM
is required.
"""
for mod in sorted(set(modules)):
if mod not in self.provided_modules:
mlog.log('LLVM module', mod, 'found:', mlog.red('NO'),
'(optional)' if not required else '')
if required:
self.is_found = False
if self.required:
raise DependencyException(
'Could not find required LLVM Component: {}'.format(mod))
else:
self.required_modules.add(mod)
mlog.log('LLVM module', mod, 'found:', mlog.green('YES'))
def need_threads(self):
return True
class ValgrindDependency(PkgConfigDependency):
'''
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, kwargs)
def get_link_args(self):
return []