dev.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. # Copyright 2013-2017 The Meson development team
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. # Unless required by applicable law or agreed to in writing, software
  7. # distributed under the License is distributed on an "AS IS" BASIS,
  8. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. # See the License for the specific language governing permissions and
  10. # limitations under the License.
  11. # This file contains the detection logic for external dependencies useful for
  12. # development purposes, such as testing, debugging, etc..
  13. import os
  14. import shlex
  15. import shutil
  16. from .. import mlog
  17. from .. import mesonlib
  18. from ..mesonlib import version_compare, Popen_safe
  19. from .base import DependencyException, ExternalDependency, PkgConfigDependency
  20. class GTestDependency(ExternalDependency):
  21. def __init__(self, environment, kwargs):
  22. super().__init__('gtest', environment, 'cpp', kwargs)
  23. self.main = kwargs.get('main', False)
  24. self.src_dirs = ['/usr/src/gtest/src', '/usr/src/googletest/googletest/src']
  25. self.detect()
  26. def detect(self):
  27. self.version = '1.something_maybe'
  28. gtest_detect = self.compiler.find_library("gtest", self.env, [])
  29. gtest_main_detect = self.compiler.find_library("gtest_main", self.env, [])
  30. if gtest_detect and (not self.main or gtest_main_detect):
  31. self.is_found = True
  32. self.compile_args = []
  33. self.link_args = gtest_detect
  34. if self.main:
  35. self.link_args += gtest_main_detect
  36. self.sources = []
  37. mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)')
  38. elif self.detect_srcdir():
  39. self.is_found = True
  40. self.compile_args = ['-I' + self.src_include_dir]
  41. self.link_args = []
  42. if self.main:
  43. self.sources = [self.all_src, self.main_src]
  44. else:
  45. self.sources = [self.all_src]
  46. mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)')
  47. else:
  48. mlog.log('Dependency GTest found:', mlog.red('NO'))
  49. self.is_found = False
  50. def detect_srcdir(self):
  51. for s in self.src_dirs:
  52. if os.path.exists(s):
  53. self.src_dir = s
  54. self.all_src = mesonlib.File.from_absolute_file(
  55. os.path.join(self.src_dir, 'gtest-all.cc'))
  56. self.main_src = mesonlib.File.from_absolute_file(
  57. os.path.join(self.src_dir, 'gtest_main.cc'))
  58. self.src_include_dir = os.path.normpath(os.path.join(self.src_dir, '..'))
  59. return True
  60. return False
  61. def need_threads(self):
  62. return True
  63. class GMockDependency(ExternalDependency):
  64. def __init__(self, environment, kwargs):
  65. super().__init__('gmock', environment, 'cpp', kwargs)
  66. self.version = '1.something_maybe'
  67. # GMock may be a library or just source.
  68. # Work with both.
  69. gmock_detect = self.compiler.find_library("gmock", self.env, [])
  70. if gmock_detect:
  71. self.is_found = True
  72. self.compile_args = []
  73. self.link_args = gmock_detect
  74. self.sources = []
  75. mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)')
  76. return
  77. for d in ['/usr/src/googletest/googlemock/src', '/usr/src/gmock/src', '/usr/src/gmock']:
  78. if os.path.exists(d):
  79. self.is_found = True
  80. # Yes, we need both because there are multiple
  81. # versions of gmock that do different things.
  82. d2 = os.path.normpath(os.path.join(d, '..'))
  83. self.compile_args = ['-I' + d, '-I' + d2]
  84. self.link_args = []
  85. all_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock-all.cc'))
  86. main_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock_main.cc'))
  87. if kwargs.get('main', False):
  88. self.sources = [all_src, main_src]
  89. else:
  90. self.sources = [all_src]
  91. mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)')
  92. return
  93. mlog.log('Dependency GMock found:', mlog.red('NO'))
  94. self.is_found = False
  95. class LLVMDependency(ExternalDependency):
  96. """
  97. LLVM uses a special tool, llvm-config, which has arguments for getting
  98. c args, cxx args, and ldargs as well as version.
  99. """
  100. # Ordered list of llvm-config binaries to try. Start with base, then try
  101. # newest back to oldest (3.5 is abitrary), and finally the devel version.
  102. # Please note that llvm-config-5.0 is a development snapshot and it should
  103. # not be moved to the beginning of the list. The only difference between
  104. # llvm-config-5.0 and llvm-config-devel is that the former is used by
  105. # Debian and the latter is used by FreeBSD.
  106. llvm_config_bins = [
  107. 'llvm-config', # base
  108. 'llvm-config-4.0', 'llvm-config40', # latest stable release
  109. 'llvm-config-3.9', 'llvm-config39', # old stable releases
  110. 'llvm-config-3.8', 'llvm-config38',
  111. 'llvm-config-3.7', 'llvm-config37',
  112. 'llvm-config-3.6', 'llvm-config36',
  113. 'llvm-config-3.5', 'llvm-config35',
  114. 'llvm-config-5.0', 'llvm-config-devel', # development snapshot
  115. ]
  116. llvmconfig = None
  117. _llvmconfig_found = False
  118. __best_found = None
  119. __cpp_blacklist = {'-DNDEBUG'}
  120. def __init__(self, environment, kwargs):
  121. # It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0
  122. # the C linker works fine if only using the C API.
  123. super().__init__('llvm-config', environment, 'cpp', kwargs)
  124. self.modules = []
  125. # FIXME: Support multiple version requirements ala PkgConfigDependency
  126. req_version = kwargs.get('version', None)
  127. if self.llvmconfig is None:
  128. self.check_llvmconfig(req_version)
  129. if not self._llvmconfig_found:
  130. if self.__best_found is not None:
  131. mlog.log('found {!r} but need:'.format(self.__best_found),
  132. req_version)
  133. else:
  134. mlog.log("No llvm-config found; can't detect dependency")
  135. mlog.log('Dependency LLVM found:', mlog.red('NO'))
  136. if self.required:
  137. raise DependencyException('Dependency LLVM not found')
  138. return
  139. p, out, err = Popen_safe([self.llvmconfig, '--version'])
  140. if p.returncode != 0:
  141. mlog.debug('stdout: {}\nstderr: {}'.format(out, err))
  142. if self.required:
  143. raise DependencyException('Dependency LLVM not found')
  144. return
  145. else:
  146. self.version = out.strip()
  147. mlog.log('Dependency LLVM found:', mlog.green('YES'))
  148. self.is_found = True
  149. p, out = Popen_safe(
  150. [self.llvmconfig, '--libs', '--ldflags', '--system-libs'])[:2]
  151. if p.returncode != 0:
  152. raise DependencyException('Could not generate libs for LLVM.')
  153. self.link_args = shlex.split(out)
  154. p, out = Popen_safe([self.llvmconfig, '--cppflags'])[:2]
  155. if p.returncode != 0:
  156. raise DependencyException('Could not generate includedir for LLVM.')
  157. cargs = mesonlib.OrderedSet(shlex.split(out))
  158. self.compile_args = list(cargs.difference(self.__cpp_blacklist))
  159. p, out = Popen_safe([self.llvmconfig, '--components'])[:2]
  160. if p.returncode != 0:
  161. raise DependencyException('Could not generate modules for LLVM.')
  162. self.modules = shlex.split(out)
  163. modules = mesonlib.stringlistify(kwargs.get('modules', []))
  164. for mod in modules:
  165. if mod not in self.modules:
  166. mlog.log('LLVM module', mod, 'found:', mlog.red('NO'))
  167. self.is_found = False
  168. if self.required:
  169. raise DependencyException(
  170. 'Could not find required LLVM Component: {}'.format(mod))
  171. else:
  172. mlog.log('LLVM module', mod, 'found:', mlog.green('YES'))
  173. @classmethod
  174. def check_llvmconfig(cls, version_req):
  175. """Try to find the highest version of llvm-config."""
  176. for llvmconfig in cls.llvm_config_bins:
  177. try:
  178. p, out = Popen_safe([llvmconfig, '--version'])[0:2]
  179. out = out.strip()
  180. if p.returncode != 0:
  181. continue
  182. # FIXME: As soon as some llvm-config is found, version checks
  183. # in further dependnecy() calls will be ignored
  184. if version_req:
  185. if version_compare(out, version_req, strict=True):
  186. if cls.__best_found and version_compare(out, '<={}'.format(cls.__best_found), strict=True):
  187. continue
  188. cls.__best_found = out
  189. cls.llvmconfig = llvmconfig
  190. else:
  191. # If no specific version is requested use the first version
  192. # found, since that should be the best.
  193. cls.__best_found = out
  194. cls.llvmconfig = llvmconfig
  195. break
  196. except (FileNotFoundError, PermissionError):
  197. pass
  198. if cls.__best_found:
  199. mlog.log('Found llvm-config:',
  200. mlog.bold(shutil.which(cls.llvmconfig)),
  201. '({})'.format(out.strip()))
  202. cls._llvmconfig_found = True
  203. else:
  204. cls.llvmconfig = False
  205. def need_threads(self):
  206. return True
  207. class ValgrindDependency(PkgConfigDependency):
  208. '''
  209. Consumers of Valgrind usually only need the compile args and do not want to
  210. link to its (static) libraries.
  211. '''
  212. def __init__(self, env, kwargs):
  213. super().__init__('valgrind', env, kwargs)
  214. def get_link_args(self):
  215. return []