run_unittests.py 92 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984
  1. #!/usr/bin/env python3
  2. # Copyright 2016-2017 The Meson development team
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS,
  9. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. # See the License for the specific language governing permissions and
  11. # limitations under the License.
  12. import stat
  13. import shlex
  14. import subprocess
  15. import re, json
  16. import tempfile
  17. import os
  18. import shutil
  19. import sys
  20. import unittest
  21. from configparser import ConfigParser
  22. from glob import glob
  23. from pathlib import PurePath
  24. import mesonbuild.mlog
  25. import mesonbuild.compilers
  26. import mesonbuild.environment
  27. import mesonbuild.mesonlib
  28. from mesonbuild.mesonlib import is_windows, is_osx, is_cygwin, windows_proof_rmtree
  29. from mesonbuild.environment import Environment
  30. from mesonbuild.dependencies import DependencyException
  31. from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
  32. from run_tests import exe_suffix, get_fake_options, FakeEnvironment
  33. from run_tests import get_builddir_target_args, get_backend_commands, Backend
  34. from run_tests import ensure_backend_detects_changes, run_configure_inprocess
  35. def get_dynamic_section_entry(fname, entry):
  36. try:
  37. raw_out = subprocess.check_output(['readelf', '-d', fname],
  38. universal_newlines=True)
  39. except FileNotFoundError:
  40. # FIXME: Try using depfixer.py:Elf() as a fallback
  41. raise unittest.SkipTest('readelf not found')
  42. pattern = re.compile(entry + r': \[(.*?)\]')
  43. for line in raw_out.split('\n'):
  44. m = pattern.search(line)
  45. if m is not None:
  46. return m.group(1)
  47. raise RuntimeError('Could not determine {}:\n\n'.format(entry) + raw_out)
  48. def get_soname(fname):
  49. return get_dynamic_section_entry(fname, 'soname')
  50. def get_rpath(fname):
  51. return get_dynamic_section_entry(fname, r'(?:rpath|runpath)')
  52. class InternalTests(unittest.TestCase):
  53. def test_version_number(self):
  54. searchfunc = mesonbuild.environment.search_version
  55. self.assertEqual(searchfunc('foobar 1.2.3'), '1.2.3')
  56. self.assertEqual(searchfunc('1.2.3'), '1.2.3')
  57. self.assertEqual(searchfunc('foobar 2016.10.28 1.2.3'), '1.2.3')
  58. self.assertEqual(searchfunc('2016.10.28 1.2.3'), '1.2.3')
  59. self.assertEqual(searchfunc('foobar 2016.10.128'), 'unknown version')
  60. self.assertEqual(searchfunc('2016.10.128'), 'unknown version')
  61. def test_mode_symbolic_to_bits(self):
  62. modefunc = mesonbuild.mesonlib.FileMode.perms_s_to_bits
  63. self.assertEqual(modefunc('---------'), 0)
  64. self.assertEqual(modefunc('r--------'), stat.S_IRUSR)
  65. self.assertEqual(modefunc('---r-----'), stat.S_IRGRP)
  66. self.assertEqual(modefunc('------r--'), stat.S_IROTH)
  67. self.assertEqual(modefunc('-w-------'), stat.S_IWUSR)
  68. self.assertEqual(modefunc('----w----'), stat.S_IWGRP)
  69. self.assertEqual(modefunc('-------w-'), stat.S_IWOTH)
  70. self.assertEqual(modefunc('--x------'), stat.S_IXUSR)
  71. self.assertEqual(modefunc('-----x---'), stat.S_IXGRP)
  72. self.assertEqual(modefunc('--------x'), stat.S_IXOTH)
  73. self.assertEqual(modefunc('--S------'), stat.S_ISUID)
  74. self.assertEqual(modefunc('-----S---'), stat.S_ISGID)
  75. self.assertEqual(modefunc('--------T'), stat.S_ISVTX)
  76. self.assertEqual(modefunc('--s------'), stat.S_ISUID | stat.S_IXUSR)
  77. self.assertEqual(modefunc('-----s---'), stat.S_ISGID | stat.S_IXGRP)
  78. self.assertEqual(modefunc('--------t'), stat.S_ISVTX | stat.S_IXOTH)
  79. self.assertEqual(modefunc('rwx------'), stat.S_IRWXU)
  80. self.assertEqual(modefunc('---rwx---'), stat.S_IRWXG)
  81. self.assertEqual(modefunc('------rwx'), stat.S_IRWXO)
  82. # We could keep listing combinations exhaustively but that seems
  83. # tedious and pointless. Just test a few more.
  84. self.assertEqual(modefunc('rwxr-xr-x'),
  85. stat.S_IRWXU |
  86. stat.S_IRGRP | stat.S_IXGRP |
  87. stat.S_IROTH | stat.S_IXOTH)
  88. self.assertEqual(modefunc('rw-r--r--'),
  89. stat.S_IRUSR | stat.S_IWUSR |
  90. stat.S_IRGRP |
  91. stat.S_IROTH)
  92. self.assertEqual(modefunc('rwsr-x---'),
  93. stat.S_IRWXU | stat.S_ISUID |
  94. stat.S_IRGRP | stat.S_IXGRP)
  95. def test_compiler_args_class(self):
  96. cargsfunc = mesonbuild.compilers.CompilerArgs
  97. c = mesonbuild.compilers.CCompiler([], 'fake', False)
  98. # Test that bad initialization fails
  99. self.assertRaises(TypeError, cargsfunc, [])
  100. self.assertRaises(TypeError, cargsfunc, [], [])
  101. self.assertRaises(TypeError, cargsfunc, c, [], [])
  102. # Test that empty initialization works
  103. a = cargsfunc(c)
  104. self.assertEqual(a, [])
  105. # Test that list initialization works
  106. a = cargsfunc(['-I.', '-I..'], c)
  107. self.assertEqual(a, ['-I.', '-I..'])
  108. # Test that there is no de-dup on initialization
  109. self.assertEqual(cargsfunc(['-I.', '-I.'], c), ['-I.', '-I.'])
  110. ## Test that appending works
  111. a.append('-I..')
  112. self.assertEqual(a, ['-I..', '-I.'])
  113. a.append('-O3')
  114. self.assertEqual(a, ['-I..', '-I.', '-O3'])
  115. ## Test that in-place addition works
  116. a += ['-O2', '-O2']
  117. self.assertEqual(a, ['-I..', '-I.', '-O3', '-O2', '-O2'])
  118. # Test that removal works
  119. a.remove('-O2')
  120. self.assertEqual(a, ['-I..', '-I.', '-O3', '-O2'])
  121. # Test that de-dup happens on addition
  122. a += ['-Ifoo', '-Ifoo']
  123. self.assertEqual(a, ['-Ifoo', '-I..', '-I.', '-O3', '-O2'])
  124. # .extend() is just +=, so we don't test it
  125. ## Test that addition works
  126. # Test that adding a list with just one old arg works and yields the same array
  127. a = a + ['-Ifoo']
  128. self.assertEqual(a, ['-Ifoo', '-I..', '-I.', '-O3', '-O2'])
  129. # Test that adding a list with one arg new and one old works
  130. a = a + ['-Ifoo', '-Ibaz']
  131. self.assertEqual(a, ['-Ifoo', '-Ibaz', '-I..', '-I.', '-O3', '-O2'])
  132. # Test that adding args that must be prepended and appended works
  133. a = a + ['-Ibar', '-Wall']
  134. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-O3', '-O2', '-Wall'])
  135. ## Test that reflected addition works
  136. # Test that adding to a list with just one old arg works and yields the same array
  137. a = ['-Ifoo'] + a
  138. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-O3', '-O2', '-Wall'])
  139. # Test that adding to a list with just one new arg that is not pre-pended works
  140. a = ['-Werror'] + a
  141. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Werror', '-O3', '-O2', '-Wall'])
  142. # Test that adding to a list with two new args preserves the order
  143. a = ['-Ldir', '-Lbah'] + a
  144. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Ldir', '-Lbah', '-Werror', '-O3', '-O2', '-Wall'])
  145. # Test that adding to a list with old args does nothing
  146. a = ['-Ibar', '-Ibaz', '-Ifoo'] + a
  147. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Ldir', '-Lbah', '-Werror', '-O3', '-O2', '-Wall'])
  148. ## Test that adding libraries works
  149. l = cargsfunc(c, ['-Lfoodir', '-lfoo'])
  150. self.assertEqual(l, ['-Lfoodir', '-lfoo'])
  151. # Adding a library and a libpath appends both correctly
  152. l += ['-Lbardir', '-lbar']
  153. self.assertEqual(l, ['-Lbardir', '-Lfoodir', '-lfoo', '-lbar'])
  154. # Adding the same library again does nothing
  155. l += ['-lbar']
  156. self.assertEqual(l, ['-Lbardir', '-Lfoodir', '-lfoo', '-lbar'])
  157. ## Test that 'direct' append and extend works
  158. l = cargsfunc(c, ['-Lfoodir', '-lfoo'])
  159. self.assertEqual(l, ['-Lfoodir', '-lfoo'])
  160. # Direct-adding a library and a libpath appends both correctly
  161. l.extend_direct(['-Lbardir', '-lbar'])
  162. self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar'])
  163. # Direct-adding the same library again still adds it
  164. l.append_direct('-lbar')
  165. self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar'])
  166. def test_commonpath(self):
  167. from os.path import sep
  168. commonpath = mesonbuild.mesonlib.commonpath
  169. self.assertRaises(ValueError, commonpath, [])
  170. self.assertEqual(commonpath(['/usr', '/usr']), sep + 'usr')
  171. self.assertEqual(commonpath(['/usr', '/usr/']), sep + 'usr')
  172. self.assertEqual(commonpath(['/usr', '/usr/bin']), sep + 'usr')
  173. self.assertEqual(commonpath(['/usr/', '/usr/bin']), sep + 'usr')
  174. self.assertEqual(commonpath(['/usr/./', '/usr/bin']), sep + 'usr')
  175. self.assertEqual(commonpath(['/usr/bin', '/usr/bin']), sep + 'usr' + sep + 'bin')
  176. self.assertEqual(commonpath(['/usr//bin', '/usr/bin']), sep + 'usr' + sep + 'bin')
  177. self.assertEqual(commonpath(['/usr/./bin', '/usr/bin']), sep + 'usr' + sep + 'bin')
  178. self.assertEqual(commonpath(['/usr/local', '/usr/lib']), sep + 'usr')
  179. self.assertEqual(commonpath(['/usr', '/bin']), sep)
  180. self.assertEqual(commonpath(['/usr', 'bin']), '')
  181. self.assertEqual(commonpath(['blam', 'bin']), '')
  182. prefix = '/some/path/to/prefix'
  183. libdir = '/some/path/to/prefix/libdir'
  184. self.assertEqual(commonpath([prefix, libdir]), str(PurePath(prefix)))
  185. def test_string_templates_substitution(self):
  186. dictfunc = mesonbuild.mesonlib.get_filenames_templates_dict
  187. substfunc = mesonbuild.mesonlib.substitute_values
  188. ME = mesonbuild.mesonlib.MesonException
  189. # Identity
  190. self.assertEqual(dictfunc([], []), {})
  191. # One input, no outputs
  192. inputs = ['bar/foo.c.in']
  193. outputs = []
  194. ret = dictfunc(inputs, outputs)
  195. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
  196. '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c'}
  197. # Check dictionary
  198. self.assertEqual(ret, d)
  199. # Check substitutions
  200. cmd = ['some', 'ordinary', 'strings']
  201. self.assertEqual(substfunc(cmd, d), cmd)
  202. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  203. self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out'] + cmd[1:])
  204. cmd = ['@INPUT0@.out', '@PLAINNAME@.ok', 'strings']
  205. self.assertEqual(substfunc(cmd, d),
  206. [inputs[0] + '.out'] + [d['@PLAINNAME@'] + '.ok'] + cmd[2:])
  207. cmd = ['@INPUT@', '@BASENAME@.hah', 'strings']
  208. self.assertEqual(substfunc(cmd, d),
  209. inputs + [d['@BASENAME@'] + '.hah'] + cmd[2:])
  210. cmd = ['@OUTPUT@']
  211. self.assertRaises(ME, substfunc, cmd, d)
  212. # One input, one output
  213. inputs = ['bar/foo.c.in']
  214. outputs = ['out.c']
  215. ret = dictfunc(inputs, outputs)
  216. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
  217. '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
  218. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': '.'}
  219. # Check dictionary
  220. self.assertEqual(ret, d)
  221. # Check substitutions
  222. cmd = ['some', 'ordinary', 'strings']
  223. self.assertEqual(substfunc(cmd, d), cmd)
  224. cmd = ['@INPUT@.out', '@OUTPUT@', 'strings']
  225. self.assertEqual(substfunc(cmd, d),
  226. [inputs[0] + '.out'] + outputs + cmd[2:])
  227. cmd = ['@INPUT0@.out', '@PLAINNAME@.ok', '@OUTPUT0@']
  228. self.assertEqual(substfunc(cmd, d),
  229. [inputs[0] + '.out', d['@PLAINNAME@'] + '.ok'] + outputs)
  230. cmd = ['@INPUT@', '@BASENAME@.hah', 'strings']
  231. self.assertEqual(substfunc(cmd, d),
  232. inputs + [d['@BASENAME@'] + '.hah'] + cmd[2:])
  233. # One input, one output with a subdir
  234. outputs = ['dir/out.c']
  235. ret = dictfunc(inputs, outputs)
  236. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
  237. '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
  238. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
  239. # Check dictionary
  240. self.assertEqual(ret, d)
  241. # Two inputs, no outputs
  242. inputs = ['bar/foo.c.in', 'baz/foo.c.in']
  243. outputs = []
  244. ret = dictfunc(inputs, outputs)
  245. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1]}
  246. # Check dictionary
  247. self.assertEqual(ret, d)
  248. # Check substitutions
  249. cmd = ['some', 'ordinary', 'strings']
  250. self.assertEqual(substfunc(cmd, d), cmd)
  251. cmd = ['@INPUT@', 'ordinary', 'strings']
  252. self.assertEqual(substfunc(cmd, d), inputs + cmd[1:])
  253. cmd = ['@INPUT0@.out', 'ordinary', 'strings']
  254. self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out'] + cmd[1:])
  255. cmd = ['@INPUT0@.out', '@INPUT1@.ok', 'strings']
  256. self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out', inputs[1] + '.ok'] + cmd[2:])
  257. cmd = ['@INPUT0@', '@INPUT1@', 'strings']
  258. self.assertEqual(substfunc(cmd, d), inputs + cmd[2:])
  259. # Many inputs, can't use @INPUT@ like this
  260. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  261. self.assertRaises(ME, substfunc, cmd, d)
  262. # Not enough inputs
  263. cmd = ['@INPUT2@.out', 'ordinary', 'strings']
  264. self.assertRaises(ME, substfunc, cmd, d)
  265. # Too many inputs
  266. cmd = ['@PLAINNAME@']
  267. self.assertRaises(ME, substfunc, cmd, d)
  268. cmd = ['@BASENAME@']
  269. self.assertRaises(ME, substfunc, cmd, d)
  270. # No outputs
  271. cmd = ['@OUTPUT@']
  272. self.assertRaises(ME, substfunc, cmd, d)
  273. cmd = ['@OUTPUT0@']
  274. self.assertRaises(ME, substfunc, cmd, d)
  275. cmd = ['@OUTDIR@']
  276. self.assertRaises(ME, substfunc, cmd, d)
  277. # Two inputs, one output
  278. outputs = ['dir/out.c']
  279. ret = dictfunc(inputs, outputs)
  280. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
  281. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
  282. # Check dictionary
  283. self.assertEqual(ret, d)
  284. # Check substitutions
  285. cmd = ['some', 'ordinary', 'strings']
  286. self.assertEqual(substfunc(cmd, d), cmd)
  287. cmd = ['@OUTPUT@', 'ordinary', 'strings']
  288. self.assertEqual(substfunc(cmd, d), outputs + cmd[1:])
  289. cmd = ['@OUTPUT@.out', 'ordinary', 'strings']
  290. self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out'] + cmd[1:])
  291. cmd = ['@OUTPUT0@.out', '@INPUT1@.ok', 'strings']
  292. self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok'] + cmd[2:])
  293. # Many inputs, can't use @INPUT@ like this
  294. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  295. self.assertRaises(ME, substfunc, cmd, d)
  296. # Not enough inputs
  297. cmd = ['@INPUT2@.out', 'ordinary', 'strings']
  298. self.assertRaises(ME, substfunc, cmd, d)
  299. # Not enough outputs
  300. cmd = ['@OUTPUT2@.out', 'ordinary', 'strings']
  301. self.assertRaises(ME, substfunc, cmd, d)
  302. # Two inputs, two outputs
  303. outputs = ['dir/out.c', 'dir/out2.c']
  304. ret = dictfunc(inputs, outputs)
  305. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
  306. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTPUT1@': outputs[1],
  307. '@OUTDIR@': 'dir'}
  308. # Check dictionary
  309. self.assertEqual(ret, d)
  310. # Check substitutions
  311. cmd = ['some', 'ordinary', 'strings']
  312. self.assertEqual(substfunc(cmd, d), cmd)
  313. cmd = ['@OUTPUT@', 'ordinary', 'strings']
  314. self.assertEqual(substfunc(cmd, d), outputs + cmd[1:])
  315. cmd = ['@OUTPUT0@', '@OUTPUT1@', 'strings']
  316. self.assertEqual(substfunc(cmd, d), outputs + cmd[2:])
  317. cmd = ['@OUTPUT0@.out', '@INPUT1@.ok', '@OUTDIR@']
  318. self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok', 'dir'])
  319. # Many inputs, can't use @INPUT@ like this
  320. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  321. self.assertRaises(ME, substfunc, cmd, d)
  322. # Not enough inputs
  323. cmd = ['@INPUT2@.out', 'ordinary', 'strings']
  324. self.assertRaises(ME, substfunc, cmd, d)
  325. # Not enough outputs
  326. cmd = ['@OUTPUT2@.out', 'ordinary', 'strings']
  327. self.assertRaises(ME, substfunc, cmd, d)
  328. # Many outputs, can't use @OUTPUT@ like this
  329. cmd = ['@OUTPUT@.out', 'ordinary', 'strings']
  330. self.assertRaises(ME, substfunc, cmd, d)
  331. def test_needs_exe_wrapper_override(self):
  332. config = ConfigParser()
  333. config['binaries'] = {
  334. 'c': '\'/usr/bin/gcc\'',
  335. }
  336. config['host_machine'] = {
  337. 'system': '\'linux\'',
  338. 'cpu_family': '\'arm\'',
  339. 'cpu': '\'armv7\'',
  340. 'endian': '\'little\'',
  341. }
  342. # Can not be used as context manager because we need to
  343. # open it a second time and this is not possible on
  344. # Windows.
  345. configfile = tempfile.NamedTemporaryFile(mode='w+', delete=False)
  346. configfilename = configfile.name
  347. config.write(configfile)
  348. configfile.flush()
  349. configfile.close()
  350. detected_value = mesonbuild.environment.CrossBuildInfo(configfile.name).need_exe_wrapper()
  351. os.unlink(configfilename)
  352. desired_value = not detected_value
  353. config['properties'] = {
  354. 'needs_exe_wrapper': 'true' if desired_value else 'false'
  355. }
  356. configfile = tempfile.NamedTemporaryFile(mode='w+', delete=False)
  357. configfilename = configfile.name
  358. config.write(configfile)
  359. configfile.close()
  360. forced_value = mesonbuild.environment.CrossBuildInfo(configfile.name).need_exe_wrapper()
  361. os.unlink(configfilename)
  362. self.assertEqual(forced_value, desired_value)
  363. class BasePlatformTests(unittest.TestCase):
  364. def setUp(self):
  365. super().setUp()
  366. src_root = os.path.dirname(__file__)
  367. src_root = os.path.join(os.getcwd(), src_root)
  368. self.src_root = src_root
  369. # In case the directory is inside a symlinked directory, find the real
  370. # path otherwise we might not find the srcdir from inside the builddir.
  371. self.builddir = os.path.realpath(tempfile.mkdtemp())
  372. self.logdir = os.path.join(self.builddir, 'meson-logs')
  373. self.prefix = '/usr'
  374. self.libdir = os.path.join(self.prefix, 'lib')
  375. self.installdir = os.path.join(self.builddir, 'install')
  376. self.distdir = os.path.join(self.builddir, 'meson-dist')
  377. # Get the backend
  378. # FIXME: Extract this from argv?
  379. self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja'))
  380. self.meson_args = [os.path.join(src_root, 'meson.py'), '--backend=' + self.backend.name]
  381. self.meson_command = [sys.executable] + self.meson_args
  382. self.mconf_command = [sys.executable, os.path.join(src_root, 'mesonconf.py')]
  383. self.mintro_command = [sys.executable, os.path.join(src_root, 'mesonintrospect.py')]
  384. self.mtest_command = [sys.executable, os.path.join(src_root, 'mesontest.py'), '-C', self.builddir]
  385. # Backend-specific build commands
  386. self.build_command, self.clean_command, self.test_command, self.install_command, \
  387. self.uninstall_command = get_backend_commands(self.backend)
  388. # Test directories
  389. self.common_test_dir = os.path.join(src_root, 'test cases/common')
  390. self.vala_test_dir = os.path.join(src_root, 'test cases/vala')
  391. self.framework_test_dir = os.path.join(src_root, 'test cases/frameworks')
  392. self.unit_test_dir = os.path.join(src_root, 'test cases/unit')
  393. # Misc stuff
  394. self.orig_env = os.environ.copy()
  395. if self.backend is Backend.ninja:
  396. self.no_rebuild_stdout = 'ninja: no work to do.'
  397. else:
  398. # VS doesn't have a stable output when no changes are done
  399. # XCode backend is untested with unit tests, help welcome!
  400. self.no_rebuild_stdout = 'UNKNOWN BACKEND {!r}'.format(self.backend.name)
  401. def _print_meson_log(self):
  402. log = os.path.join(self.logdir, 'meson-log.txt')
  403. if not os.path.isfile(log):
  404. print("{!r} doesn't exist".format(log))
  405. return
  406. with open(log, 'r', encoding='utf-8') as f:
  407. print(f.read())
  408. def tearDown(self):
  409. windows_proof_rmtree(self.builddir)
  410. os.environ = self.orig_env
  411. super().tearDown()
  412. def _run(self, command, workdir=None):
  413. '''
  414. Run a command while printing the stdout and stderr to stdout,
  415. and also return a copy of it
  416. '''
  417. p = subprocess.Popen(command, stdout=subprocess.PIPE,
  418. stderr=subprocess.STDOUT, env=os.environ.copy(),
  419. universal_newlines=True, cwd=workdir)
  420. output = p.communicate()[0]
  421. print(output)
  422. if p.returncode != 0:
  423. if 'MESON_SKIP_TEST' in output:
  424. raise unittest.SkipTest('Project requested skipping.')
  425. raise subprocess.CalledProcessError(p.returncode, command)
  426. return output
  427. def init(self, srcdir, extra_args=None, default_args=True, inprocess=False):
  428. self.assertTrue(os.path.exists(srcdir))
  429. if extra_args is None:
  430. extra_args = []
  431. if not isinstance(extra_args, list):
  432. extra_args = [extra_args]
  433. args = [srcdir, self.builddir]
  434. if default_args:
  435. args += ['--prefix', self.prefix,
  436. '--libdir', self.libdir]
  437. self.privatedir = os.path.join(self.builddir, 'meson-private')
  438. if inprocess:
  439. try:
  440. out = run_configure_inprocess(self.meson_args + args + extra_args)[1]
  441. except:
  442. self._print_meson_log()
  443. raise
  444. finally:
  445. # Close log file to satisfy Windows file locking
  446. mesonbuild.mlog.shutdown()
  447. mesonbuild.mlog.log_dir = None
  448. mesonbuild.mlog.log_file = None
  449. else:
  450. try:
  451. out = self._run(self.meson_command + args + extra_args)
  452. except unittest.SkipTest:
  453. raise unittest.SkipTest('Project requested skipping: ' + srcdir)
  454. except:
  455. self._print_meson_log()
  456. raise
  457. return out
  458. def build(self, target=None, extra_args=None):
  459. if extra_args is None:
  460. extra_args = []
  461. # Add arguments for building the target (if specified),
  462. # and using the build dir (if required, with VS)
  463. args = get_builddir_target_args(self.backend, self.builddir, target)
  464. return self._run(self.build_command + args + extra_args, workdir=self.builddir)
  465. def clean(self):
  466. dir_args = get_builddir_target_args(self.backend, self.builddir, None)
  467. self._run(self.clean_command + dir_args, workdir=self.builddir)
  468. def run_tests(self):
  469. self._run(self.test_command, workdir=self.builddir)
  470. def install(self):
  471. if self.backend is not Backend.ninja:
  472. raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name))
  473. os.environ['DESTDIR'] = self.installdir
  474. self._run(self.install_command, workdir=self.builddir)
  475. def uninstall(self):
  476. self._run(self.uninstall_command, workdir=self.builddir)
  477. def run_target(self, target):
  478. '''
  479. Run a Ninja target while printing the stdout and stderr to stdout,
  480. and also return a copy of it
  481. '''
  482. return self.build(target=target)
  483. def setconf(self, arg, will_build=True):
  484. if will_build:
  485. ensure_backend_detects_changes(self.backend)
  486. self._run(self.mconf_command + [arg, self.builddir])
  487. def wipe(self):
  488. windows_proof_rmtree(self.builddir)
  489. def utime(self, f):
  490. ensure_backend_detects_changes(self.backend)
  491. os.utime(f)
  492. def get_compdb(self):
  493. if self.backend is not Backend.ninja:
  494. raise unittest.SkipTest('Compiler db not available with {} backend'.format(self.backend.name))
  495. with open(os.path.join(self.builddir, 'compile_commands.json')) as ifile:
  496. contents = json.load(ifile)
  497. # If Ninja is using .rsp files, generate them, read their contents, and
  498. # replace it as the command for all compile commands in the parsed json.
  499. if len(contents) > 0 and contents[0]['command'].endswith('.rsp'):
  500. # Pretend to build so that the rsp files are generated
  501. self.build(extra_args=['-d', 'keeprsp', '-n'])
  502. for each in contents:
  503. # Extract the actual command from the rsp file
  504. compiler, rsp = each['command'].split(' @')
  505. rsp = os.path.join(self.builddir, rsp)
  506. # Replace the command with its contents
  507. with open(rsp, 'r', encoding='utf-8') as f:
  508. each['command'] = compiler + ' ' + f.read()
  509. return contents
  510. def get_meson_log(self):
  511. with open(os.path.join(self.builddir, 'meson-logs', 'meson-log.txt')) as f:
  512. return f.readlines()
  513. def get_meson_log_compiler_checks(self):
  514. '''
  515. Fetch a list command-lines run by meson for compiler checks.
  516. Each command-line is returned as a list of arguments.
  517. '''
  518. log = self.get_meson_log()
  519. prefix = 'Command line:'
  520. cmds = [l[len(prefix):].split() for l in log if l.startswith(prefix)]
  521. return cmds
  522. def introspect(self, arg):
  523. out = subprocess.check_output(self.mintro_command + [arg, self.builddir],
  524. universal_newlines=True)
  525. return json.loads(out)
  526. def assertPathEqual(self, path1, path2):
  527. '''
  528. Handles a lot of platform-specific quirks related to paths such as
  529. separator, case-sensitivity, etc.
  530. '''
  531. self.assertEqual(PurePath(path1), PurePath(path2))
  532. def assertPathBasenameEqual(self, path, basename):
  533. msg = '{!r} does not end with {!r}'.format(path, basename)
  534. # We cannot use os.path.basename because it returns '' when the path
  535. # ends with '/' for some silly reason. This is not how the UNIX utility
  536. # `basename` works.
  537. path_basename = PurePath(path).parts[-1]
  538. self.assertEqual(PurePath(path_basename), PurePath(basename), msg)
  539. def assertBuildIsNoop(self):
  540. ret = self.build()
  541. if self.backend is Backend.ninja:
  542. self.assertEqual(ret.split('\n')[-2], self.no_rebuild_stdout)
  543. elif self.backend is Backend.vs:
  544. # Ensure that some target said that no rebuild was done
  545. self.assertIn('CustomBuild:\n All outputs are up-to-date.', ret)
  546. self.assertIn('ClCompile:\n All outputs are up-to-date.', ret)
  547. self.assertIn('Link:\n All outputs are up-to-date.', ret)
  548. # Ensure that no targets were built
  549. clre = re.compile('ClCompile:\n [^\n]*cl', flags=re.IGNORECASE)
  550. linkre = re.compile('Link:\n [^\n]*link', flags=re.IGNORECASE)
  551. self.assertNotRegex(ret, clre)
  552. self.assertNotRegex(ret, linkre)
  553. elif self.backend is Backend.xcode:
  554. raise unittest.SkipTest('Please help us fix this test on the xcode backend')
  555. else:
  556. raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name))
  557. def assertRebuiltTarget(self, target):
  558. ret = self.build()
  559. if self.backend is Backend.ninja:
  560. self.assertIn('Linking target {}'.format(target), ret)
  561. elif self.backend is Backend.vs:
  562. # Ensure that this target was rebuilt
  563. clre = re.compile('ClCompile:\n [^\n]*cl[^\n]*' + target, flags=re.IGNORECASE)
  564. linkre = re.compile('Link:\n [^\n]*link[^\n]*' + target, flags=re.IGNORECASE)
  565. self.assertRegex(ret, clre)
  566. self.assertRegex(ret, linkre)
  567. elif self.backend is Backend.xcode:
  568. raise unittest.SkipTest('Please help us fix this test on the xcode backend')
  569. else:
  570. raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name))
  571. class AllPlatformTests(BasePlatformTests):
  572. '''
  573. Tests that should run on all platforms
  574. '''
  575. def test_default_options_prefix(self):
  576. '''
  577. Tests that setting a prefix in default_options in project() works.
  578. Can't be an ordinary test because we pass --prefix to meson there.
  579. https://github.com/mesonbuild/meson/issues/1349
  580. '''
  581. testdir = os.path.join(self.common_test_dir, '94 default options')
  582. self.init(testdir, default_args=False)
  583. opts = self.introspect('--buildoptions')
  584. for opt in opts:
  585. if opt['name'] == 'prefix':
  586. prefix = opt['value']
  587. self.assertEqual(prefix, '/absoluteprefix')
  588. def test_absolute_prefix_libdir(self):
  589. '''
  590. Tests that setting absolute paths for --prefix and --libdir work. Can't
  591. be an ordinary test because these are set via the command-line.
  592. https://github.com/mesonbuild/meson/issues/1341
  593. https://github.com/mesonbuild/meson/issues/1345
  594. '''
  595. testdir = os.path.join(self.common_test_dir, '94 default options')
  596. prefix = '/someabs'
  597. libdir = 'libdir'
  598. extra_args = ['--prefix=' + prefix,
  599. # This can just be a relative path, but we want to test
  600. # that passing this as an absolute path also works
  601. '--libdir=' + prefix + '/' + libdir]
  602. self.init(testdir, extra_args, default_args=False)
  603. opts = self.introspect('--buildoptions')
  604. for opt in opts:
  605. if opt['name'] == 'prefix':
  606. self.assertEqual(prefix, opt['value'])
  607. elif opt['name'] == 'libdir':
  608. self.assertEqual(libdir, opt['value'])
  609. def test_libdir_must_be_inside_prefix(self):
  610. '''
  611. Tests that libdir is forced to be inside prefix no matter how it is set.
  612. Must be a unit test for obvious reasons.
  613. '''
  614. testdir = os.path.join(self.common_test_dir, '1 trivial')
  615. # libdir being inside prefix is ok
  616. args = ['--prefix', '/opt', '--libdir', '/opt/lib32']
  617. self.init(testdir, args)
  618. self.wipe()
  619. # libdir not being inside prefix is not ok
  620. args = ['--prefix', '/usr', '--libdir', '/opt/lib32']
  621. self.assertRaises(subprocess.CalledProcessError, self.init, testdir, args)
  622. self.wipe()
  623. # libdir must be inside prefix even when set via mesonconf
  624. self.init(testdir)
  625. self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt', False)
  626. def test_static_library_overwrite(self):
  627. '''
  628. Tests that static libraries are never appended to, always overwritten.
  629. Has to be a unit test because this involves building a project,
  630. reconfiguring, and building it again so that `ar` is run twice on the
  631. same static library.
  632. https://github.com/mesonbuild/meson/issues/1355
  633. '''
  634. testdir = os.path.join(self.common_test_dir, '3 static')
  635. env = Environment(testdir, self.builddir, self.meson_command,
  636. get_fake_options(self.prefix), [])
  637. cc = env.detect_c_compiler(False)
  638. static_linker = env.detect_static_linker(cc)
  639. if is_windows():
  640. raise unittest.SkipTest('https://github.com/mesonbuild/meson/issues/1526')
  641. if not isinstance(static_linker, mesonbuild.linkers.ArLinker):
  642. raise unittest.SkipTest('static linker is not `ar`')
  643. # Configure
  644. self.init(testdir)
  645. # Get name of static library
  646. targets = self.introspect('--targets')
  647. self.assertEqual(len(targets), 1)
  648. libname = targets[0]['filename']
  649. # Build and get contents of static library
  650. self.build()
  651. before = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split()
  652. # Filter out non-object-file contents
  653. before = [f for f in before if f.endswith(('.o', '.obj'))]
  654. # Static library should contain only one object
  655. self.assertEqual(len(before), 1, msg=before)
  656. # Change the source to be built into the static library
  657. self.setconf('-Dsource=libfile2.c')
  658. self.build()
  659. after = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split()
  660. # Filter out non-object-file contents
  661. after = [f for f in after if f.endswith(('.o', '.obj'))]
  662. # Static library should contain only one object
  663. self.assertEqual(len(after), 1, msg=after)
  664. # and the object must have changed
  665. self.assertNotEqual(before, after)
  666. def test_static_compile_order(self):
  667. '''
  668. Test that the order of files in a compiler command-line while compiling
  669. and linking statically is deterministic. This can't be an ordinary test
  670. case because we need to inspect the compiler database.
  671. https://github.com/mesonbuild/meson/pull/951
  672. '''
  673. testdir = os.path.join(self.common_test_dir, '5 linkstatic')
  674. self.init(testdir)
  675. compdb = self.get_compdb()
  676. # Rules will get written out in this order
  677. self.assertTrue(compdb[0]['file'].endswith("libfile.c"))
  678. self.assertTrue(compdb[1]['file'].endswith("libfile2.c"))
  679. self.assertTrue(compdb[2]['file'].endswith("libfile3.c"))
  680. self.assertTrue(compdb[3]['file'].endswith("libfile4.c"))
  681. # FIXME: We don't have access to the linker command
  682. def test_run_target_files_path(self):
  683. '''
  684. Test that run_targets are run from the correct directory
  685. https://github.com/mesonbuild/meson/issues/957
  686. '''
  687. testdir = os.path.join(self.common_test_dir, '58 run target')
  688. self.init(testdir)
  689. self.run_target('check_exists')
  690. def test_install_introspection(self):
  691. '''
  692. Tests that the Meson introspection API exposes install filenames correctly
  693. https://github.com/mesonbuild/meson/issues/829
  694. '''
  695. if self.backend is not Backend.ninja:
  696. raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name))
  697. testdir = os.path.join(self.common_test_dir, '8 install')
  698. self.init(testdir)
  699. intro = self.introspect('--targets')
  700. if intro[0]['type'] == 'executable':
  701. intro = intro[::-1]
  702. self.assertPathEqual(intro[0]['install_filename'], '/usr/lib/libstat.a')
  703. self.assertPathEqual(intro[1]['install_filename'], '/usr/bin/prog' + exe_suffix)
  704. def test_uninstall(self):
  705. exename = os.path.join(self.installdir, 'usr/bin/prog' + exe_suffix)
  706. testdir = os.path.join(self.common_test_dir, '8 install')
  707. self.init(testdir)
  708. self.assertFalse(os.path.exists(exename))
  709. self.install()
  710. self.assertTrue(os.path.exists(exename))
  711. self.uninstall()
  712. self.assertFalse(os.path.exists(exename))
  713. def test_testsetups(self):
  714. if not shutil.which('valgrind'):
  715. raise unittest.SkipTest('Valgrind not installed.')
  716. testdir = os.path.join(self.unit_test_dir, '2 testsetups')
  717. self.init(testdir)
  718. self.build()
  719. # Run tests without setup
  720. self.run_tests()
  721. with open(os.path.join(self.logdir, 'testlog.txt')) as f:
  722. basic_log = f.read()
  723. # Run buggy test with setup that has env that will make it fail
  724. self.assertRaises(subprocess.CalledProcessError,
  725. self._run, self.mtest_command + ['--setup=valgrind'])
  726. with open(os.path.join(self.logdir, 'testlog-valgrind.txt')) as f:
  727. vg_log = f.read()
  728. self.assertFalse('TEST_ENV is set' in basic_log)
  729. self.assertFalse('Memcheck' in basic_log)
  730. self.assertTrue('TEST_ENV is set' in vg_log)
  731. self.assertTrue('Memcheck' in vg_log)
  732. # Run buggy test with setup without env that will pass
  733. self._run(self.mtest_command + ['--setup=wrapper'])
  734. # Setup with no properties works
  735. self._run(self.mtest_command + ['--setup=empty'])
  736. # Setup with only env works
  737. self._run(self.mtest_command + ['--setup=onlyenv'])
  738. # Setup with only a timeout works
  739. self._run(self.mtest_command + ['--setup=timeout'])
  740. def assertFailedTestCount(self, failure_count, command):
  741. try:
  742. self._run(command)
  743. self.assertEqual(0, failure_count, 'Expected %d tests to fail.' % failure_count)
  744. except subprocess.CalledProcessError as e:
  745. self.assertEqual(e.returncode, failure_count)
  746. def test_suite_selection(self):
  747. testdir = os.path.join(self.unit_test_dir, '4 suite selection')
  748. self.init(testdir)
  749. self.build()
  750. self.assertFailedTestCount(3, self.mtest_command)
  751. self.assertFailedTestCount(0, self.mtest_command + ['--suite', ':success'])
  752. self.assertFailedTestCount(3, self.mtest_command + ['--suite', ':fail'])
  753. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', ':success'])
  754. self.assertFailedTestCount(0, self.mtest_command + ['--no-suite', ':fail'])
  755. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj'])
  756. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc'])
  757. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail'])
  758. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix'])
  759. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj'])
  760. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc'])
  761. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail'])
  762. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix'])
  763. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj:fail'])
  764. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'mainprj:success'])
  765. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj:fail'])
  766. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'mainprj:success'])
  767. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail:fail'])
  768. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjfail:success'])
  769. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail:fail'])
  770. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail:success'])
  771. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:fail'])
  772. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:success'])
  773. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:fail'])
  774. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:success'])
  775. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix:fail'])
  776. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjmix:success'])
  777. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix:fail'])
  778. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix:success'])
  779. self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail'])
  780. self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj'])
  781. self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail'])
  782. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail', 'mainprj-failing_test'])
  783. self.assertFailedTestCount(1, self.mtest_command + ['--no-suite', 'subprjfail:fail', '--no-suite', 'subprjmix:fail'])
  784. def test_build_by_default(self):
  785. testdir = os.path.join(self.common_test_dir, '137 build by default')
  786. self.init(testdir)
  787. self.build()
  788. genfile = os.path.join(self.builddir, 'generated.dat')
  789. exe = os.path.join(self.builddir, 'fooprog' + exe_suffix)
  790. self.assertTrue(os.path.exists(genfile))
  791. self.assertFalse(os.path.exists(exe))
  792. self.build(target=('fooprog' + exe_suffix))
  793. self.assertTrue(os.path.exists(exe))
  794. def test_internal_include_order(self):
  795. testdir = os.path.join(self.common_test_dir, '138 include order')
  796. self.init(testdir)
  797. execmd = fxecmd = None
  798. for cmd in self.get_compdb():
  799. if 'someexe' in cmd['command']:
  800. execmd = cmd['command']
  801. continue
  802. if 'somefxe' in cmd['command']:
  803. fxecmd = cmd['command']
  804. continue
  805. if not execmd or not fxecmd:
  806. raise Exception('Could not find someexe and somfxe commands')
  807. # Check include order for 'someexe'
  808. incs = [a for a in shlex.split(execmd) if a.startswith("-I")]
  809. self.assertEqual(len(incs), 8)
  810. # target private dir
  811. self.assertPathEqual(incs[0], "-Isub4/someexe@exe")
  812. # target build subdir
  813. self.assertPathEqual(incs[1], "-Isub4")
  814. # target source subdir
  815. self.assertPathBasenameEqual(incs[2], 'sub4')
  816. # include paths added via per-target c_args: ['-I'...]
  817. self.assertPathBasenameEqual(incs[3], 'sub3')
  818. # target include_directories: build dir
  819. self.assertPathEqual(incs[4], "-Isub2")
  820. # target include_directories: source dir
  821. self.assertPathBasenameEqual(incs[5], 'sub2')
  822. # target internal dependency include_directories: build dir
  823. self.assertPathEqual(incs[6], "-Isub1")
  824. # target internal dependency include_directories: source dir
  825. self.assertPathBasenameEqual(incs[7], 'sub1')
  826. # Check include order for 'somefxe'
  827. incs = [a for a in shlex.split(fxecmd) if a.startswith('-I')]
  828. self.assertEqual(len(incs), 9)
  829. # target private dir
  830. self.assertPathEqual(incs[0], '-Isomefxe@exe')
  831. # target build dir
  832. self.assertPathEqual(incs[1], '-I.')
  833. # target source dir
  834. self.assertPathBasenameEqual(incs[2], os.path.basename(testdir))
  835. # target internal dependency correct include_directories: build dir
  836. self.assertPathEqual(incs[3], "-Isub4")
  837. # target internal dependency correct include_directories: source dir
  838. self.assertPathBasenameEqual(incs[4], 'sub4')
  839. # target internal dependency dep include_directories: build dir
  840. self.assertPathEqual(incs[5], "-Isub1")
  841. # target internal dependency dep include_directories: source dir
  842. self.assertPathBasenameEqual(incs[6], 'sub1')
  843. # target internal dependency wrong include_directories: build dir
  844. self.assertPathEqual(incs[7], "-Isub2")
  845. # target internal dependency wrong include_directories: source dir
  846. self.assertPathBasenameEqual(incs[8], 'sub2')
  847. def test_compiler_detection(self):
  848. '''
  849. Test that automatic compiler detection and setting from the environment
  850. both work just fine. This is needed because while running project tests
  851. and other unit tests, we always read CC/CXX/etc from the environment.
  852. '''
  853. gnu = mesonbuild.compilers.GnuCompiler
  854. clang = mesonbuild.compilers.ClangCompiler
  855. intel = mesonbuild.compilers.IntelCompiler
  856. msvc = mesonbuild.compilers.VisualStudioCCompiler
  857. ar = mesonbuild.linkers.ArLinker
  858. lib = mesonbuild.linkers.VisualStudioLinker
  859. langs = [('c', 'CC'), ('cpp', 'CXX')]
  860. if not is_windows():
  861. langs += [('objc', 'OBJC'), ('objcpp', 'OBJCXX')]
  862. testdir = os.path.join(self.unit_test_dir, '5 compiler detection')
  863. env = Environment(testdir, self.builddir, self.meson_command,
  864. get_fake_options(self.prefix), [])
  865. for lang, evar in langs:
  866. # Detect with evar and do sanity checks on that
  867. if evar in os.environ:
  868. ecc = getattr(env, 'detect_{}_compiler'.format(lang))(False)
  869. self.assertTrue(ecc.version)
  870. elinker = env.detect_static_linker(ecc)
  871. # Pop it so we don't use it for the next detection
  872. evalue = os.environ.pop(evar)
  873. # Very rough/strict heuristics. Would never work for actual
  874. # compiler detection, but should be ok for the tests.
  875. if os.path.basename(evalue).startswith('g'):
  876. self.assertIsInstance(ecc, gnu)
  877. self.assertIsInstance(elinker, ar)
  878. elif 'clang' in os.path.basename(evalue):
  879. self.assertIsInstance(ecc, clang)
  880. self.assertIsInstance(elinker, ar)
  881. elif os.path.basename(evalue).startswith('ic'):
  882. self.assertIsInstance(ecc, intel)
  883. self.assertIsInstance(elinker, ar)
  884. elif os.path.basename(evalue).startswith('cl'):
  885. self.assertIsInstance(ecc, msvc)
  886. self.assertIsInstance(elinker, lib)
  887. else:
  888. raise AssertionError('Unknown compiler {!r}'.format(evalue))
  889. # Check that we actually used the evalue correctly as the compiler
  890. self.assertEqual(ecc.get_exelist(), shlex.split(evalue))
  891. # Do auto-detection of compiler based on platform, PATH, etc.
  892. cc = getattr(env, 'detect_{}_compiler'.format(lang))(False)
  893. self.assertTrue(cc.version)
  894. linker = env.detect_static_linker(cc)
  895. # Check compiler type
  896. if isinstance(cc, gnu):
  897. self.assertIsInstance(linker, ar)
  898. if is_osx():
  899. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_OSX)
  900. elif is_windows():
  901. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_MINGW)
  902. elif is_cygwin():
  903. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_CYGWIN)
  904. else:
  905. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_STANDARD)
  906. if isinstance(cc, clang):
  907. self.assertIsInstance(linker, ar)
  908. if is_osx():
  909. self.assertEqual(cc.clang_type, mesonbuild.compilers.CLANG_OSX)
  910. elif is_windows():
  911. # Not implemented yet
  912. self.assertEqual(cc.clang_type, mesonbuild.compilers.CLANG_WIN)
  913. else:
  914. self.assertEqual(cc.clang_type, mesonbuild.compilers.CLANG_STANDARD)
  915. if isinstance(cc, intel):
  916. self.assertIsInstance(linker, ar)
  917. if is_osx():
  918. self.assertEqual(cc.icc_type, mesonbuild.compilers.ICC_OSX)
  919. elif is_windows():
  920. self.assertEqual(cc.icc_type, mesonbuild.compilers.ICC_WIN)
  921. else:
  922. self.assertEqual(cc.icc_type, mesonbuild.compilers.ICC_STANDARD)
  923. if isinstance(cc, msvc):
  924. self.assertTrue(is_windows())
  925. self.assertIsInstance(linker, lib)
  926. self.assertEqual(cc.id, 'msvc')
  927. self.assertTrue(hasattr(cc, 'is_64'))
  928. # If we're in the appveyor CI, we know what the compiler will be
  929. if 'arch' in os.environ:
  930. if os.environ['arch'] == 'x64':
  931. self.assertTrue(cc.is_64)
  932. else:
  933. self.assertFalse(cc.is_64)
  934. # Set evar ourselves to a wrapper script that just calls the same
  935. # exelist + some argument. This is meant to test that setting
  936. # something like `ccache gcc -pipe` or `distcc ccache gcc` works.
  937. wrapper = os.path.join(testdir, 'compiler wrapper.py')
  938. wrappercc = [sys.executable, wrapper] + cc.get_exelist() + ['-DSOME_ARG']
  939. wrappercc_s = ''
  940. for w in wrappercc:
  941. wrappercc_s += shlex.quote(w) + ' '
  942. os.environ[evar] = wrappercc_s
  943. wcc = getattr(env, 'detect_{}_compiler'.format(lang))(False)
  944. # Check static linker too
  945. wrapperlinker = [sys.executable, wrapper] + linker.get_exelist() + linker.get_always_args()
  946. wrapperlinker_s = ''
  947. for w in wrapperlinker:
  948. wrapperlinker_s += shlex.quote(w) + ' '
  949. os.environ['AR'] = wrapperlinker_s
  950. wlinker = env.detect_static_linker(wcc)
  951. # Must be the same type since it's a wrapper around the same exelist
  952. self.assertIs(type(cc), type(wcc))
  953. self.assertIs(type(linker), type(wlinker))
  954. # Ensure that the exelist is correct
  955. self.assertEqual(wcc.get_exelist(), wrappercc)
  956. self.assertEqual(wlinker.get_exelist(), wrapperlinker)
  957. # Ensure that the version detection worked correctly
  958. self.assertEqual(cc.version, wcc.version)
  959. if hasattr(cc, 'is_64'):
  960. self.assertEqual(cc.is_64, wcc.is_64)
  961. def test_always_prefer_c_compiler_for_asm(self):
  962. testdir = os.path.join(self.common_test_dir, '141 c cpp and asm')
  963. # Skip if building with MSVC
  964. env = Environment(testdir, self.builddir, self.meson_command,
  965. get_fake_options(self.prefix), [])
  966. if env.detect_c_compiler(False).get_id() == 'msvc':
  967. raise unittest.SkipTest('MSVC can\'t compile assembly')
  968. self.init(testdir)
  969. commands = {'c-asm': {}, 'cpp-asm': {}, 'cpp-c-asm': {}, 'c-cpp-asm': {}}
  970. for cmd in self.get_compdb():
  971. # Get compiler
  972. split = shlex.split(cmd['command'])
  973. if split[0] == 'ccache':
  974. compiler = split[1]
  975. else:
  976. compiler = split[0]
  977. # Classify commands
  978. if 'Ic-asm' in cmd['command']:
  979. if cmd['file'].endswith('.S'):
  980. commands['c-asm']['asm'] = compiler
  981. elif cmd['file'].endswith('.c'):
  982. commands['c-asm']['c'] = compiler
  983. else:
  984. raise AssertionError('{!r} found in cpp-asm?'.format(cmd['command']))
  985. elif 'Icpp-asm' in cmd['command']:
  986. if cmd['file'].endswith('.S'):
  987. commands['cpp-asm']['asm'] = compiler
  988. elif cmd['file'].endswith('.cpp'):
  989. commands['cpp-asm']['cpp'] = compiler
  990. else:
  991. raise AssertionError('{!r} found in cpp-asm?'.format(cmd['command']))
  992. elif 'Ic-cpp-asm' in cmd['command']:
  993. if cmd['file'].endswith('.S'):
  994. commands['c-cpp-asm']['asm'] = compiler
  995. elif cmd['file'].endswith('.c'):
  996. commands['c-cpp-asm']['c'] = compiler
  997. elif cmd['file'].endswith('.cpp'):
  998. commands['c-cpp-asm']['cpp'] = compiler
  999. else:
  1000. raise AssertionError('{!r} found in c-cpp-asm?'.format(cmd['command']))
  1001. elif 'Icpp-c-asm' in cmd['command']:
  1002. if cmd['file'].endswith('.S'):
  1003. commands['cpp-c-asm']['asm'] = compiler
  1004. elif cmd['file'].endswith('.c'):
  1005. commands['cpp-c-asm']['c'] = compiler
  1006. elif cmd['file'].endswith('.cpp'):
  1007. commands['cpp-c-asm']['cpp'] = compiler
  1008. else:
  1009. raise AssertionError('{!r} found in cpp-c-asm?'.format(cmd['command']))
  1010. else:
  1011. raise AssertionError('Unknown command {!r} found'.format(cmd['command']))
  1012. # Check that .S files are always built with the C compiler
  1013. self.assertEqual(commands['c-asm']['asm'], commands['c-asm']['c'])
  1014. self.assertEqual(commands['c-asm']['asm'], commands['cpp-asm']['asm'])
  1015. self.assertEqual(commands['cpp-asm']['asm'], commands['c-cpp-asm']['c'])
  1016. self.assertEqual(commands['c-cpp-asm']['asm'], commands['c-cpp-asm']['c'])
  1017. self.assertEqual(commands['cpp-c-asm']['asm'], commands['cpp-c-asm']['c'])
  1018. self.assertNotEqual(commands['cpp-asm']['asm'], commands['cpp-asm']['cpp'])
  1019. self.assertNotEqual(commands['c-cpp-asm']['c'], commands['c-cpp-asm']['cpp'])
  1020. self.assertNotEqual(commands['cpp-c-asm']['c'], commands['cpp-c-asm']['cpp'])
  1021. # Check that the c-asm target is always linked with the C linker
  1022. build_ninja = os.path.join(self.builddir, 'build.ninja')
  1023. with open(build_ninja, 'r', encoding='utf-8') as f:
  1024. contents = f.read()
  1025. m = re.search('build c-asm.*: c_LINKER', contents)
  1026. self.assertIsNotNone(m, msg=contents)
  1027. def test_preprocessor_checks_CPPFLAGS(self):
  1028. '''
  1029. Test that preprocessor compiler checks read CPPFLAGS but not CFLAGS
  1030. '''
  1031. testdir = os.path.join(self.common_test_dir, '140 get define')
  1032. define = 'MESON_TEST_DEFINE_VALUE'
  1033. # NOTE: this list can't have \n, ' or "
  1034. # \n is never substituted by the GNU pre-processor via a -D define
  1035. # ' and " confuse shlex.split() even when they are escaped
  1036. # % and # confuse the MSVC preprocessor
  1037. value = 'spaces and fun!@$^&*()-=_+{}[]:;<>?,./~`'
  1038. os.environ['CPPFLAGS'] = '-D{}="{}"'.format(define, value)
  1039. os.environ['CFLAGS'] = '-DMESON_FAIL_VALUE=cflags-read'.format(define)
  1040. self.init(testdir, ['-D{}={}'.format(define, value)])
  1041. def test_custom_target_exe_data_deterministic(self):
  1042. testdir = os.path.join(self.common_test_dir, '117 custom target capture')
  1043. self.init(testdir)
  1044. meson_exe_dat1 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
  1045. self.wipe()
  1046. self.init(testdir)
  1047. meson_exe_dat2 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
  1048. self.assertListEqual(meson_exe_dat1, meson_exe_dat2)
  1049. def test_source_changes_cause_rebuild(self):
  1050. '''
  1051. Test that changes to sources and headers cause rebuilds, but not
  1052. changes to unused files (as determined by the dependency file) in the
  1053. input files list.
  1054. '''
  1055. testdir = os.path.join(self.common_test_dir, '22 header in file list')
  1056. self.init(testdir)
  1057. self.build()
  1058. # Immediately rebuilding should not do anything
  1059. self.assertBuildIsNoop()
  1060. # Changing mtime of header.h should rebuild everything
  1061. self.utime(os.path.join(testdir, 'header.h'))
  1062. self.assertRebuiltTarget('prog')
  1063. def test_custom_target_changes_cause_rebuild(self):
  1064. '''
  1065. Test that in a custom target, changes to the input files, the
  1066. ExternalProgram, and any File objects on the command-line cause
  1067. a rebuild.
  1068. '''
  1069. testdir = os.path.join(self.common_test_dir, '64 custom header generator')
  1070. self.init(testdir)
  1071. self.build()
  1072. # Immediately rebuilding should not do anything
  1073. self.assertBuildIsNoop()
  1074. # Changing mtime of these should rebuild everything
  1075. for f in ('input.def', 'makeheader.py', 'somefile.txt'):
  1076. self.utime(os.path.join(testdir, f))
  1077. self.assertRebuiltTarget('prog')
  1078. def test_static_library_lto(self):
  1079. '''
  1080. Test that static libraries can be built with LTO and linked to
  1081. executables. On Linux, this requires the use of gcc-ar.
  1082. https://github.com/mesonbuild/meson/issues/1646
  1083. '''
  1084. testdir = os.path.join(self.common_test_dir, '5 linkstatic')
  1085. self.init(testdir, extra_args='-Db_lto=true')
  1086. self.build()
  1087. self.run_tests()
  1088. def test_dist_git(self):
  1089. if not shutil.which('git'):
  1090. raise unittest.SkipTest('Git not found')
  1091. def git_init(project_dir):
  1092. subprocess.check_call(['git', 'init'], cwd=project_dir)
  1093. subprocess.check_call(['git', 'config',
  1094. 'user.name', 'Author Person'], cwd=project_dir)
  1095. subprocess.check_call(['git', 'config',
  1096. 'user.email', 'teh_coderz@example.com'], cwd=project_dir)
  1097. subprocess.check_call(['git', 'add', 'meson.build', 'distexe.c'], cwd=project_dir)
  1098. subprocess.check_call(['git', 'commit', '-a', '-m', 'I am a project'], cwd=project_dir)
  1099. try:
  1100. self.dist_impl(git_init)
  1101. except PermissionError:
  1102. # When run under Windows CI, something (virus scanner?)
  1103. # holds on to the git files so cleaning up the dir
  1104. # fails sometimes.
  1105. pass
  1106. def test_dist_hg(self):
  1107. if not shutil.which('hg'):
  1108. raise unittest.SkipTest('Mercurial not found')
  1109. if self.backend is not Backend.ninja:
  1110. raise unittest.SkipTest('Dist is only supported with Ninja')
  1111. def hg_init(project_dir):
  1112. subprocess.check_call(['hg', 'init'], cwd=project_dir)
  1113. with open(os.path.join(project_dir, '.hg', 'hgrc'), 'w') as f:
  1114. print('[ui]', file=f)
  1115. print('username=Author Person <teh_coderz@example.com>', file=f)
  1116. subprocess.check_call(['hg', 'add', 'meson.build', 'distexe.c'], cwd=project_dir)
  1117. subprocess.check_call(['hg', 'commit', '-m', 'I am a project'], cwd=project_dir)
  1118. try:
  1119. self.dist_impl(hg_init)
  1120. except PermissionError:
  1121. # When run under Windows CI, something (virus scanner?)
  1122. # holds on to the hg files so cleaning up the dir
  1123. # fails sometimes.
  1124. pass
  1125. def dist_impl(self, vcs_init):
  1126. # Create this on the fly because having rogue .git directories inside
  1127. # the source tree leads to all kinds of trouble.
  1128. with tempfile.TemporaryDirectory() as project_dir:
  1129. with open(os.path.join(project_dir, 'meson.build'), 'w') as ofile:
  1130. ofile.write('''project('disttest', 'c', version : '1.4.3')
  1131. e = executable('distexe', 'distexe.c')
  1132. test('dist test', e)
  1133. ''')
  1134. with open(os.path.join(project_dir, 'distexe.c'), 'w') as ofile:
  1135. ofile.write('''#include<stdio.h>
  1136. int main(int argc, char **argv) {
  1137. printf("I am a distribution test.\\n");
  1138. return 0;
  1139. }
  1140. ''')
  1141. vcs_init(project_dir)
  1142. self.init(project_dir)
  1143. self.build('dist')
  1144. distfile = os.path.join(self.distdir, 'disttest-1.4.3.tar.xz')
  1145. checksumfile = distfile + '.sha256sum'
  1146. self.assertTrue(os.path.exists(distfile))
  1147. self.assertTrue(os.path.exists(checksumfile))
  1148. def test_rpath_uses_ORIGIN(self):
  1149. '''
  1150. Test that built targets use $ORIGIN in rpath, which ensures that they
  1151. are relocatable and ensures that builds are reproducible since the
  1152. build directory won't get embedded into the built binaries.
  1153. '''
  1154. if is_windows() or is_cygwin():
  1155. raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH')
  1156. testdir = os.path.join(self.common_test_dir, '46 library chain')
  1157. self.init(testdir)
  1158. self.build()
  1159. for each in ('prog', 'subdir/liblib1.so', 'subdir/subdir2/liblib2.so',
  1160. 'subdir/subdir3/liblib3.so'):
  1161. rpath = get_rpath(os.path.join(self.builddir, each))
  1162. self.assertTrue(rpath)
  1163. for path in rpath.split(':'):
  1164. self.assertTrue(path.startswith('$ORIGIN'), msg=(each, path))
  1165. def test_dash_d_dedup(self):
  1166. testdir = os.path.join(self.unit_test_dir, '10 d dedup')
  1167. self.init(testdir)
  1168. cmd = self.get_compdb()[0]['command']
  1169. self.assertTrue('-D FOO -D BAR' in cmd or
  1170. '"-D" "FOO" "-D" "BAR"' in cmd or
  1171. '/D FOO /D BAR' in cmd or
  1172. '"/D" "FOO" "/D" "BAR"' in cmd)
  1173. class FailureTests(BasePlatformTests):
  1174. '''
  1175. Tests that test failure conditions. Build files here should be dynamically
  1176. generated and static tests should go into `test cases/failing*`.
  1177. This is useful because there can be many ways in which a particular
  1178. function can fail, and creating failing tests for all of them is tedious
  1179. and slows down testing.
  1180. '''
  1181. dnf = "[Dd]ependency.*not found"
  1182. def setUp(self):
  1183. super().setUp()
  1184. self.srcdir = os.path.realpath(tempfile.mkdtemp())
  1185. self.mbuild = os.path.join(self.srcdir, 'meson.build')
  1186. def tearDown(self):
  1187. super().tearDown()
  1188. windows_proof_rmtree(self.srcdir)
  1189. def assertMesonRaises(self, contents, match, extra_args=None, langs=None):
  1190. '''
  1191. Assert that running meson configure on the specified @contents raises
  1192. a error message matching regex @match.
  1193. '''
  1194. if langs is None:
  1195. langs = []
  1196. with open(self.mbuild, 'w') as f:
  1197. f.write("project('failure test', 'c', 'cpp')\n")
  1198. for lang in langs:
  1199. f.write("add_languages('{}', required : false)\n".format(lang))
  1200. f.write(contents)
  1201. # Force tracebacks so we can detect them properly
  1202. os.environ['MESON_FORCE_BACKTRACE'] = '1'
  1203. with self.assertRaisesRegex(DependencyException, match, msg=contents):
  1204. # Must run in-process or we'll get a generic CalledProcessError
  1205. self.init(self.srcdir, extra_args=extra_args, inprocess=True)
  1206. def assertMesonOutputs(self, contents, match, extra_args=None, langs=None):
  1207. '''
  1208. Assert that running meson configure on the specified @contents outputs
  1209. something that matches regex @match.
  1210. '''
  1211. if langs is None:
  1212. langs = []
  1213. with open(self.mbuild, 'w') as f:
  1214. f.write("project('output test', 'c', 'cpp')\n")
  1215. for lang in langs:
  1216. f.write("add_languages('{}', required : false)\n".format(lang))
  1217. f.write(contents)
  1218. # Run in-process for speed and consistency with assertMesonRaises
  1219. out = self.init(self.srcdir, extra_args=extra_args, inprocess=True)
  1220. self.assertRegex(out, match)
  1221. def test_dependency(self):
  1222. if not shutil.which('pkg-config'):
  1223. raise unittest.SkipTest('pkg-config not found')
  1224. a = (("dependency('zlib', method : 'fail')", "'fail' is invalid"),
  1225. ("dependency('zlib', static : '1')", "[Ss]tatic.*boolean"),
  1226. ("dependency('zlib', version : 1)", "[Vv]ersion.*string or list"),
  1227. ("dependency('zlib', required : 1)", "[Rr]equired.*boolean"),
  1228. ("dependency('zlib', method : 1)", "[Mm]ethod.*string"),
  1229. ("dependency('zlibfail')", self.dnf),)
  1230. for contents, match in a:
  1231. self.assertMesonRaises(contents, match)
  1232. def test_apple_frameworks_dependency(self):
  1233. if not is_osx():
  1234. raise unittest.SkipTest('only run on macOS')
  1235. self.assertMesonRaises("dependency('appleframeworks')",
  1236. "requires at least one module")
  1237. def test_sdl2_notfound_dependency(self):
  1238. # Want to test failure, so skip if available
  1239. if shutil.which('sdl2-config'):
  1240. raise unittest.SkipTest('sdl2-config found')
  1241. self.assertMesonRaises("dependency('sdl2', method : 'sdlconfig')", self.dnf)
  1242. self.assertMesonRaises("dependency('sdl2', method : 'pkg-config')", self.dnf)
  1243. def test_gnustep_notfound_dependency(self):
  1244. # Want to test failure, so skip if available
  1245. if shutil.which('gnustep-config'):
  1246. raise unittest.SkipTest('gnustep-config found')
  1247. self.assertMesonRaises("dependency('gnustep')",
  1248. "(requires a Objc compiler|{})".format(self.dnf),
  1249. langs = ['objc'])
  1250. def test_wx_notfound_dependency(self):
  1251. # Want to test failure, so skip if available
  1252. if shutil.which('wx-config-3.0') or shutil.which('wx-config'):
  1253. raise unittest.SkipTest('wx-config or wx-config-3.0 found')
  1254. self.assertMesonRaises("dependency('wxwidgets')", self.dnf)
  1255. self.assertMesonOutputs("dependency('wxwidgets', required : false)",
  1256. "nor wx-config found")
  1257. def test_wx_dependency(self):
  1258. if not shutil.which('wx-config-3.0') and not shutil.which('wx-config'):
  1259. raise unittest.SkipTest('Neither wx-config nor wx-config-3.0 found')
  1260. self.assertMesonRaises("dependency('wxwidgets', modules : 1)",
  1261. "module argument is not a string")
  1262. def test_llvm_dependency(self):
  1263. self.assertMesonRaises("dependency('llvm', modules : 'fail')",
  1264. "(required.*fail|{})".format(self.dnf))
  1265. def test_boost_notfound_dependency(self):
  1266. # Can be run even if Boost is found or not
  1267. self.assertMesonRaises("dependency('boost', modules : 1)",
  1268. "module.*not a string")
  1269. self.assertMesonRaises("dependency('boost', modules : 'fail')",
  1270. "(fail.*not found|{})".format(self.dnf))
  1271. def test_boost_BOOST_ROOT_dependency(self):
  1272. # Test BOOST_ROOT; can be run even if Boost is found or not
  1273. os.environ['BOOST_ROOT'] = 'relative/path'
  1274. self.assertMesonRaises("dependency('boost')",
  1275. "(BOOST_ROOT.*absolute|{})".format(self.dnf))
  1276. class WindowsTests(BasePlatformTests):
  1277. '''
  1278. Tests that should run on Cygwin, MinGW, and MSVC
  1279. '''
  1280. def setUp(self):
  1281. super().setUp()
  1282. self.platform_test_dir = os.path.join(self.src_root, 'test cases/windows')
  1283. def test_find_program(self):
  1284. '''
  1285. Test that Windows-specific edge-cases in find_program are functioning
  1286. correctly. Cannot be an ordinary test because it involves manipulating
  1287. PATH to point to a directory with Python scripts.
  1288. '''
  1289. testdir = os.path.join(self.platform_test_dir, '9 find program')
  1290. # Find `cmd` and `cmd.exe`
  1291. prog1 = ExternalProgram('cmd')
  1292. self.assertTrue(prog1.found(), msg='cmd not found')
  1293. prog2 = ExternalProgram('cmd.exe')
  1294. self.assertTrue(prog2.found(), msg='cmd.exe not found')
  1295. self.assertPathEqual(prog1.get_path(), prog2.get_path())
  1296. # Find cmd with an absolute path that's missing the extension
  1297. cmd_path = prog2.get_path()[:-4]
  1298. prog = ExternalProgram(cmd_path)
  1299. self.assertTrue(prog.found(), msg='{!r} not found'.format(cmd_path))
  1300. # Finding a script with no extension inside a directory works
  1301. prog = ExternalProgram(os.path.join(testdir, 'test-script'))
  1302. self.assertTrue(prog.found(), msg='test-script not found')
  1303. # Finding a script with an extension inside a directory works
  1304. prog = ExternalProgram(os.path.join(testdir, 'test-script-ext.py'))
  1305. self.assertTrue(prog.found(), msg='test-script-ext.py not found')
  1306. # Finding a script in PATH w/o extension works and adds the interpreter
  1307. os.environ['PATH'] += os.pathsep + testdir
  1308. prog = ExternalProgram('test-script-ext')
  1309. self.assertTrue(prog.found(), msg='test-script-ext not found in PATH')
  1310. self.assertPathEqual(prog.get_command()[0], sys.executable)
  1311. self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
  1312. # Finding a script in PATH with extension works and adds the interpreter
  1313. prog = ExternalProgram('test-script-ext.py')
  1314. self.assertTrue(prog.found(), msg='test-script-ext.py not found in PATH')
  1315. self.assertPathEqual(prog.get_command()[0], sys.executable)
  1316. self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
  1317. def test_ignore_libs(self):
  1318. '''
  1319. Test that find_library on libs that are to be ignored returns an empty
  1320. array of arguments. Must be a unit test because we cannot inspect
  1321. ExternalLibraryHolder from build files.
  1322. '''
  1323. testdir = os.path.join(self.platform_test_dir, '1 basic')
  1324. env = Environment(testdir, self.builddir, self.meson_command,
  1325. get_fake_options(self.prefix), [])
  1326. cc = env.detect_c_compiler(False)
  1327. if cc.id != 'msvc':
  1328. raise unittest.SkipTest('Not using MSVC')
  1329. # To force people to update this test, and also test
  1330. self.assertEqual(set(cc.ignore_libs), {'c', 'm', 'pthread'})
  1331. for l in cc.ignore_libs:
  1332. self.assertEqual(cc.find_library(l, env, []), [])
  1333. class LinuxlikeTests(BasePlatformTests):
  1334. '''
  1335. Tests that should run on Linux and *BSD
  1336. '''
  1337. def test_basic_soname(self):
  1338. '''
  1339. Test that the soname is set correctly for shared libraries. This can't
  1340. be an ordinary test case because we need to run `readelf` and actually
  1341. check the soname.
  1342. https://github.com/mesonbuild/meson/issues/785
  1343. '''
  1344. testdir = os.path.join(self.common_test_dir, '4 shared')
  1345. self.init(testdir)
  1346. self.build()
  1347. lib1 = os.path.join(self.builddir, 'libmylib.so')
  1348. soname = get_soname(lib1)
  1349. self.assertEqual(soname, 'libmylib.so')
  1350. def test_custom_soname(self):
  1351. '''
  1352. Test that the soname is set correctly for shared libraries when
  1353. a custom prefix and/or suffix is used. This can't be an ordinary test
  1354. case because we need to run `readelf` and actually check the soname.
  1355. https://github.com/mesonbuild/meson/issues/785
  1356. '''
  1357. testdir = os.path.join(self.common_test_dir, '27 library versions')
  1358. self.init(testdir)
  1359. self.build()
  1360. lib1 = os.path.join(self.builddir, 'prefixsomelib.suffix')
  1361. soname = get_soname(lib1)
  1362. self.assertEqual(soname, 'prefixsomelib.suffix')
  1363. def test_pic(self):
  1364. '''
  1365. Test that -fPIC is correctly added to static libraries when b_staticpic
  1366. is true and not when it is false. This can't be an ordinary test case
  1367. because we need to inspect the compiler database.
  1368. '''
  1369. testdir = os.path.join(self.common_test_dir, '3 static')
  1370. self.init(testdir)
  1371. compdb = self.get_compdb()
  1372. self.assertIn('-fPIC', compdb[0]['command'])
  1373. self.setconf('-Db_staticpic=false')
  1374. # Regenerate build
  1375. self.build()
  1376. compdb = self.get_compdb()
  1377. self.assertNotIn('-fPIC', compdb[0]['command'])
  1378. def test_pkgconfig_gen(self):
  1379. '''
  1380. Test that generated pkg-config files can be found and have the correct
  1381. version and link args. This can't be an ordinary test case because we
  1382. need to run pkg-config outside of a Meson build file.
  1383. https://github.com/mesonbuild/meson/issues/889
  1384. '''
  1385. testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
  1386. self.init(testdir)
  1387. env = FakeEnvironment()
  1388. kwargs = {'required': True, 'silent': True}
  1389. os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
  1390. simple_dep = PkgConfigDependency('libfoo', env, kwargs)
  1391. self.assertTrue(simple_dep.found())
  1392. self.assertEqual(simple_dep.get_version(), '1.0')
  1393. self.assertIn('-lfoo', simple_dep.get_link_args())
  1394. self.assertEqual(simple_dep.get_pkgconfig_variable('foo'), 'bar')
  1395. self.assertPathEqual(simple_dep.get_pkgconfig_variable('datadir'), '/usr/data')
  1396. def test_vala_c_warnings(self):
  1397. '''
  1398. Test that no warnings are emitted for C code generated by Vala. This
  1399. can't be an ordinary test case because we need to inspect the compiler
  1400. database.
  1401. https://github.com/mesonbuild/meson/issues/864
  1402. '''
  1403. testdir = os.path.join(self.vala_test_dir, '5 target glib')
  1404. self.init(testdir)
  1405. compdb = self.get_compdb()
  1406. vala_command = None
  1407. c_command = None
  1408. for each in compdb:
  1409. if each['file'].endswith('GLib.Thread.c'):
  1410. vala_command = each['command']
  1411. elif each['file'].endswith('GLib.Thread.vala'):
  1412. continue
  1413. elif each['file'].endswith('retcode.c'):
  1414. c_command = each['command']
  1415. else:
  1416. m = 'Unknown file {!r} in vala_c_warnings test'.format(each['file'])
  1417. raise AssertionError(m)
  1418. self.assertIsNotNone(vala_command)
  1419. self.assertIsNotNone(c_command)
  1420. # -w suppresses all warnings, should be there in Vala but not in C
  1421. self.assertIn(" -w ", vala_command)
  1422. self.assertNotIn(" -w ", c_command)
  1423. # -Wall enables all warnings, should be there in C but not in Vala
  1424. self.assertNotIn(" -Wall ", vala_command)
  1425. self.assertIn(" -Wall ", c_command)
  1426. # -Werror converts warnings to errors, should always be there since it's
  1427. # injected by an unrelated piece of code and the project has werror=true
  1428. self.assertIn(" -Werror ", vala_command)
  1429. self.assertIn(" -Werror ", c_command)
  1430. def test_qt5dependency_pkgconfig_detection(self):
  1431. '''
  1432. Test that qt4 and qt5 detection with pkgconfig works.
  1433. '''
  1434. # Verify Qt4 or Qt5 can be found with pkg-config
  1435. if not shutil.which('pkg-config'):
  1436. raise unittest.SkipTest('pkg-config not found')
  1437. qt4 = subprocess.call(['pkg-config', '--exists', 'QtCore'])
  1438. qt5 = subprocess.call(['pkg-config', '--exists', 'Qt5Core'])
  1439. if qt4 != 0 or qt5 != 0:
  1440. raise unittest.SkipTest('Qt not found with pkg-config')
  1441. testdir = os.path.join(self.framework_test_dir, '4 qt')
  1442. self.init(testdir, ['-Dmethod=pkg-config'])
  1443. # Confirm that the dependency was found with qmake
  1444. msg = 'Qt4 native `pkg-config` dependency (modules: Core, Gui) found: YES\n'
  1445. msg2 = 'Qt5 native `pkg-config` dependency (modules: Core, Gui) found: YES\n'
  1446. mesonlog = self.get_meson_log()
  1447. self.assertTrue(msg in mesonlog or msg2 in mesonlog)
  1448. def test_qt5dependency_qmake_detection(self):
  1449. '''
  1450. Test that qt5 detection with qmake works. This can't be an ordinary
  1451. test case because it involves setting the environment.
  1452. '''
  1453. # Verify that qmake is for Qt5
  1454. if not shutil.which('qmake-qt5'):
  1455. if not shutil.which('qmake'):
  1456. raise unittest.SkipTest('QMake not found')
  1457. # For some inexplicable reason qmake --version gives different
  1458. # results when run from the command line vs invoked by Python.
  1459. # Check for both cases in case this behavior changes in the future.
  1460. output = subprocess.getoutput(['qmake', '--version'])
  1461. if 'Qt version 5' not in output and 'qt5' not in output:
  1462. raise unittest.SkipTest('Qmake found, but it is not for Qt 5.')
  1463. # Disable pkg-config codepath and force searching with qmake/qmake-qt5
  1464. testdir = os.path.join(self.framework_test_dir, '4 qt')
  1465. self.init(testdir, ['-Dmethod=qmake'])
  1466. # Confirm that the dependency was found with qmake
  1467. msg = 'Qt5 native `qmake-qt5` dependency (modules: Core) found: YES\n'
  1468. msg2 = 'Qt5 native `qmake` dependency (modules: Core) found: YES\n'
  1469. mesonlog = self.get_meson_log()
  1470. self.assertTrue(msg in mesonlog or msg2 in mesonlog)
  1471. def _test_soname_impl(self, libpath, install):
  1472. testdir = os.path.join(self.unit_test_dir, '1 soname')
  1473. self.init(testdir)
  1474. self.build()
  1475. if install:
  1476. self.install()
  1477. # File without aliases set.
  1478. nover = os.path.join(libpath, 'libnover.so')
  1479. self.assertTrue(os.path.exists(nover))
  1480. self.assertFalse(os.path.islink(nover))
  1481. self.assertEqual(get_soname(nover), 'libnover.so')
  1482. self.assertEqual(len(glob(nover[:-3] + '*')), 1)
  1483. # File with version set
  1484. verset = os.path.join(libpath, 'libverset.so')
  1485. self.assertTrue(os.path.exists(verset + '.4.5.6'))
  1486. self.assertEqual(os.readlink(verset), 'libverset.so.4')
  1487. self.assertEqual(get_soname(verset), 'libverset.so.4')
  1488. self.assertEqual(len(glob(verset[:-3] + '*')), 3)
  1489. # File with soversion set
  1490. soverset = os.path.join(libpath, 'libsoverset.so')
  1491. self.assertTrue(os.path.exists(soverset + '.1.2.3'))
  1492. self.assertEqual(os.readlink(soverset), 'libsoverset.so.1.2.3')
  1493. self.assertEqual(get_soname(soverset), 'libsoverset.so.1.2.3')
  1494. self.assertEqual(len(glob(soverset[:-3] + '*')), 2)
  1495. # File with version and soversion set to same values
  1496. settosame = os.path.join(libpath, 'libsettosame.so')
  1497. self.assertTrue(os.path.exists(settosame + '.7.8.9'))
  1498. self.assertEqual(os.readlink(settosame), 'libsettosame.so.7.8.9')
  1499. self.assertEqual(get_soname(settosame), 'libsettosame.so.7.8.9')
  1500. self.assertEqual(len(glob(settosame[:-3] + '*')), 2)
  1501. # File with version and soversion set to different values
  1502. bothset = os.path.join(libpath, 'libbothset.so')
  1503. self.assertTrue(os.path.exists(bothset + '.1.2.3'))
  1504. self.assertEqual(os.readlink(bothset), 'libbothset.so.1.2.3')
  1505. self.assertEqual(os.readlink(bothset + '.1.2.3'), 'libbothset.so.4.5.6')
  1506. self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3')
  1507. self.assertEqual(len(glob(bothset[:-3] + '*')), 3)
  1508. def test_soname(self):
  1509. self._test_soname_impl(self.builddir, False)
  1510. def test_installed_soname(self):
  1511. self._test_soname_impl(self.installdir + self.libdir, True)
  1512. def test_compiler_check_flags_order(self):
  1513. '''
  1514. Test that compiler check flags override all other flags. This can't be
  1515. an ordinary test case because it needs the environment to be set.
  1516. '''
  1517. Oflag = '-O3'
  1518. os.environ['CFLAGS'] = os.environ['CXXFLAGS'] = Oflag
  1519. testdir = os.path.join(self.common_test_dir, '43 has function')
  1520. self.init(testdir)
  1521. cmds = self.get_meson_log_compiler_checks()
  1522. for cmd in cmds:
  1523. if cmd[0] == 'ccache':
  1524. cmd = cmd[1:]
  1525. # Verify that -I flags from the `args` kwarg are first
  1526. # This is set in the '43 has function' test case
  1527. self.assertEqual(cmd[1], '-I/tmp')
  1528. # Verify that -O3 set via the environment is overriden by -O0
  1529. Oargs = [arg for arg in cmd if arg.startswith('-O')]
  1530. self.assertEqual(Oargs, [Oflag, '-O0'])
  1531. def _test_stds_impl(self, testdir, compiler, p):
  1532. lang_std = p + '_std'
  1533. # Check that all the listed -std=xxx options for this compiler work
  1534. # just fine when used
  1535. for v in compiler.get_options()[lang_std].choices:
  1536. std_opt = '{}={}'.format(lang_std, v)
  1537. self.init(testdir, ['-D' + std_opt])
  1538. cmd = self.get_compdb()[0]['command']
  1539. if v != 'none':
  1540. cmd_std = " -std={} ".format(v)
  1541. self.assertIn(cmd_std, cmd)
  1542. try:
  1543. self.build()
  1544. except:
  1545. print('{} was {!r}'.format(lang_std, v))
  1546. raise
  1547. self.wipe()
  1548. # Check that an invalid std option in CFLAGS/CPPFLAGS fails
  1549. # Needed because by default ICC ignores invalid options
  1550. cmd_std = '-std=FAIL'
  1551. env_flags = p.upper() + 'FLAGS'
  1552. os.environ[env_flags] = cmd_std
  1553. self.init(testdir)
  1554. cmd = self.get_compdb()[0]['command']
  1555. qcmd_std = " {} ".format(cmd_std)
  1556. self.assertIn(qcmd_std, cmd)
  1557. with self.assertRaises(subprocess.CalledProcessError,
  1558. msg='{} should have failed'.format(qcmd_std)):
  1559. self.build()
  1560. def test_compiler_c_stds(self):
  1561. '''
  1562. Test that C stds specified for this compiler can all be used. Can't be
  1563. an ordinary test because it requires passing options to meson.
  1564. '''
  1565. testdir = os.path.join(self.common_test_dir, '1 trivial')
  1566. env = Environment(testdir, self.builddir, self.meson_command,
  1567. get_fake_options(self.prefix), [])
  1568. cc = env.detect_c_compiler(False)
  1569. self._test_stds_impl(testdir, cc, 'c')
  1570. def test_compiler_cpp_stds(self):
  1571. '''
  1572. Test that C++ stds specified for this compiler can all be used. Can't
  1573. be an ordinary test because it requires passing options to meson.
  1574. '''
  1575. testdir = os.path.join(self.common_test_dir, '2 cpp')
  1576. env = Environment(testdir, self.builddir, self.meson_command,
  1577. get_fake_options(self.prefix), [])
  1578. cpp = env.detect_cpp_compiler(False)
  1579. self._test_stds_impl(testdir, cpp, 'cpp')
  1580. def test_unity_subproj(self):
  1581. testdir = os.path.join(self.common_test_dir, '49 subproject')
  1582. self.init(testdir, extra_args='--unity=subprojects')
  1583. self.assertTrue(os.path.exists(os.path.join(self.builddir, 'subprojects/sublib/simpletest@exe/simpletest-unity.c')))
  1584. self.assertTrue(os.path.exists(os.path.join(self.builddir, 'subprojects/sublib/sublib@sha/sublib-unity.c')))
  1585. self.assertFalse(os.path.exists(os.path.join(self.builddir, 'user@exe/user-unity.c')))
  1586. self.build()
  1587. def test_installed_modes(self):
  1588. '''
  1589. Test that files installed by these tests have the correct permissions.
  1590. Can't be an ordinary test because our installed_files.txt is very basic.
  1591. '''
  1592. # Test file modes
  1593. testdir = os.path.join(self.common_test_dir, '12 data')
  1594. self.init(testdir)
  1595. self.install()
  1596. f = os.path.join(self.installdir, 'etc', 'etcfile.dat')
  1597. found_mode = stat.filemode(os.stat(f).st_mode)
  1598. want_mode = 'rw------T'
  1599. self.assertEqual(want_mode, found_mode[1:])
  1600. f = os.path.join(self.installdir, 'usr', 'bin', 'runscript.sh')
  1601. statf = os.stat(f)
  1602. found_mode = stat.filemode(statf.st_mode)
  1603. want_mode = 'rwxr-sr-x'
  1604. self.assertEqual(want_mode, found_mode[1:])
  1605. if os.getuid() == 0:
  1606. # The chown failed nonfatally if we're not root
  1607. self.assertEqual(0, statf.st_uid)
  1608. self.assertEqual(0, statf.st_gid)
  1609. f = os.path.join(self.installdir, 'usr', 'share', 'progname',
  1610. 'fileobject_datafile.dat')
  1611. orig = os.path.join(testdir, 'fileobject_datafile.dat')
  1612. statf = os.stat(f)
  1613. statorig = os.stat(orig)
  1614. found_mode = stat.filemode(statf.st_mode)
  1615. orig_mode = stat.filemode(statorig.st_mode)
  1616. self.assertEqual(orig_mode[1:], found_mode[1:])
  1617. self.assertEqual(os.getuid(), statf.st_uid)
  1618. if os.getuid() == 0:
  1619. # The chown failed nonfatally if we're not root
  1620. self.assertEqual(0, statf.st_gid)
  1621. self.wipe()
  1622. # Test directory modes
  1623. testdir = os.path.join(self.common_test_dir, '66 install subdir')
  1624. self.init(testdir)
  1625. self.install()
  1626. f = os.path.join(self.installdir, 'usr', 'share', 'sub1')
  1627. statf = os.stat(f)
  1628. found_mode = stat.filemode(statf.st_mode)
  1629. want_mode = 'rwxr-x--t'
  1630. self.assertEqual(want_mode, found_mode[1:])
  1631. if os.getuid() == 0:
  1632. # The chown failed nonfatally if we're not root
  1633. self.assertEqual(0, statf.st_uid)
  1634. def test_cpp_std_override(self):
  1635. testdir = os.path.join(self.unit_test_dir, '6 std override')
  1636. self.init(testdir)
  1637. compdb = self.get_compdb()
  1638. for i in compdb:
  1639. if 'prog03' in i['file']:
  1640. c03_comp = i['command']
  1641. if 'prog11' in i['file']:
  1642. c11_comp = i['command']
  1643. if 'progp' in i['file']:
  1644. plain_comp = i['command']
  1645. self.assertNotEqual(len(plain_comp), 0)
  1646. self.assertIn('-std=c++03', c03_comp)
  1647. self.assertNotIn('-std=c++11', c03_comp)
  1648. self.assertIn('-std=c++11', c11_comp)
  1649. self.assertNotIn('-std=c++03', c11_comp)
  1650. self.assertNotIn('-std=c++03', plain_comp)
  1651. self.assertNotIn('-std=c++11', plain_comp)
  1652. # Now werror
  1653. self.assertIn('-Werror', plain_comp)
  1654. self.assertNotIn('-Werror', c03_comp)
  1655. def test_run_installed(self):
  1656. testdir = os.path.join(self.unit_test_dir, '7 run installed')
  1657. self.init(testdir)
  1658. self.build()
  1659. self.install()
  1660. installed_exe = os.path.join(self.installdir, 'usr/bin/prog')
  1661. installed_libdir = os.path.join(self.installdir, 'usr/foo')
  1662. installed_lib = os.path.join(installed_libdir, 'libfoo.so')
  1663. self.assertTrue(os.path.isfile(installed_exe))
  1664. self.assertTrue(os.path.isdir(installed_libdir))
  1665. self.assertTrue(os.path.isfile(installed_lib))
  1666. # Must fail when run without LD_LIBRARY_PATH to ensure that
  1667. # rpath has been properly stripped rather than pointing to the builddir.
  1668. self.assertNotEqual(subprocess.call(installed_exe, stderr=subprocess.DEVNULL), 0)
  1669. # When LD_LIBRARY_PATH is set it should start working.
  1670. # For some reason setting LD_LIBRARY_PATH in os.environ fails
  1671. # when all tests are run (but works when only this test is run),
  1672. # but doing this explicitly works.
  1673. env = os.environ.copy()
  1674. env['LD_LIBRARY_PATH'] = installed_libdir
  1675. self.assertEqual(subprocess.call(installed_exe, env=env), 0)
  1676. def test_order_of_l_arguments(self):
  1677. testdir = os.path.join(self.unit_test_dir, '9 -L -l order')
  1678. os.environ['PKG_CONFIG_PATH'] = testdir
  1679. self.init(testdir)
  1680. # NOTE: .pc file has -Lfoo -lfoo -Lbar -lbar but pkg-config reorders
  1681. # the flags before returning them to -Lfoo -Lbar -lfoo -lbar
  1682. # but pkgconf seems to not do that. Sigh. Support both.
  1683. expected_order = [('-L/me/first', '-lfoo1'),
  1684. ('-L/me/second', '-lfoo2'),
  1685. ('-L/me/first', '-L/me/second'),
  1686. ('-lfoo1', '-lfoo2'),
  1687. ('-L/me/second', '-L/me/third'),
  1688. ('-L/me/third', '-L/me/fourth',),
  1689. ('-L/me/third', '-lfoo3'),
  1690. ('-L/me/fourth', '-lfoo4'),
  1691. ('-lfoo3', '-lfoo4'),
  1692. ]
  1693. with open(os.path.join(self.builddir, 'build.ninja')) as ifile:
  1694. for line in ifile:
  1695. if expected_order[0][0] in line:
  1696. for first, second in expected_order:
  1697. self.assertLess(line.index(first), line.index(second))
  1698. return
  1699. raise RuntimeError('Linker entries not found in the Ninja file.')
  1700. def test_introspect_dependencies(self):
  1701. '''
  1702. Tests that mesonintrospect --dependencies returns expected output.
  1703. '''
  1704. testdir = os.path.join(self.framework_test_dir, '7 gnome')
  1705. self.init(testdir)
  1706. glib_found = False
  1707. gobject_found = False
  1708. deps = self.introspect('--dependencies')
  1709. self.assertIsInstance(deps, list)
  1710. for dep in deps:
  1711. self.assertIsInstance(dep, dict)
  1712. self.assertIn('name', dep)
  1713. self.assertIn('compile_args', dep)
  1714. self.assertIn('link_args', dep)
  1715. if dep['name'] == 'glib-2.0':
  1716. glib_found = True
  1717. elif dep['name'] == 'gobject-2.0':
  1718. gobject_found = True
  1719. self.assertTrue(glib_found)
  1720. self.assertTrue(gobject_found)
  1721. def test_build_rpath(self):
  1722. testdir = os.path.join(self.unit_test_dir, '11 build_rpath')
  1723. self.init(testdir)
  1724. self.build()
  1725. build_rpath = get_rpath(os.path.join(self.builddir, 'prog'))
  1726. self.assertEqual(build_rpath, '$ORIGIN/sub:/foo/bar')
  1727. self.install()
  1728. install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/prog'))
  1729. self.assertEqual(install_rpath, '/baz')
  1730. def test_pch_with_address_sanitizer(self):
  1731. testdir = os.path.join(self.common_test_dir, '13 pch')
  1732. self.init(testdir, ['-Db_sanitize=address'])
  1733. self.build()
  1734. compdb = self.get_compdb()
  1735. for i in compdb:
  1736. self.assertIn("-fsanitize=address", i["command"])
  1737. class LinuxArmCrossCompileTests(BasePlatformTests):
  1738. '''
  1739. Tests that verify cross-compilation to Linux/ARM
  1740. '''
  1741. def setUp(self):
  1742. super().setUp()
  1743. src_root = os.path.dirname(__file__)
  1744. self.meson_command += ['--cross=' + os.path.join(src_root, 'cross', 'ubuntu-armhf.txt')]
  1745. def test_cflags_cross_environment_pollution(self):
  1746. '''
  1747. Test that the CFLAGS environment variable does not pollute the cross
  1748. environment. This can't be an ordinary test case because we need to
  1749. inspect the compiler database.
  1750. '''
  1751. testdir = os.path.join(self.common_test_dir, '3 static')
  1752. os.environ['CFLAGS'] = '-DBUILD_ENVIRONMENT_ONLY'
  1753. self.init(testdir)
  1754. compdb = self.get_compdb()
  1755. self.assertNotIn('-DBUILD_ENVIRONMENT_ONLY', compdb[0]['command'])
  1756. class RewriterTests(unittest.TestCase):
  1757. def setUp(self):
  1758. super().setUp()
  1759. src_root = os.path.dirname(__file__)
  1760. self.testroot = os.path.realpath(tempfile.mkdtemp())
  1761. self.rewrite_command = [sys.executable, os.path.join(src_root, 'mesonrewriter.py')]
  1762. self.tmpdir = os.path.realpath(tempfile.mkdtemp())
  1763. self.workdir = os.path.join(self.tmpdir, 'foo')
  1764. self.test_dir = os.path.join(src_root, 'test cases/rewrite')
  1765. def tearDown(self):
  1766. windows_proof_rmtree(self.tmpdir)
  1767. def read_contents(self, fname):
  1768. with open(os.path.join(self.workdir, fname)) as f:
  1769. return f.read()
  1770. def check_effectively_same(self, mainfile, truth):
  1771. mf = self.read_contents(mainfile)
  1772. t = self.read_contents(truth)
  1773. # Rewriting is not guaranteed to do a perfect job of
  1774. # maintaining whitespace.
  1775. self.assertEqual(mf.replace(' ', ''), t.replace(' ', ''))
  1776. def prime(self, dirname):
  1777. shutil.copytree(os.path.join(self.test_dir, dirname), self.workdir)
  1778. def test_basic(self):
  1779. self.prime('1 basic')
  1780. subprocess.check_call(self.rewrite_command + ['remove',
  1781. '--target=trivialprog',
  1782. '--filename=notthere.c',
  1783. '--sourcedir', self.workdir],
  1784. universal_newlines=True)
  1785. self.check_effectively_same('meson.build', 'removed.txt')
  1786. subprocess.check_call(self.rewrite_command + ['add',
  1787. '--target=trivialprog',
  1788. '--filename=notthere.c',
  1789. '--sourcedir', self.workdir],
  1790. universal_newlines=True)
  1791. self.check_effectively_same('meson.build', 'added.txt')
  1792. subprocess.check_call(self.rewrite_command + ['remove',
  1793. '--target=trivialprog',
  1794. '--filename=notthere.c',
  1795. '--sourcedir', self.workdir],
  1796. universal_newlines=True)
  1797. self.check_effectively_same('meson.build', 'removed.txt')
  1798. def test_subdir(self):
  1799. self.prime('2 subdirs')
  1800. top = self.read_contents('meson.build')
  1801. s2 = self.read_contents('sub2/meson.build')
  1802. subprocess.check_call(self.rewrite_command + ['remove',
  1803. '--target=something',
  1804. '--filename=second.c',
  1805. '--sourcedir', self.workdir],
  1806. universal_newlines=True)
  1807. self.check_effectively_same('sub1/meson.build', 'sub1/after.txt')
  1808. self.assertEqual(top, self.read_contents('meson.build'))
  1809. self.assertEqual(s2, self.read_contents('sub2/meson.build'))
  1810. def unset_envs():
  1811. # For unit tests we must fully control all commend lines
  1812. # so that there are no unexpected changes coming from the
  1813. # environment, for example when doing a package build.
  1814. varnames = ['CPPFLAGS', 'LDFLAGS'] + list(mesonbuild.environment.cflags_mapping.values())
  1815. for v in varnames:
  1816. if v in os.environ:
  1817. del os.environ[v]
  1818. if __name__ == '__main__':
  1819. unset_envs()
  1820. unittest.main(buffer=True)