dependencies.py 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  1. # Copyright 2013-2015 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
  12. # dependencies. Mostly just uses pkg-config but also contains
  13. # custom logic for packages that don't provide them.
  14. # Currently one file, should probably be split into a
  15. # package before this gets too big.
  16. import re
  17. import os, stat, glob, subprocess, shutil
  18. import sysconfig
  19. from . mesonlib import MesonException
  20. from . import mlog
  21. from . import mesonlib
  22. from .environment import detect_cpu_family
  23. class DependencyException(MesonException):
  24. def __init__(self, *args, **kwargs):
  25. MesonException.__init__(self, *args, **kwargs)
  26. class Dependency():
  27. def __init__(self):
  28. self.name = "null"
  29. self.is_found = False
  30. def get_compile_args(self):
  31. return []
  32. def get_link_args(self):
  33. return []
  34. def found(self):
  35. return self.is_found
  36. def get_sources(self):
  37. """Source files that need to be added to the target.
  38. As an example, gtest-all.cc when using GTest."""
  39. return []
  40. def get_name(self):
  41. return self.name
  42. def get_exe_args(self):
  43. return []
  44. def need_threads(self):
  45. return False
  46. class InternalDependency(Dependency):
  47. def __init__(self, version, incdirs, compile_args, link_args, libraries, sources, ext_deps):
  48. super().__init__()
  49. self.version = version
  50. self.include_directories = incdirs
  51. self.compile_args = compile_args
  52. self.link_args = link_args
  53. self.libraries = libraries
  54. self.sources = sources
  55. self.ext_deps = ext_deps
  56. def get_compile_args(self):
  57. return self.compile_args
  58. def get_link_args(self):
  59. return self.link_args
  60. def get_version(self):
  61. return self.version
  62. class PkgConfigDependency(Dependency):
  63. pkgconfig_found = None
  64. def __init__(self, name, environment, kwargs):
  65. Dependency.__init__(self)
  66. self.is_libtool = False
  67. self.required = kwargs.get('required', True)
  68. self.static = kwargs.get('static', False)
  69. if not isinstance(self.static, bool):
  70. raise DependencyException('Static keyword must be boolean')
  71. self.cargs = []
  72. self.libs = []
  73. if 'native' in kwargs and environment.is_cross_build():
  74. want_cross = not kwargs['native']
  75. else:
  76. want_cross = environment.is_cross_build()
  77. self.name = name
  78. if PkgConfigDependency.pkgconfig_found is None:
  79. self.check_pkgconfig()
  80. self.is_found = False
  81. if not PkgConfigDependency.pkgconfig_found:
  82. if self.required:
  83. raise DependencyException('Pkg-config not found.')
  84. return
  85. if environment.is_cross_build() and want_cross:
  86. if "pkgconfig" not in environment.cross_info.config["binaries"]:
  87. raise DependencyException('Pkg-config binary missing from cross file.')
  88. pkgbin = environment.cross_info.config["binaries"]['pkgconfig']
  89. self.type_string = 'Cross'
  90. else:
  91. pkgbin = 'pkg-config'
  92. self.type_string = 'Native'
  93. mlog.debug('Determining dependency %s with pkg-config executable %s.' % (name, pkgbin))
  94. self.pkgbin = pkgbin
  95. p = subprocess.Popen([pkgbin, '--modversion', name],
  96. stdout=subprocess.PIPE,
  97. stderr=subprocess.PIPE)
  98. out = p.communicate()[0]
  99. if p.returncode != 0:
  100. if self.required:
  101. raise DependencyException('%s dependency %s not found.' % (self.type_string, name))
  102. self.modversion = 'none'
  103. return
  104. self.modversion = out.decode().strip()
  105. found_msg = ['%s dependency' % self.type_string, mlog.bold(name), 'found:']
  106. self.version_requirement = kwargs.get('version', None)
  107. if self.version_requirement is None:
  108. self.is_found = True
  109. else:
  110. if not isinstance(self.version_requirement, str):
  111. raise DependencyException('Version argument must be string.')
  112. self.is_found = mesonlib.version_compare(self.modversion, self.version_requirement)
  113. if not self.is_found:
  114. found_msg += [mlog.red('NO'), 'found {!r}'.format(self.modversion),
  115. 'but need {!r}'.format(self.version_requirement)]
  116. mlog.log(*found_msg)
  117. if self.required:
  118. raise DependencyException(
  119. 'Invalid version of a dependency, needed %s %s found %s.' %
  120. (name, self.version_requirement, self.modversion))
  121. return
  122. found_msg += [mlog.green('YES'), self.modversion]
  123. mlog.log(*found_msg)
  124. # Fetch cargs to be used while using this dependency
  125. self._set_cargs()
  126. # Fetch the libraries and library paths needed for using this
  127. self._set_libs()
  128. def _set_cargs(self):
  129. p = subprocess.Popen([self.pkgbin, '--cflags', self.name],
  130. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  131. out = p.communicate()[0]
  132. if p.returncode != 0:
  133. raise DependencyException('Could not generate cargs for %s:\n\n%s' % \
  134. (self.name, out.decode(errors='ignore')))
  135. self.cargs = out.decode().split()
  136. def _set_libs(self):
  137. libcmd = [self.pkgbin, '--libs']
  138. if self.static:
  139. libcmd.append('--static')
  140. p = subprocess.Popen(libcmd + [self.name],
  141. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  142. out = p.communicate()[0]
  143. if p.returncode != 0:
  144. raise DependencyException('Could not generate libs for %s:\n\n%s' % \
  145. (self.name, out.decode(errors='ignore')))
  146. self.libs = []
  147. for lib in out.decode().split():
  148. if lib.endswith(".la"):
  149. shared_libname = self.extract_libtool_shlib(lib)
  150. shared_lib = os.path.join(os.path.dirname(lib), shared_libname)
  151. if not os.path.exists(shared_lib):
  152. shared_lib = os.path.join(os.path.dirname(lib), ".libs", shared_libname)
  153. if not os.path.exists(shared_lib):
  154. raise DependencyException('Got a libtools specific "%s" dependencies'
  155. 'but we could not compute the actual shared'
  156. 'library path' % lib)
  157. lib = shared_lib
  158. self.is_libtool = True
  159. self.libs.append(lib)
  160. def get_variable(self, variable_name):
  161. p = subprocess.Popen([self.pkgbin, '--variable=%s' % variable_name, self.name],
  162. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  163. out = p.communicate()[0]
  164. variable = ''
  165. if p.returncode != 0:
  166. if self.required:
  167. raise DependencyException('%s dependency %s not found.' %
  168. (self.type_string, self.name))
  169. else:
  170. variable = out.decode().strip()
  171. mlog.debug('return of subprocess : %s' % variable)
  172. return variable
  173. def get_modversion(self):
  174. return self.modversion
  175. def get_version(self):
  176. return self.get_modversion()
  177. def get_compile_args(self):
  178. return self.cargs
  179. def get_link_args(self):
  180. return self.libs
  181. def check_pkgconfig(self):
  182. try:
  183. p = subprocess.Popen(['pkg-config', '--version'], stdout=subprocess.PIPE,
  184. stderr=subprocess.PIPE)
  185. out = p.communicate()[0]
  186. if p.returncode == 0:
  187. mlog.log('Found pkg-config:', mlog.bold(shutil.which('pkg-config')),
  188. '(%s)' % out.decode().strip())
  189. PkgConfigDependency.pkgconfig_found = True
  190. return
  191. except Exception:
  192. pass
  193. PkgConfigDependency.pkgconfig_found = False
  194. mlog.log('Found Pkg-config:', mlog.red('NO'))
  195. def found(self):
  196. return self.is_found
  197. def extract_field(self, la_file, fieldname):
  198. with open(la_file) as f:
  199. for line in f:
  200. arr = line.strip().split('=')
  201. if arr[0] == fieldname:
  202. return arr[1][1:-1]
  203. return None
  204. def extract_dlname_field(self, la_file):
  205. return self.extract_field(la_file, 'dlname')
  206. def extract_libdir_field(self, la_file):
  207. return self.extract_field(la_file, 'libdir')
  208. def extract_libtool_shlib(self, la_file):
  209. '''
  210. Returns the path to the shared library
  211. corresponding to this .la file
  212. '''
  213. dlname = self.extract_dlname_field(la_file)
  214. if dlname is None:
  215. return None
  216. # Darwin uses absolute paths where possible; since the libtool files never
  217. # contain absolute paths, use the libdir field
  218. if mesonlib.is_osx():
  219. dlbasename = os.path.basename(dlname)
  220. libdir = self.extract_libdir_field(la_file)
  221. if libdir is None:
  222. return dlbasename
  223. return os.path.join(libdir, dlbasename)
  224. # From the comments in extract_libtool(), older libtools had
  225. # a path rather than the raw dlname
  226. return os.path.basename(dlname)
  227. class WxDependency(Dependency):
  228. wx_found = None
  229. def __init__(self, environment, kwargs):
  230. Dependency.__init__(self)
  231. self.is_found = False
  232. if WxDependency.wx_found is None:
  233. self.check_wxconfig()
  234. if not WxDependency.wx_found:
  235. mlog.log("Neither wx-config-3.0 nor wx-config found; can't detect dependency")
  236. return
  237. p = subprocess.Popen([self.wxc, '--version'],
  238. stdout=subprocess.PIPE,
  239. stderr=subprocess.PIPE)
  240. out = p.communicate()[0]
  241. if p.returncode != 0:
  242. mlog.log('Dependency wxwidgets found:', mlog.red('NO'))
  243. self.cargs = []
  244. self.libs = []
  245. else:
  246. self.modversion = out.decode().strip()
  247. version_req = kwargs.get('version', None)
  248. if version_req is not None:
  249. if not mesonlib.version_compare(self.modversion, version_req):
  250. mlog.log('Wxwidgets version %s does not fullfill requirement %s' %\
  251. (self.modversion, version_req))
  252. return
  253. mlog.log('Dependency wxwidgets found:', mlog.green('YES'))
  254. self.is_found = True
  255. self.requested_modules = self.get_requested(kwargs)
  256. # wx-config seems to have a cflags as well but since it requires C++,
  257. # this should be good, at least for now.
  258. p = subprocess.Popen([self.wxc, '--cxxflags'],
  259. stdout=subprocess.PIPE,
  260. stderr=subprocess.PIPE)
  261. out = p.communicate()[0]
  262. if p.returncode != 0:
  263. raise DependencyException('Could not generate cargs for wxwidgets.')
  264. self.cargs = out.decode().split()
  265. p = subprocess.Popen([self.wxc, '--libs'] + self.requested_modules,
  266. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  267. out = p.communicate()[0]
  268. if p.returncode != 0:
  269. raise DependencyException('Could not generate libs for wxwidgets.')
  270. self.libs = out.decode().split()
  271. def get_requested(self, kwargs):
  272. modules = 'modules'
  273. if not modules in kwargs:
  274. return []
  275. candidates = kwargs[modules]
  276. if isinstance(candidates, str):
  277. return [candidates]
  278. for c in candidates:
  279. if not isinstance(c, str):
  280. raise DependencyException('wxwidgets module argument is not a string.')
  281. return candidates
  282. def get_modversion(self):
  283. return self.modversion
  284. def get_compile_args(self):
  285. return self.cargs
  286. def get_link_args(self):
  287. return self.libs
  288. def check_wxconfig(self):
  289. for wxc in ['wx-config-3.0', 'wx-config']:
  290. try:
  291. p = subprocess.Popen([wxc, '--version'], stdout=subprocess.PIPE,
  292. stderr=subprocess.PIPE)
  293. out = p.communicate()[0]
  294. if p.returncode == 0:
  295. mlog.log('Found wx-config:', mlog.bold(shutil.which(wxc)),
  296. '(%s)' % out.decode().strip())
  297. self.wxc = wxc
  298. WxDependency.wx_found = True
  299. return
  300. except Exception:
  301. pass
  302. WxDependency.wxconfig_found = False
  303. mlog.log('Found wx-config:', mlog.red('NO'))
  304. def found(self):
  305. return self.is_found
  306. class ExternalProgram():
  307. def __init__(self, name, fullpath=None, silent=False, search_dir=None):
  308. self.name = name
  309. if fullpath is not None:
  310. if not isinstance(fullpath, list):
  311. self.fullpath = [fullpath]
  312. else:
  313. self.fullpath = fullpath
  314. else:
  315. self.fullpath = self._search(name, search_dir)
  316. if not silent:
  317. if self.found():
  318. mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'),
  319. '(%s)' % ' '.join(self.fullpath))
  320. else:
  321. mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO'))
  322. @staticmethod
  323. def _shebang_to_cmd(script):
  324. """
  325. Windows does not understand shebangs, so we check if the file has a
  326. shebang and manually parse it to figure out the interpreter to use
  327. """
  328. try:
  329. with open(script) as f:
  330. first_line = f.readline().strip()
  331. if first_line.startswith('#!'):
  332. commands = first_line[2:].split('#')[0].strip().split()
  333. if mesonlib.is_windows():
  334. # Windows does not have /usr/bin.
  335. commands[0] = commands[0].split('/')[-1]
  336. if commands[0] == 'env':
  337. commands = commands[1:]
  338. return commands + [script]
  339. except Exception:
  340. pass
  341. return False
  342. @staticmethod
  343. def _is_executable(path):
  344. suffix = os.path.splitext(path)[-1].lower()[1:]
  345. if mesonlib.is_windows():
  346. if suffix == 'exe' or suffix == 'com' or suffix == 'bat':
  347. return True
  348. elif os.access(path, os.X_OK):
  349. return True
  350. return False
  351. def _search_dir(self, name, search_dir):
  352. if search_dir is None:
  353. return False
  354. trial = os.path.join(search_dir, name)
  355. if not os.path.exists(trial):
  356. return False
  357. if self._is_executable(trial):
  358. return [trial]
  359. # Now getting desperate. Maybe it is a script file that is a) not chmodded
  360. # executable or b) we are on windows so they can't be directly executed.
  361. return self._shebang_to_cmd(trial)
  362. def _search(self, name, search_dir):
  363. commands = self._search_dir(name, search_dir)
  364. if commands:
  365. return commands
  366. # Do a standard search in PATH
  367. fullpath = shutil.which(name)
  368. if fullpath or not mesonlib.is_windows():
  369. # On UNIX-like platforms, the standard PATH search is enough
  370. return [fullpath]
  371. # On Windows, interpreted scripts must have an extension otherwise they
  372. # cannot be found by a standard PATH search. So we do a custom search
  373. # where we manually search for a script with a shebang in PATH.
  374. search_dirs = os.environ.get('PATH', '').split(';')
  375. for search_dir in search_dirs:
  376. commands = self._search_dir(name, search_dir)
  377. if commands:
  378. return commands
  379. return [None]
  380. def found(self):
  381. return self.fullpath[0] is not None
  382. def get_command(self):
  383. return self.fullpath
  384. def get_name(self):
  385. return self.name
  386. class ExternalLibrary(Dependency):
  387. def __init__(self, name, link_args=None, silent=False):
  388. super().__init__()
  389. self.name = name
  390. # Rename fullpath to link_args once standalone find_library() gets removed.
  391. if link_args is not None:
  392. if isinstance(link_args, list):
  393. self.link_args = link_args
  394. else:
  395. self.link_args = [link_args]
  396. else:
  397. self.link_args = link_args
  398. if not silent:
  399. if self.found():
  400. mlog.log('Library', mlog.bold(name), 'found:', mlog.green('YES'))
  401. else:
  402. mlog.log('Library', mlog.bold(name), 'found:', mlog.red('NO'))
  403. def found(self):
  404. return self.link_args is not None
  405. def get_link_args(self):
  406. if self.found():
  407. return self.link_args
  408. return []
  409. class BoostDependency(Dependency):
  410. # Some boost libraries have different names for
  411. # their sources and libraries. This dict maps
  412. # between the two.
  413. name2lib = {'test' : 'unit_test_framework'}
  414. def __init__(self, environment, kwargs):
  415. Dependency.__init__(self)
  416. self.name = 'boost'
  417. self.environment = environment
  418. self.libdir = ''
  419. if 'native' in kwargs and environment.is_cross_build():
  420. want_cross = not kwargs['native']
  421. else:
  422. want_cross = environment.is_cross_build()
  423. try:
  424. self.boost_root = os.environ['BOOST_ROOT']
  425. if not os.path.isabs(self.boost_root):
  426. raise DependencyException('BOOST_ROOT must be an absolute path.')
  427. except KeyError:
  428. self.boost_root = None
  429. if self.boost_root is None:
  430. if want_cross:
  431. raise DependencyException('BOOST_ROOT is needed while cross-compiling')
  432. if mesonlib.is_windows():
  433. self.boost_root = self.detect_win_root()
  434. self.incdir = self.boost_root
  435. else:
  436. self.incdir = '/usr/include'
  437. else:
  438. self.incdir = os.path.join(self.boost_root, 'include')
  439. self.boost_inc_subdir = os.path.join(self.incdir, 'boost')
  440. mlog.debug('Boost library root dir is', self.boost_root)
  441. self.src_modules = {}
  442. self.lib_modules = {}
  443. self.lib_modules_mt = {}
  444. self.detect_version()
  445. self.requested_modules = self.get_requested(kwargs)
  446. module_str = ', '.join(self.requested_modules)
  447. if self.version is not None:
  448. self.detect_src_modules()
  449. self.detect_lib_modules()
  450. self.validate_requested()
  451. if self.boost_root is not None:
  452. info = self.version + ', ' + self.boost_root
  453. else:
  454. info = self.version
  455. mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'),
  456. '(' + info + ')')
  457. else:
  458. mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
  459. def detect_win_root(self):
  460. globtext = 'c:\\local\\boost_*'
  461. files = glob.glob(globtext)
  462. if len(files) > 0:
  463. return files[0]
  464. return 'C:\\'
  465. def get_compile_args(self):
  466. args = []
  467. if self.boost_root is not None:
  468. if mesonlib.is_windows():
  469. args.append('-I' + self.boost_root)
  470. else:
  471. args.append('-I' + os.path.join(self.boost_root, 'include'))
  472. else:
  473. args.append('-I' + self.incdir)
  474. return args
  475. def get_requested(self, kwargs):
  476. candidates = kwargs.get('modules', [])
  477. if isinstance(candidates, str):
  478. return [candidates]
  479. for c in candidates:
  480. if not isinstance(c, str):
  481. raise DependencyException('Boost module argument is not a string.')
  482. return candidates
  483. def validate_requested(self):
  484. for m in self.requested_modules:
  485. if m not in self.src_modules:
  486. raise DependencyException('Requested Boost module "%s" not found.' % m)
  487. def found(self):
  488. return self.version is not None
  489. def get_version(self):
  490. return self.version
  491. def detect_version(self):
  492. try:
  493. ifile = open(os.path.join(self.boost_inc_subdir, 'version.hpp'))
  494. except FileNotFoundError:
  495. self.version = None
  496. return
  497. with ifile:
  498. for line in ifile:
  499. if line.startswith("#define") and 'BOOST_LIB_VERSION' in line:
  500. ver = line.split()[-1]
  501. ver = ver[1:-1]
  502. self.version = ver.replace('_', '.')
  503. return
  504. self.version = None
  505. def detect_src_modules(self):
  506. for entry in os.listdir(self.boost_inc_subdir):
  507. entry = os.path.join(self.boost_inc_subdir, entry)
  508. if stat.S_ISDIR(os.stat(entry).st_mode):
  509. self.src_modules[os.path.split(entry)[-1]] = True
  510. def detect_lib_modules(self):
  511. if mesonlib.is_windows():
  512. return self.detect_lib_modules_win()
  513. return self.detect_lib_modules_nix()
  514. def detect_lib_modules_win(self):
  515. arch = detect_cpu_family(self.environment.coredata.compilers)
  516. # Guess the libdir
  517. if arch == 'x86':
  518. gl = 'lib32*'
  519. elif arch == 'x86_64':
  520. gl = 'lib64*'
  521. else:
  522. # Does anyone do Boost cross-compiling to other archs on Windows?
  523. gl = None
  524. # See if the libdir is valid
  525. if gl:
  526. libdir = glob.glob(os.path.join(self.boost_root, gl))
  527. else:
  528. libdir = []
  529. # Can't find libdir, bail
  530. if len(libdir) == 0:
  531. return
  532. libdir = libdir[0]
  533. self.libdir = libdir
  534. globber = 'boost_*-gd-*.lib' # FIXME
  535. for entry in glob.glob(os.path.join(libdir, globber)):
  536. (_, fname) = os.path.split(entry)
  537. base = fname.split('_', 1)[1]
  538. modname = base.split('-', 1)[0]
  539. self.lib_modules_mt[modname] = fname
  540. def detect_lib_modules_nix(self):
  541. libsuffix = None
  542. if mesonlib.is_osx():
  543. libsuffix = 'dylib'
  544. else:
  545. libsuffix = 'so'
  546. globber = 'libboost_*.{}'.format(libsuffix)
  547. if self.boost_root is None:
  548. libdirs = mesonlib.get_library_dirs()
  549. else:
  550. libdirs = [os.path.join(self.boost_root, 'lib')]
  551. for libdir in libdirs:
  552. for entry in glob.glob(os.path.join(libdir, globber)):
  553. lib = os.path.basename(entry)
  554. name = lib.split('.')[0].split('_', 1)[-1]
  555. # I'm not 100% sure what to do here. Some distros
  556. # have modules such as thread only as -mt versions.
  557. if entry.endswith('-mt.so'):
  558. self.lib_modules_mt[name] = True
  559. else:
  560. self.lib_modules[name] = True
  561. def get_win_link_args(self):
  562. args = []
  563. if self.boost_root:
  564. args.append('-L' + self.libdir)
  565. for module in self.requested_modules:
  566. module = BoostDependency.name2lib.get(module, module)
  567. if module in self.lib_modules_mt:
  568. args.append(self.lib_modules_mt[module])
  569. return args
  570. def get_link_args(self):
  571. if mesonlib.is_windows():
  572. return self.get_win_link_args()
  573. args = []
  574. if self.boost_root:
  575. args.append('-L' + os.path.join(self.boost_root, 'lib'))
  576. for module in self.requested_modules:
  577. module = BoostDependency.name2lib.get(module, module)
  578. if module in self.lib_modules or module in self.lib_modules_mt:
  579. linkcmd = '-lboost_' + module
  580. args.append(linkcmd)
  581. # FIXME a hack, but Boost's testing framework has a lot of
  582. # different options and it's hard to determine what to do
  583. # without feedback from actual users. Update this
  584. # as we get more bug reports.
  585. if module == 'unit_testing_framework':
  586. args.append('-lboost_test_exec_monitor')
  587. elif module + '-mt' in self.lib_modules_mt:
  588. linkcmd = '-lboost_' + module + '-mt'
  589. args.append(linkcmd)
  590. if module == 'unit_testing_framework':
  591. args.append('-lboost_test_exec_monitor-mt')
  592. return args
  593. def get_sources(self):
  594. return []
  595. def need_threads(self):
  596. return 'thread' in self.requested_modules
  597. class GTestDependency(Dependency):
  598. def __init__(self, environment, kwargs):
  599. Dependency.__init__(self)
  600. self.main = kwargs.get('main', False)
  601. self.name = 'gtest'
  602. self.libname = 'libgtest.so'
  603. self.libmain_name = 'libgtest_main.so'
  604. self.include_dir = '/usr/include'
  605. self.src_include_dir = '/usr/src/gtest'
  606. self.src_dir = '/usr/src/gtest/src'
  607. self.all_src = mesonlib.File.from_absolute_file(
  608. os.path.join(self.src_dir, 'gtest-all.cc'))
  609. self.main_src = mesonlib.File.from_absolute_file(
  610. os.path.join(self.src_dir, 'gtest_main.cc'))
  611. self.detect()
  612. def found(self):
  613. return self.is_found
  614. def detect(self):
  615. trial_dirs = mesonlib.get_library_dirs()
  616. glib_found = False
  617. gmain_found = False
  618. for d in trial_dirs:
  619. if os.path.isfile(os.path.join(d, self.libname)):
  620. glib_found = True
  621. if os.path.isfile(os.path.join(d, self.libmain_name)):
  622. gmain_found = True
  623. if glib_found and gmain_found:
  624. self.is_found = True
  625. self.compile_args = []
  626. self.link_args = ['-lgtest']
  627. if self.main:
  628. self.link_args.append('-lgtest_main')
  629. self.sources = []
  630. mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)')
  631. elif os.path.exists(self.src_dir):
  632. self.is_found = True
  633. self.compile_args = ['-I' + self.src_include_dir]
  634. self.link_args = []
  635. if self.main:
  636. self.sources = [self.all_src, self.main_src]
  637. else:
  638. self.sources = [self.all_src]
  639. mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)')
  640. else:
  641. mlog.log('Dependency GTest found:', mlog.red('NO'))
  642. self.is_found = False
  643. return self.is_found
  644. def get_compile_args(self):
  645. arr = []
  646. if self.include_dir != '/usr/include':
  647. arr.append('-I' + self.include_dir)
  648. arr.append('-I' + self.src_include_dir)
  649. return arr
  650. def get_link_args(self):
  651. return self.link_args
  652. def get_version(self):
  653. return '1.something_maybe'
  654. def get_sources(self):
  655. return self.sources
  656. def need_threads(self):
  657. return True
  658. class GMockDependency(Dependency):
  659. def __init__(self, environment, kwargs):
  660. Dependency.__init__(self)
  661. # GMock may be a library or just source.
  662. # Work with both.
  663. self.name = 'gmock'
  664. self.libname = 'libgmock.so'
  665. trial_dirs = mesonlib.get_library_dirs()
  666. gmock_found = False
  667. for d in trial_dirs:
  668. if os.path.isfile(os.path.join(d, self.libname)):
  669. gmock_found = True
  670. if gmock_found:
  671. self.is_found = True
  672. self.compile_args = []
  673. self.link_args = ['-lgmock']
  674. self.sources = []
  675. mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)')
  676. return
  677. for d in ['/usr/src/gmock/src', '/usr/src/gmock']:
  678. if os.path.exists(d):
  679. self.is_found = True
  680. # Yes, we need both because there are multiple
  681. # versions of gmock that do different things.
  682. self.compile_args = ['-I/usr/src/gmock', '-I/usr/src/gmock/src']
  683. self.link_args = []
  684. all_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock-all.cc'))
  685. main_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock_main.cc'))
  686. if kwargs.get('main', False):
  687. self.sources = [all_src, main_src]
  688. else:
  689. self.sources = [all_src]
  690. mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)')
  691. return
  692. mlog.log('Dependency GMock found:', mlog.red('NO'))
  693. self.is_found = False
  694. def get_version(self):
  695. return '1.something_maybe'
  696. def get_compile_args(self):
  697. return self.compile_args
  698. def get_sources(self):
  699. return self.sources
  700. def get_link_args(self):
  701. return self.link_args
  702. def found(self):
  703. return self.is_found
  704. class Qt5Dependency(Dependency):
  705. def __init__(self, environment, kwargs):
  706. Dependency.__init__(self)
  707. self.name = 'qt5'
  708. self.root = '/usr'
  709. mods = kwargs.get('modules', [])
  710. self.cargs = []
  711. self.largs = []
  712. self.is_found = False
  713. if isinstance(mods, str):
  714. mods = [mods]
  715. if len(mods) == 0:
  716. raise DependencyException('No Qt5 modules specified.')
  717. type_text = 'native'
  718. if environment.is_cross_build() and kwargs.get('native', False):
  719. type_text = 'cross'
  720. self.pkgconfig_detect(mods, environment, kwargs)
  721. elif not environment.is_cross_build() and shutil.which('pkg-config') is not None:
  722. self.pkgconfig_detect(mods, environment, kwargs)
  723. elif shutil.which('qmake') is not None:
  724. self.qmake_detect(mods, kwargs)
  725. else:
  726. self.version = 'none'
  727. if not self.is_found:
  728. mlog.log('Qt5 %s dependency found: ' % type_text, mlog.red('NO'))
  729. else:
  730. mlog.log('Qt5 %s dependency found: ' % type_text, mlog.green('YES'))
  731. def pkgconfig_detect(self, mods, environment, kwargs):
  732. modules = []
  733. for module in mods:
  734. modules.append(PkgConfigDependency('Qt5' + module, environment, kwargs))
  735. for m in modules:
  736. self.cargs += m.get_compile_args()
  737. self.largs += m.get_link_args()
  738. self.is_found = True
  739. self.version = modules[0].modversion
  740. def qmake_detect(self, mods, kwargs):
  741. pc = subprocess.Popen(['qmake', '-v'], stdout=subprocess.PIPE,
  742. stderr=subprocess.PIPE)
  743. (stdo, _) = pc.communicate()
  744. if pc.returncode != 0:
  745. return
  746. stdo = stdo.decode()
  747. if not 'version 5' in stdo:
  748. mlog.log('QMake is not for Qt5.')
  749. return
  750. self.version = re.search('5(\.\d+)+', stdo).group(0)
  751. (stdo, _) = subprocess.Popen(['qmake', '-query'], stdout=subprocess.PIPE).communicate()
  752. qvars = {}
  753. for line in stdo.decode().split('\n'):
  754. line = line.strip()
  755. if line == '':
  756. continue
  757. (k, v) = tuple(line.split(':', 1))
  758. qvars[k] = v
  759. if mesonlib.is_osx():
  760. return self.framework_detect(qvars, mods, kwargs)
  761. incdir = qvars['QT_INSTALL_HEADERS']
  762. self.cargs.append('-I' + incdir)
  763. libdir = qvars['QT_INSTALL_LIBS']
  764. bindir = qvars['QT_INSTALL_BINS']
  765. #self.largs.append('-L' + libdir)
  766. for module in mods:
  767. mincdir = os.path.join(incdir, 'Qt' + module)
  768. self.cargs.append('-I' + mincdir)
  769. libfile = os.path.join(libdir, 'Qt5' + module + '.lib')
  770. if not os.path.isfile(libfile):
  771. # MinGW links directly to .dll, not to .lib.
  772. libfile = os.path.join(bindir, 'Qt5' + module + '.dll')
  773. self.largs.append(libfile)
  774. self.is_found = True
  775. def framework_detect(self, qvars, modules, kwargs):
  776. libdir = qvars['QT_INSTALL_LIBS']
  777. for m in modules:
  778. fname = 'Qt' + m
  779. fwdep = ExtraFrameworkDependency(fname, kwargs.get('required', True), libdir)
  780. self.cargs.append('-F' + libdir)
  781. if fwdep.found():
  782. self.is_found = True
  783. self.cargs += fwdep.get_compile_args()
  784. self.largs += fwdep.get_link_args()
  785. def get_version(self):
  786. return self.version
  787. def get_compile_args(self):
  788. return self.cargs
  789. def get_sources(self):
  790. return []
  791. def get_link_args(self):
  792. return self.largs
  793. def found(self):
  794. return self.is_found
  795. def get_exe_args(self):
  796. # Originally this was -fPIE but nowadays the default
  797. # for upstream and distros seems to be -reduce-relocations
  798. # which requires -fPIC. This may cause a performance
  799. # penalty when using self-built Qt or on platforms
  800. # where -fPIC is not required. If this is an issue
  801. # for you, patches are welcome.
  802. # Fix this to be more portable, especially to MSVC.
  803. return ['-fPIC']
  804. class Qt4Dependency(Dependency):
  805. def __init__(self, environment, kwargs):
  806. Dependency.__init__(self)
  807. self.name = 'qt4'
  808. self.root = '/usr'
  809. self.modules = []
  810. mods = kwargs.get('modules', [])
  811. if isinstance(mods, str):
  812. mods = [mods]
  813. for module in mods:
  814. self.modules.append(PkgConfigDependency('Qt' + module, environment, kwargs))
  815. if len(self.modules) == 0:
  816. raise DependencyException('No Qt4 modules specified.')
  817. def get_version(self):
  818. return self.modules[0].get_version()
  819. def get_compile_args(self):
  820. args = []
  821. for m in self.modules:
  822. args += m.get_compile_args()
  823. return args
  824. def get_sources(self):
  825. return []
  826. def get_link_args(self):
  827. args = []
  828. for module in self.modules:
  829. args += module.get_link_args()
  830. return args
  831. def found(self):
  832. for i in self.modules:
  833. if not i.found():
  834. return False
  835. return True
  836. class GnuStepDependency(Dependency):
  837. def __init__(self, environment, kwargs):
  838. Dependency.__init__(self)
  839. self.modules = kwargs.get('modules', [])
  840. self.detect()
  841. def detect(self):
  842. confprog = 'gnustep-config'
  843. try:
  844. gp = subprocess.Popen([confprog, '--help'],
  845. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  846. gp.communicate()
  847. except FileNotFoundError:
  848. self.args = None
  849. mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)')
  850. return
  851. if gp.returncode != 0:
  852. self.args = None
  853. mlog.log('Dependency GnuStep found:', mlog.red('NO'))
  854. return
  855. if 'gui' in self.modules:
  856. arg = '--gui-libs'
  857. else:
  858. arg = '--base-libs'
  859. fp = subprocess.Popen([confprog, '--objc-flags'],
  860. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  861. (flagtxt, flagerr) = fp.communicate()
  862. flagtxt = flagtxt.decode()
  863. flagerr = flagerr.decode()
  864. if fp.returncode != 0:
  865. raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr))
  866. args = flagtxt.split()
  867. self.args = self.filter_arsg(args)
  868. fp = subprocess.Popen([confprog, arg],
  869. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  870. (libtxt, liberr) = fp.communicate()
  871. libtxt = libtxt.decode()
  872. liberr = liberr.decode()
  873. if fp.returncode != 0:
  874. raise DependencyException('Error getting objc-lib args: %s %s' % (libtxt, liberr))
  875. self.libs = self.weird_filter(libtxt.split())
  876. mlog.log('Dependency GnuStep found:', mlog.green('YES'))
  877. def weird_filter(self, elems):
  878. """When building packages, the output of the enclosing Make
  879. is sometimes mixed among the subprocess output. I have no idea
  880. why. As a hack filter out everything that is not a flag."""
  881. return [e for e in elems if e.startswith('-')]
  882. def filter_arsg(self, args):
  883. """gnustep-config returns a bunch of garbage args such
  884. as -O2 and so on. Drop everything that is not needed."""
  885. result = []
  886. for f in args:
  887. if f.startswith('-D') or f.startswith('-f') or \
  888. f.startswith('-I') or f == '-pthread' or\
  889. (f.startswith('-W') and not f == '-Wall'):
  890. result.append(f)
  891. return result
  892. def found(self):
  893. return self.args is not None
  894. def get_compile_args(self):
  895. if self.args is None:
  896. return []
  897. return self.args
  898. def get_link_args(self):
  899. return self.libs
  900. class AppleFrameworks(Dependency):
  901. def __init__(self, environment, kwargs):
  902. Dependency.__init__(self)
  903. modules = kwargs.get('modules', [])
  904. if isinstance(modules, str):
  905. modules = [modules]
  906. if len(modules) == 0:
  907. raise DependencyException("AppleFrameworks dependency requires at least one module.")
  908. self.frameworks = modules
  909. def get_link_args(self):
  910. args = []
  911. for f in self.frameworks:
  912. args.append('-framework')
  913. args.append(f)
  914. return args
  915. def found(self):
  916. return mesonlib.is_osx()
  917. class GLDependency(Dependency):
  918. def __init__(self, environment, kwargs):
  919. Dependency.__init__(self)
  920. self.is_found = False
  921. self.cargs = []
  922. self.linkargs = []
  923. try:
  924. pcdep = PkgConfigDependency('gl', environment, kwargs)
  925. if pcdep.found():
  926. self.is_found = True
  927. self.cargs = pcdep.get_compile_args()
  928. self.linkargs = pcdep.get_link_args()
  929. return
  930. except Exception:
  931. pass
  932. if mesonlib.is_osx():
  933. self.is_found = True
  934. self.linkargs = ['-framework', 'OpenGL']
  935. return
  936. if mesonlib.is_windows():
  937. self.is_found = True
  938. self.linkargs = ['-lopengl32']
  939. return
  940. def get_link_args(self):
  941. return self.linkargs
  942. # There are three different ways of depending on SDL2:
  943. # sdl2-config, pkg-config and OSX framework
  944. class SDL2Dependency(Dependency):
  945. def __init__(self, environment, kwargs):
  946. Dependency.__init__(self)
  947. self.is_found = False
  948. self.cargs = []
  949. self.linkargs = []
  950. try:
  951. pcdep = PkgConfigDependency('sdl2', environment, kwargs)
  952. if pcdep.found():
  953. self.is_found = True
  954. self.cargs = pcdep.get_compile_args()
  955. self.linkargs = pcdep.get_link_args()
  956. self.version = pcdep.get_version()
  957. return
  958. except Exception as e:
  959. mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e))
  960. pass
  961. sdlconf = shutil.which('sdl2-config')
  962. if sdlconf:
  963. pc = subprocess.Popen(['sdl2-config', '--cflags'],
  964. stdout=subprocess.PIPE,
  965. stderr=subprocess.DEVNULL)
  966. (stdo, _) = pc.communicate()
  967. self.cargs = stdo.decode().strip().split()
  968. pc = subprocess.Popen(['sdl2-config', '--libs'],
  969. stdout=subprocess.PIPE,
  970. stderr=subprocess.DEVNULL)
  971. (stdo, _) = pc.communicate()
  972. self.linkargs = stdo.decode().strip().split()
  973. self.is_found = True
  974. mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'), '(%s)' % sdlconf)
  975. self.version = '2' # FIXME
  976. return
  977. mlog.debug('Could not find sdl2-config binary, trying next.')
  978. if mesonlib.is_osx():
  979. fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True))
  980. if fwdep.found():
  981. self.is_found = True
  982. self.cargs = fwdep.get_compile_args()
  983. self.linkargs = fwdep.get_link_args()
  984. self.version = '2' # FIXME
  985. return
  986. mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO'))
  987. def get_compile_args(self):
  988. return self.cargs
  989. def get_link_args(self):
  990. return self.linkargs
  991. def found(self):
  992. return self.is_found
  993. def get_version(self):
  994. return self.version
  995. class ExtraFrameworkDependency(Dependency):
  996. def __init__(self, name, required, path=None):
  997. Dependency.__init__(self)
  998. self.name = None
  999. self.detect(name, path)
  1000. if self.found():
  1001. mlog.log('Dependency', mlog.bold(name), 'found:', mlog.green('YES'),
  1002. os.path.join(self.path, self.name))
  1003. else:
  1004. mlog.log('Dependency', name, 'found:', mlog.red('NO'))
  1005. def detect(self, name, path):
  1006. lname = name.lower()
  1007. if path is None:
  1008. paths = ['/Library/Frameworks']
  1009. else:
  1010. paths = [path]
  1011. for p in paths:
  1012. for d in os.listdir(p):
  1013. fullpath = os.path.join(p, d)
  1014. if lname != d.split('.')[0].lower():
  1015. continue
  1016. if not stat.S_ISDIR(os.stat(fullpath).st_mode):
  1017. continue
  1018. self.path = p
  1019. self.name = d
  1020. return
  1021. def get_compile_args(self):
  1022. if self.found():
  1023. return ['-I' + os.path.join(self.path, self.name, 'Headers')]
  1024. return []
  1025. def get_link_args(self):
  1026. if self.found():
  1027. return ['-F' + self.path, '-framework', self.name.split('.')[0]]
  1028. return []
  1029. def found(self):
  1030. return self.name is not None
  1031. class ThreadDependency(Dependency):
  1032. def __init__(self, environment, kwargs):
  1033. super().__init__()
  1034. self.name = 'threads'
  1035. self.is_found = True
  1036. mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'))
  1037. def need_threads(self):
  1038. return True
  1039. class Python3Dependency(Dependency):
  1040. def __init__(self, environment, kwargs):
  1041. super().__init__()
  1042. self.name = 'python3'
  1043. self.is_found = False
  1044. self.version = "3.something_maybe"
  1045. try:
  1046. pkgdep = PkgConfigDependency('python3', environment, kwargs)
  1047. if pkgdep.found():
  1048. self.cargs = pkgdep.cargs
  1049. self.libs = pkgdep.libs
  1050. self.version = pkgdep.get_version()
  1051. self.is_found = True
  1052. return
  1053. except Exception:
  1054. pass
  1055. if not self.is_found:
  1056. if mesonlib.is_windows():
  1057. inc = sysconfig.get_path('include')
  1058. platinc = sysconfig.get_path('platinclude')
  1059. self.cargs = ['-I' + inc]
  1060. if inc != platinc:
  1061. self.cargs.append('-I' + platinc)
  1062. # Nothing exposes this directly that I coulf find
  1063. basedir = sysconfig.get_config_var('base')
  1064. vernum = sysconfig.get_config_var('py_version_nodot')
  1065. self.libs = ['-L{}/libs'.format(basedir),
  1066. '-lpython{}'.format(vernum)]
  1067. self.is_found = True
  1068. self.version = sysconfig.get_config_var('py_version_short')
  1069. elif mesonlib.is_osx():
  1070. # In OSX the Python 3 framework does not have a version
  1071. # number in its name.
  1072. fw = ExtraFrameworkDependency('python', False)
  1073. if fw.found():
  1074. self.cargs = fw.get_compile_args()
  1075. self.libs = fw.get_link_args()
  1076. self.is_found = True
  1077. if self.is_found:
  1078. mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'))
  1079. else:
  1080. mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO'))
  1081. def get_compile_args(self):
  1082. return self.cargs
  1083. def get_link_args(self):
  1084. return self.libs
  1085. def get_version(self):
  1086. return self.version
  1087. def get_dep_identifier(name, kwargs):
  1088. elements = [name]
  1089. modlist = kwargs.get('modules', [])
  1090. if isinstance(modlist, str):
  1091. modlist = [modlist]
  1092. for module in modlist:
  1093. elements.append(module)
  1094. # We use a tuple because we need a non-mutable structure to use as the key
  1095. # of a dictionary and a string has potential for name collisions
  1096. identifier = tuple(elements)
  1097. identifier += ('main', kwargs.get('main', False))
  1098. identifier += ('static', kwargs.get('static', False))
  1099. if 'fallback' in kwargs:
  1100. f = kwargs.get('fallback')
  1101. identifier += ('fallback', f[0], f[1])
  1102. return identifier
  1103. def find_external_dependency(name, environment, kwargs):
  1104. required = kwargs.get('required', True)
  1105. if not isinstance(required, bool):
  1106. raise DependencyException('Keyword "required" must be a boolean.')
  1107. lname = name.lower()
  1108. if lname in packages:
  1109. dep = packages[lname](environment, kwargs)
  1110. if required and not dep.found():
  1111. raise DependencyException('Dependency "%s" not found' % name)
  1112. return dep
  1113. pkg_exc = None
  1114. pkgdep = None
  1115. try:
  1116. pkgdep = PkgConfigDependency(name, environment, kwargs)
  1117. if pkgdep.found():
  1118. return pkgdep
  1119. except Exception as e:
  1120. pkg_exc = e
  1121. if mesonlib.is_osx():
  1122. fwdep = ExtraFrameworkDependency(name, required)
  1123. if required and not fwdep.found():
  1124. raise DependencyException('Dependency "%s" not found' % name)
  1125. return fwdep
  1126. if pkg_exc is not None:
  1127. raise pkg_exc
  1128. mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
  1129. return pkgdep
  1130. # This has to be at the end so the classes it references
  1131. # are defined.
  1132. packages = {'boost': BoostDependency,
  1133. 'gtest': GTestDependency,
  1134. 'gmock': GMockDependency,
  1135. 'qt5': Qt5Dependency,
  1136. 'qt4': Qt4Dependency,
  1137. 'gnustep': GnuStepDependency,
  1138. 'appleframeworks': AppleFrameworks,
  1139. 'wxwidgets' : WxDependency,
  1140. 'sdl2' : SDL2Dependency,
  1141. 'gl' : GLDependency,
  1142. 'threads' : ThreadDependency,
  1143. 'python3' : Python3Dependency,
  1144. }