The Meson Build System http://mesonbuild.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
11 KiB

# 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 shlex
import shutil
from .. import mlog
from .. import mesonlib
from ..mesonlib import version_compare, Popen_safe
from .base import Dependency, DependencyException, PkgConfigDependency, dependency_get_compiler
class GTestDependency(Dependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'gtest', kwargs)
self.env = environment
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.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
return self.is_found
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 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):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'gmock', kwargs)
# 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, [])
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
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.
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 abitrary), and finally the devel version.
llvm_config_bins = [
'llvm-config', 'llvm-config-4.0', '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-devel',
]
llvmconfig = None
_llvmconfig_found = False
__best_found = None
__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 = []
self.modules = []
required = kwargs.get('required', True)
req_version = kwargs.get('version', None)
if self.llvmconfig is None:
self.check_llvmconfig(req_version)
if not self._llvmconfig_found:
if self.__best_found is not None:
mlog.log('found {!r} but need:'.format(self.__best_found),
req_version)
else:
mlog.log("No llvm-config found; can't detect dependency")
mlog.log('Dependency LLVM found:', mlog.red('NO'))
if 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:
raise DependencyException('Dependency LLVM not found')
return
else:
self.version = out.strip()
mlog.log('Dependency LLVM found:', mlog.green('YES'))
self.is_found = True
p, out = Popen_safe(
[self.llvmconfig, '--libs', '--ldflags', '--system-libs'])[:2]
if p.returncode != 0:
raise DependencyException('Could not generate libs for LLVM.')
self.libs = 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))
p, out = Popen_safe([self.llvmconfig, '--components'])[:2]
if p.returncode != 0:
raise DependencyException('Could not generate modules for LLVM.')
self.modules = shlex.split(out)
modules = mesonlib.stringlistify(kwargs.get('modules', []))
for mod in modules:
if mod not in self.modules:
mlog.log('LLVM module', mod, 'found:', mlog.red('NO'))
self.is_found = False
if 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."""
for llvmconfig in cls.llvm_config_bins:
try:
p, out = Popen_safe([llvmconfig, '--version'])[0:2]
out = out.strip()
if p.returncode != 0:
continue
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):
continue
cls.__best_found = out
cls.llvmconfig = llvmconfig
else:
# If no specific version is requested use the first version
# found, since that should be the best.
cls.__best_found = out
cls.llvmconfig = llvmconfig
break
except (FileNotFoundError, PermissionError):
pass
if cls.__best_found:
mlog.log('Found llvm-config:',
mlog.bold(shutil.which(cls.llvmconfig)),
'({})'.format(out.strip()))
cls._llvmconfig_found = True
else:
cls.llvmconfig = False
def need_threads(self):
return True
class ValgrindDependency(PkgConfigDependency):
def __init__(self, environment, kwargs):
PkgConfigDependency.__init__(self, 'valgrind', environment, kwargs)
def get_link_args(self):
return []