run_unittests.py 112 KB

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