run_unittests.py 163 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622
  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 unittest
  21. from unittest import mock
  22. from configparser import ConfigParser
  23. from glob import glob
  24. from pathlib import (PurePath, Path)
  25. import mesonbuild.mlog
  26. import mesonbuild.compilers
  27. import mesonbuild.environment
  28. import mesonbuild.mesonlib
  29. import mesonbuild.coredata
  30. import mesonbuild.modules.gnome
  31. from mesonbuild.interpreter import ObjectHolder
  32. from mesonbuild.mesonlib import (
  33. is_windows, is_osx, is_cygwin, is_dragonflybsd,
  34. windows_proof_rmtree, python_command, version_compare,
  35. BuildDirLock
  36. )
  37. from mesonbuild.environment import Environment, detect_ninja
  38. from mesonbuild.mesonlib import MesonException, EnvironmentException
  39. from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
  40. import mesonbuild.modules.pkgconfig
  41. from run_tests import exe_suffix, get_fake_options, get_meson_script
  42. from run_tests import get_builddir_target_args, get_backend_commands, Backend
  43. from run_tests import ensure_backend_detects_changes, run_configure_inprocess
  44. from run_tests import should_run_linux_cross_tests
  45. def get_dynamic_section_entry(fname, entry):
  46. if is_cygwin() or is_osx():
  47. raise unittest.SkipTest('Test only applicable to ELF platforms')
  48. try:
  49. raw_out = subprocess.check_output(['readelf', '-d', fname],
  50. universal_newlines=True)
  51. except FileNotFoundError:
  52. # FIXME: Try using depfixer.py:Elf() as a fallback
  53. raise unittest.SkipTest('readelf not found')
  54. pattern = re.compile(entry + r': \[(.*?)\]')
  55. for line in raw_out.split('\n'):
  56. m = pattern.search(line)
  57. if m is not None:
  58. return m.group(1)
  59. return None # The file did not contain the specified entry.
  60. def get_soname(fname):
  61. return get_dynamic_section_entry(fname, 'soname')
  62. def get_rpath(fname):
  63. return get_dynamic_section_entry(fname, r'(?:rpath|runpath)')
  64. def is_ci():
  65. if 'TRAVIS' in os.environ or 'APPVEYOR' in os.environ:
  66. return True
  67. return False
  68. def skipIfNoPkgconfig(f):
  69. '''
  70. Skip this test if no pkg-config is found, unless we're on Travis or
  71. Appveyor CI. This allows users to run our test suite without having
  72. pkg-config installed on, f.ex., macOS, while ensuring that our CI does not
  73. silently skip the test because of misconfiguration.
  74. Note: Yes, we provide pkg-config even while running Windows CI
  75. '''
  76. def wrapped(*args, **kwargs):
  77. if not is_ci() and shutil.which('pkg-config') is None:
  78. raise unittest.SkipTest('pkg-config not found')
  79. return f(*args, **kwargs)
  80. return wrapped
  81. class InternalTests(unittest.TestCase):
  82. def test_version_number(self):
  83. searchfunc = mesonbuild.environment.search_version
  84. self.assertEqual(searchfunc('foobar 1.2.3'), '1.2.3')
  85. self.assertEqual(searchfunc('1.2.3'), '1.2.3')
  86. self.assertEqual(searchfunc('foobar 2016.10.28 1.2.3'), '1.2.3')
  87. self.assertEqual(searchfunc('2016.10.28 1.2.3'), '1.2.3')
  88. self.assertEqual(searchfunc('foobar 2016.10.128'), 'unknown version')
  89. self.assertEqual(searchfunc('2016.10.128'), 'unknown version')
  90. def test_mode_symbolic_to_bits(self):
  91. modefunc = mesonbuild.mesonlib.FileMode.perms_s_to_bits
  92. self.assertEqual(modefunc('---------'), 0)
  93. self.assertEqual(modefunc('r--------'), stat.S_IRUSR)
  94. self.assertEqual(modefunc('---r-----'), stat.S_IRGRP)
  95. self.assertEqual(modefunc('------r--'), stat.S_IROTH)
  96. self.assertEqual(modefunc('-w-------'), stat.S_IWUSR)
  97. self.assertEqual(modefunc('----w----'), stat.S_IWGRP)
  98. self.assertEqual(modefunc('-------w-'), stat.S_IWOTH)
  99. self.assertEqual(modefunc('--x------'), stat.S_IXUSR)
  100. self.assertEqual(modefunc('-----x---'), stat.S_IXGRP)
  101. self.assertEqual(modefunc('--------x'), stat.S_IXOTH)
  102. self.assertEqual(modefunc('--S------'), stat.S_ISUID)
  103. self.assertEqual(modefunc('-----S---'), stat.S_ISGID)
  104. self.assertEqual(modefunc('--------T'), stat.S_ISVTX)
  105. self.assertEqual(modefunc('--s------'), stat.S_ISUID | stat.S_IXUSR)
  106. self.assertEqual(modefunc('-----s---'), stat.S_ISGID | stat.S_IXGRP)
  107. self.assertEqual(modefunc('--------t'), stat.S_ISVTX | stat.S_IXOTH)
  108. self.assertEqual(modefunc('rwx------'), stat.S_IRWXU)
  109. self.assertEqual(modefunc('---rwx---'), stat.S_IRWXG)
  110. self.assertEqual(modefunc('------rwx'), stat.S_IRWXO)
  111. # We could keep listing combinations exhaustively but that seems
  112. # tedious and pointless. Just test a few more.
  113. self.assertEqual(modefunc('rwxr-xr-x'),
  114. stat.S_IRWXU |
  115. stat.S_IRGRP | stat.S_IXGRP |
  116. stat.S_IROTH | stat.S_IXOTH)
  117. self.assertEqual(modefunc('rw-r--r--'),
  118. stat.S_IRUSR | stat.S_IWUSR |
  119. stat.S_IRGRP |
  120. stat.S_IROTH)
  121. self.assertEqual(modefunc('rwsr-x---'),
  122. stat.S_IRWXU | stat.S_ISUID |
  123. stat.S_IRGRP | stat.S_IXGRP)
  124. def test_compiler_args_class(self):
  125. cargsfunc = mesonbuild.compilers.CompilerArgs
  126. c = mesonbuild.compilers.CCompiler([], 'fake', False)
  127. # Test that bad initialization fails
  128. self.assertRaises(TypeError, cargsfunc, [])
  129. self.assertRaises(TypeError, cargsfunc, [], [])
  130. self.assertRaises(TypeError, cargsfunc, c, [], [])
  131. # Test that empty initialization works
  132. a = cargsfunc(c)
  133. self.assertEqual(a, [])
  134. # Test that list initialization works
  135. a = cargsfunc(['-I.', '-I..'], c)
  136. self.assertEqual(a, ['-I.', '-I..'])
  137. # Test that there is no de-dup on initialization
  138. self.assertEqual(cargsfunc(['-I.', '-I.'], c), ['-I.', '-I.'])
  139. ## Test that appending works
  140. a.append('-I..')
  141. self.assertEqual(a, ['-I..', '-I.'])
  142. a.append('-O3')
  143. self.assertEqual(a, ['-I..', '-I.', '-O3'])
  144. ## Test that in-place addition works
  145. a += ['-O2', '-O2']
  146. self.assertEqual(a, ['-I..', '-I.', '-O3', '-O2', '-O2'])
  147. # Test that removal works
  148. a.remove('-O2')
  149. self.assertEqual(a, ['-I..', '-I.', '-O3', '-O2'])
  150. # Test that de-dup happens on addition
  151. a += ['-Ifoo', '-Ifoo']
  152. self.assertEqual(a, ['-Ifoo', '-I..', '-I.', '-O3', '-O2'])
  153. # .extend() is just +=, so we don't test it
  154. ## Test that addition works
  155. # Test that adding a list with just one old arg works and yields the same array
  156. a = a + ['-Ifoo']
  157. self.assertEqual(a, ['-Ifoo', '-I..', '-I.', '-O3', '-O2'])
  158. # Test that adding a list with one arg new and one old works
  159. a = a + ['-Ifoo', '-Ibaz']
  160. self.assertEqual(a, ['-Ifoo', '-Ibaz', '-I..', '-I.', '-O3', '-O2'])
  161. # Test that adding args that must be prepended and appended works
  162. a = a + ['-Ibar', '-Wall']
  163. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-O3', '-O2', '-Wall'])
  164. ## Test that reflected addition works
  165. # Test that adding to a list with just one old arg works and yields the same array
  166. a = ['-Ifoo'] + a
  167. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-O3', '-O2', '-Wall'])
  168. # Test that adding to a list with just one new arg that is not pre-pended works
  169. a = ['-Werror'] + a
  170. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Werror', '-O3', '-O2', '-Wall'])
  171. # Test that adding to a list with two new args preserves the order
  172. a = ['-Ldir', '-Lbah'] + a
  173. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Ldir', '-Lbah', '-Werror', '-O3', '-O2', '-Wall'])
  174. # Test that adding to a list with old args does nothing
  175. a = ['-Ibar', '-Ibaz', '-Ifoo'] + a
  176. self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Ldir', '-Lbah', '-Werror', '-O3', '-O2', '-Wall'])
  177. ## Test that adding libraries works
  178. l = cargsfunc(c, ['-Lfoodir', '-lfoo'])
  179. self.assertEqual(l, ['-Lfoodir', '-lfoo'])
  180. # Adding a library and a libpath appends both correctly
  181. l += ['-Lbardir', '-lbar']
  182. self.assertEqual(l, ['-Lbardir', '-Lfoodir', '-lfoo', '-lbar'])
  183. # Adding the same library again does nothing
  184. l += ['-lbar']
  185. self.assertEqual(l, ['-Lbardir', '-Lfoodir', '-lfoo', '-lbar'])
  186. ## Test that 'direct' append and extend works
  187. l = cargsfunc(c, ['-Lfoodir', '-lfoo'])
  188. self.assertEqual(l, ['-Lfoodir', '-lfoo'])
  189. # Direct-adding a library and a libpath appends both correctly
  190. l.extend_direct(['-Lbardir', '-lbar'])
  191. self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar'])
  192. # Direct-adding the same library again still adds it
  193. l.append_direct('-lbar')
  194. self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar'])
  195. # Direct-adding with absolute path deduplicates
  196. l.append_direct('/libbaz.a')
  197. self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a'])
  198. # Adding libbaz again does nothing
  199. l.append_direct('/libbaz.a')
  200. self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a'])
  201. def test_string_templates_substitution(self):
  202. dictfunc = mesonbuild.mesonlib.get_filenames_templates_dict
  203. substfunc = mesonbuild.mesonlib.substitute_values
  204. ME = mesonbuild.mesonlib.MesonException
  205. # Identity
  206. self.assertEqual(dictfunc([], []), {})
  207. # One input, no outputs
  208. inputs = ['bar/foo.c.in']
  209. outputs = []
  210. ret = dictfunc(inputs, outputs)
  211. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
  212. '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c'}
  213. # Check dictionary
  214. self.assertEqual(ret, d)
  215. # Check substitutions
  216. cmd = ['some', 'ordinary', 'strings']
  217. self.assertEqual(substfunc(cmd, d), cmd)
  218. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  219. self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out'] + cmd[1:])
  220. cmd = ['@INPUT0@.out', '@PLAINNAME@.ok', 'strings']
  221. self.assertEqual(substfunc(cmd, d),
  222. [inputs[0] + '.out'] + [d['@PLAINNAME@'] + '.ok'] + cmd[2:])
  223. cmd = ['@INPUT@', '@BASENAME@.hah', 'strings']
  224. self.assertEqual(substfunc(cmd, d),
  225. inputs + [d['@BASENAME@'] + '.hah'] + cmd[2:])
  226. cmd = ['@OUTPUT@']
  227. self.assertRaises(ME, substfunc, cmd, d)
  228. # One input, one output
  229. inputs = ['bar/foo.c.in']
  230. outputs = ['out.c']
  231. ret = dictfunc(inputs, outputs)
  232. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
  233. '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
  234. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': '.'}
  235. # Check dictionary
  236. self.assertEqual(ret, d)
  237. # Check substitutions
  238. cmd = ['some', 'ordinary', 'strings']
  239. self.assertEqual(substfunc(cmd, d), cmd)
  240. cmd = ['@INPUT@.out', '@OUTPUT@', 'strings']
  241. self.assertEqual(substfunc(cmd, d),
  242. [inputs[0] + '.out'] + outputs + cmd[2:])
  243. cmd = ['@INPUT0@.out', '@PLAINNAME@.ok', '@OUTPUT0@']
  244. self.assertEqual(substfunc(cmd, d),
  245. [inputs[0] + '.out', d['@PLAINNAME@'] + '.ok'] + outputs)
  246. cmd = ['@INPUT@', '@BASENAME@.hah', 'strings']
  247. self.assertEqual(substfunc(cmd, d),
  248. inputs + [d['@BASENAME@'] + '.hah'] + cmd[2:])
  249. # One input, one output with a subdir
  250. outputs = ['dir/out.c']
  251. ret = dictfunc(inputs, outputs)
  252. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
  253. '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
  254. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
  255. # Check dictionary
  256. self.assertEqual(ret, d)
  257. # Two inputs, no outputs
  258. inputs = ['bar/foo.c.in', 'baz/foo.c.in']
  259. outputs = []
  260. ret = dictfunc(inputs, outputs)
  261. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1]}
  262. # Check dictionary
  263. self.assertEqual(ret, d)
  264. # Check substitutions
  265. cmd = ['some', 'ordinary', 'strings']
  266. self.assertEqual(substfunc(cmd, d), cmd)
  267. cmd = ['@INPUT@', 'ordinary', 'strings']
  268. self.assertEqual(substfunc(cmd, d), inputs + cmd[1:])
  269. cmd = ['@INPUT0@.out', 'ordinary', 'strings']
  270. self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out'] + cmd[1:])
  271. cmd = ['@INPUT0@.out', '@INPUT1@.ok', 'strings']
  272. self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out', inputs[1] + '.ok'] + cmd[2:])
  273. cmd = ['@INPUT0@', '@INPUT1@', 'strings']
  274. self.assertEqual(substfunc(cmd, d), inputs + cmd[2:])
  275. # Many inputs, can't use @INPUT@ like this
  276. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  277. self.assertRaises(ME, substfunc, cmd, d)
  278. # Not enough inputs
  279. cmd = ['@INPUT2@.out', 'ordinary', 'strings']
  280. self.assertRaises(ME, substfunc, cmd, d)
  281. # Too many inputs
  282. cmd = ['@PLAINNAME@']
  283. self.assertRaises(ME, substfunc, cmd, d)
  284. cmd = ['@BASENAME@']
  285. self.assertRaises(ME, substfunc, cmd, d)
  286. # No outputs
  287. cmd = ['@OUTPUT@']
  288. self.assertRaises(ME, substfunc, cmd, d)
  289. cmd = ['@OUTPUT0@']
  290. self.assertRaises(ME, substfunc, cmd, d)
  291. cmd = ['@OUTDIR@']
  292. self.assertRaises(ME, substfunc, cmd, d)
  293. # Two inputs, one output
  294. outputs = ['dir/out.c']
  295. ret = dictfunc(inputs, outputs)
  296. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
  297. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
  298. # Check dictionary
  299. self.assertEqual(ret, d)
  300. # Check substitutions
  301. cmd = ['some', 'ordinary', 'strings']
  302. self.assertEqual(substfunc(cmd, d), cmd)
  303. cmd = ['@OUTPUT@', 'ordinary', 'strings']
  304. self.assertEqual(substfunc(cmd, d), outputs + cmd[1:])
  305. cmd = ['@OUTPUT@.out', 'ordinary', 'strings']
  306. self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out'] + cmd[1:])
  307. cmd = ['@OUTPUT0@.out', '@INPUT1@.ok', 'strings']
  308. self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok'] + cmd[2:])
  309. # Many inputs, can't use @INPUT@ like this
  310. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  311. self.assertRaises(ME, substfunc, cmd, d)
  312. # Not enough inputs
  313. cmd = ['@INPUT2@.out', 'ordinary', 'strings']
  314. self.assertRaises(ME, substfunc, cmd, d)
  315. # Not enough outputs
  316. cmd = ['@OUTPUT2@.out', 'ordinary', 'strings']
  317. self.assertRaises(ME, substfunc, cmd, d)
  318. # Two inputs, two outputs
  319. outputs = ['dir/out.c', 'dir/out2.c']
  320. ret = dictfunc(inputs, outputs)
  321. d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
  322. '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTPUT1@': outputs[1],
  323. '@OUTDIR@': 'dir'}
  324. # Check dictionary
  325. self.assertEqual(ret, d)
  326. # Check substitutions
  327. cmd = ['some', 'ordinary', 'strings']
  328. self.assertEqual(substfunc(cmd, d), cmd)
  329. cmd = ['@OUTPUT@', 'ordinary', 'strings']
  330. self.assertEqual(substfunc(cmd, d), outputs + cmd[1:])
  331. cmd = ['@OUTPUT0@', '@OUTPUT1@', 'strings']
  332. self.assertEqual(substfunc(cmd, d), outputs + cmd[2:])
  333. cmd = ['@OUTPUT0@.out', '@INPUT1@.ok', '@OUTDIR@']
  334. self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok', 'dir'])
  335. # Many inputs, can't use @INPUT@ like this
  336. cmd = ['@INPUT@.out', 'ordinary', 'strings']
  337. self.assertRaises(ME, substfunc, cmd, d)
  338. # Not enough inputs
  339. cmd = ['@INPUT2@.out', 'ordinary', 'strings']
  340. self.assertRaises(ME, substfunc, cmd, d)
  341. # Not enough outputs
  342. cmd = ['@OUTPUT2@.out', 'ordinary', 'strings']
  343. self.assertRaises(ME, substfunc, cmd, d)
  344. # Many outputs, can't use @OUTPUT@ like this
  345. cmd = ['@OUTPUT@.out', 'ordinary', 'strings']
  346. self.assertRaises(ME, substfunc, cmd, d)
  347. def test_needs_exe_wrapper_override(self):
  348. config = ConfigParser()
  349. config['binaries'] = {
  350. 'c': '\'/usr/bin/gcc\'',
  351. }
  352. config['host_machine'] = {
  353. 'system': '\'linux\'',
  354. 'cpu_family': '\'arm\'',
  355. 'cpu': '\'armv7\'',
  356. 'endian': '\'little\'',
  357. }
  358. # Can not be used as context manager because we need to
  359. # open it a second time and this is not possible on
  360. # Windows.
  361. configfile = tempfile.NamedTemporaryFile(mode='w+', delete=False)
  362. configfilename = configfile.name
  363. config.write(configfile)
  364. configfile.flush()
  365. configfile.close()
  366. detected_value = mesonbuild.environment.CrossBuildInfo(configfile.name).need_exe_wrapper()
  367. os.unlink(configfilename)
  368. desired_value = not detected_value
  369. config['properties'] = {
  370. 'needs_exe_wrapper': 'true' if desired_value else 'false'
  371. }
  372. configfile = tempfile.NamedTemporaryFile(mode='w+', delete=False)
  373. configfilename = configfile.name
  374. config.write(configfile)
  375. configfile.close()
  376. forced_value = mesonbuild.environment.CrossBuildInfo(configfile.name).need_exe_wrapper()
  377. os.unlink(configfilename)
  378. self.assertEqual(forced_value, desired_value)
  379. def test_listify(self):
  380. listify = mesonbuild.mesonlib.listify
  381. # Test sanity
  382. self.assertEqual([1], listify(1))
  383. self.assertEqual([], listify([]))
  384. self.assertEqual([1], listify([1]))
  385. # Test flattening
  386. self.assertEqual([1, 2, 3], listify([1, [2, 3]]))
  387. self.assertEqual([1, 2, 3], listify([1, [2, [3]]]))
  388. self.assertEqual([1, [2, [3]]], listify([1, [2, [3]]], flatten=False))
  389. # Test flattening and unholdering
  390. holder1 = ObjectHolder(1)
  391. holder3 = ObjectHolder(3)
  392. self.assertEqual([holder1], listify(holder1))
  393. self.assertEqual([holder1], listify([holder1]))
  394. self.assertEqual([holder1, 2], listify([holder1, 2]))
  395. self.assertEqual([holder1, 2, 3], listify([holder1, 2, [3]]))
  396. self.assertEqual([1], listify(holder1, unholder=True))
  397. self.assertEqual([1], listify([holder1], unholder=True))
  398. self.assertEqual([1, 2], listify([holder1, 2], unholder=True))
  399. self.assertEqual([1, 2, 3], listify([holder1, 2, [holder3]], unholder=True))
  400. # Unholding doesn't work recursively when not flattening
  401. self.assertEqual([1, [2], [holder3]], listify([holder1, [2], [holder3]], unholder=True, flatten=False))
  402. def test_extract_as_list(self):
  403. extract = mesonbuild.mesonlib.extract_as_list
  404. # Test sanity
  405. kwargs = {'sources': [1, 2, 3]}
  406. self.assertEqual([1, 2, 3], extract(kwargs, 'sources'))
  407. self.assertEqual(kwargs, {'sources': [1, 2, 3]})
  408. self.assertEqual([1, 2, 3], extract(kwargs, 'sources', pop=True))
  409. self.assertEqual(kwargs, {})
  410. # Test unholding
  411. holder3 = ObjectHolder(3)
  412. kwargs = {'sources': [1, 2, holder3]}
  413. self.assertEqual([1, 2, 3], extract(kwargs, 'sources', unholder=True))
  414. self.assertEqual(kwargs, {'sources': [1, 2, holder3]})
  415. self.assertEqual([1, 2, 3], extract(kwargs, 'sources', unholder=True, pop=True))
  416. self.assertEqual(kwargs, {})
  417. # Test listification
  418. kwargs = {'sources': [1, 2, 3], 'pch_sources': [4, 5, 6]}
  419. self.assertEqual([[1, 2, 3], [4, 5, 6]], extract(kwargs, 'sources', 'pch_sources'))
  420. @unittest.skipIf(not os.path.isdir('docs'), 'Doc dir not found, presumably because this is a tarball release.')
  421. def test_snippets(self):
  422. hashcounter = re.compile('^ *(#)+')
  423. snippet_dir = Path('docs/markdown/snippets')
  424. self.assertTrue(snippet_dir.is_dir())
  425. for f in snippet_dir.glob('*'):
  426. self.assertTrue(f.is_file())
  427. if f.suffix == '.md':
  428. with f.open() as snippet:
  429. for line in snippet:
  430. m = re.match(hashcounter, line)
  431. if m:
  432. self.assertEqual(len(m.group(0)), 2, 'All headings in snippets must have two hash symbols: ' + f.name)
  433. else:
  434. if f.name != 'add_release_note_snippets_here':
  435. self.assertTrue(False, 'A file without .md suffix in snippets dir: ' + f.name)
  436. def test_pkgconfig_module(self):
  437. class Mock:
  438. pass
  439. mock = Mock()
  440. mock.pcdep = Mock()
  441. mock.pcdep.name = "some_name"
  442. mock.version_reqs = []
  443. # pkgconfig dependency as lib
  444. deps = mesonbuild.modules.pkgconfig.DependenciesHelper("thislib")
  445. deps.add_pub_libs([mock])
  446. self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name")
  447. # pkgconfig dependency as requires
  448. deps = mesonbuild.modules.pkgconfig.DependenciesHelper("thislib")
  449. deps.add_pub_reqs([mock])
  450. self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name")
  451. class BasePlatformTests(unittest.TestCase):
  452. def setUp(self):
  453. super().setUp()
  454. src_root = os.path.dirname(__file__)
  455. src_root = os.path.join(os.getcwd(), src_root)
  456. self.src_root = src_root
  457. self.prefix = '/usr'
  458. self.libdir = 'lib'
  459. # Get the backend
  460. # FIXME: Extract this from argv?
  461. self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja'))
  462. self.meson_args = ['--backend=' + self.backend.name]
  463. self.meson_cross_file = None
  464. self.meson_command = python_command + [get_meson_script()]
  465. self.setup_command = self.meson_command + self.meson_args
  466. self.mconf_command = self.meson_command + ['configure']
  467. self.mintro_command = self.meson_command + ['introspect']
  468. self.wrap_command = self.meson_command + ['wrap']
  469. # Backend-specific build commands
  470. self.build_command, self.clean_command, self.test_command, self.install_command, \
  471. self.uninstall_command = get_backend_commands(self.backend)
  472. # Test directories
  473. self.common_test_dir = os.path.join(src_root, 'test cases/common')
  474. self.vala_test_dir = os.path.join(src_root, 'test cases/vala')
  475. self.framework_test_dir = os.path.join(src_root, 'test cases/frameworks')
  476. self.unit_test_dir = os.path.join(src_root, 'test cases/unit')
  477. # Misc stuff
  478. self.orig_env = os.environ.copy()
  479. if self.backend is Backend.ninja:
  480. self.no_rebuild_stdout = 'ninja: no work to do.'
  481. else:
  482. # VS doesn't have a stable output when no changes are done
  483. # XCode backend is untested with unit tests, help welcome!
  484. self.no_rebuild_stdout = 'UNKNOWN BACKEND {!r}'.format(self.backend.name)
  485. self.builddirs = []
  486. self.new_builddir()
  487. def change_builddir(self, newdir):
  488. self.builddir = newdir
  489. self.privatedir = os.path.join(self.builddir, 'meson-private')
  490. self.logdir = os.path.join(self.builddir, 'meson-logs')
  491. self.installdir = os.path.join(self.builddir, 'install')
  492. self.distdir = os.path.join(self.builddir, 'meson-dist')
  493. self.mtest_command = self.meson_command + ['test', '-C', self.builddir]
  494. self.builddirs.append(self.builddir)
  495. def new_builddir(self):
  496. # In case the directory is inside a symlinked directory, find the real
  497. # path otherwise we might not find the srcdir from inside the builddir.
  498. newdir = os.path.realpath(tempfile.mkdtemp())
  499. self.change_builddir(newdir)
  500. def _print_meson_log(self):
  501. log = os.path.join(self.logdir, 'meson-log.txt')
  502. if not os.path.isfile(log):
  503. print("{!r} doesn't exist".format(log))
  504. return
  505. with open(log, 'r', encoding='utf-8') as f:
  506. print(f.read())
  507. def tearDown(self):
  508. for path in self.builddirs:
  509. try:
  510. windows_proof_rmtree(path)
  511. except FileNotFoundError:
  512. pass
  513. os.environ.clear()
  514. os.environ.update(self.orig_env)
  515. super().tearDown()
  516. def _run(self, command, workdir=None):
  517. '''
  518. Run a command while printing the stdout and stderr to stdout,
  519. and also return a copy of it
  520. '''
  521. # If this call hangs CI will just abort. It is very hard to distinguish
  522. # between CI issue and test bug in that case. Set timeout and fail loud
  523. # instead.
  524. p = subprocess.run(command, stdout=subprocess.PIPE,
  525. stderr=subprocess.STDOUT, env=os.environ.copy(),
  526. universal_newlines=True, cwd=workdir, timeout=60 * 5)
  527. print(p.stdout)
  528. if p.returncode != 0:
  529. if 'MESON_SKIP_TEST' in p.stdout:
  530. raise unittest.SkipTest('Project requested skipping.')
  531. raise subprocess.CalledProcessError(p.returncode, command, output=p.stdout)
  532. return p.stdout
  533. def init(self, srcdir, extra_args=None, default_args=True, inprocess=False):
  534. self.assertPathExists(srcdir)
  535. if extra_args is None:
  536. extra_args = []
  537. if not isinstance(extra_args, list):
  538. extra_args = [extra_args]
  539. args = [srcdir, self.builddir]
  540. if default_args:
  541. args += ['--prefix', self.prefix,
  542. '--libdir', self.libdir]
  543. if self.meson_cross_file:
  544. args += ['--cross-file', self.meson_cross_file]
  545. self.privatedir = os.path.join(self.builddir, 'meson-private')
  546. if inprocess:
  547. try:
  548. (returncode, out, err) = run_configure_inprocess(self.meson_args + args + extra_args)
  549. if 'MESON_SKIP_TEST' in out:
  550. raise unittest.SkipTest('Project requested skipping.')
  551. if returncode != 0:
  552. self._print_meson_log()
  553. print('Stdout:\n')
  554. print(out)
  555. print('Stderr:\n')
  556. print(err)
  557. raise RuntimeError('Configure failed')
  558. except:
  559. self._print_meson_log()
  560. raise
  561. finally:
  562. # Close log file to satisfy Windows file locking
  563. mesonbuild.mlog.shutdown()
  564. mesonbuild.mlog.log_dir = None
  565. mesonbuild.mlog.log_file = None
  566. else:
  567. try:
  568. out = self._run(self.setup_command + args + extra_args)
  569. except unittest.SkipTest:
  570. raise unittest.SkipTest('Project requested skipping: ' + srcdir)
  571. except:
  572. self._print_meson_log()
  573. raise
  574. return out
  575. def build(self, target=None, extra_args=None):
  576. if extra_args is None:
  577. extra_args = []
  578. # Add arguments for building the target (if specified),
  579. # and using the build dir (if required, with VS)
  580. args = get_builddir_target_args(self.backend, self.builddir, target)
  581. return self._run(self.build_command + args + extra_args, workdir=self.builddir)
  582. def clean(self):
  583. dir_args = get_builddir_target_args(self.backend, self.builddir, None)
  584. self._run(self.clean_command + dir_args, workdir=self.builddir)
  585. def run_tests(self):
  586. self._run(self.test_command, workdir=self.builddir)
  587. def install(self, *, use_destdir=True):
  588. if self.backend is not Backend.ninja:
  589. raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name))
  590. if use_destdir:
  591. os.environ['DESTDIR'] = self.installdir
  592. self._run(self.install_command, workdir=self.builddir)
  593. def uninstall(self):
  594. self._run(self.uninstall_command, workdir=self.builddir)
  595. def run_target(self, target):
  596. '''
  597. Run a Ninja target while printing the stdout and stderr to stdout,
  598. and also return a copy of it
  599. '''
  600. return self.build(target=target)
  601. def setconf(self, arg, will_build=True):
  602. if not isinstance(arg, list):
  603. arg = [arg]
  604. if will_build:
  605. ensure_backend_detects_changes(self.backend)
  606. self._run(self.mconf_command + arg + [self.builddir])
  607. def wipe(self):
  608. windows_proof_rmtree(self.builddir)
  609. def utime(self, f):
  610. ensure_backend_detects_changes(self.backend)
  611. os.utime(f)
  612. def get_compdb(self):
  613. if self.backend is not Backend.ninja:
  614. raise unittest.SkipTest('Compiler db not available with {} backend'.format(self.backend.name))
  615. with open(os.path.join(self.builddir, 'compile_commands.json')) as ifile:
  616. contents = json.load(ifile)
  617. # If Ninja is using .rsp files, generate them, read their contents, and
  618. # replace it as the command for all compile commands in the parsed json.
  619. if len(contents) > 0 and contents[0]['command'].endswith('.rsp'):
  620. # Pretend to build so that the rsp files are generated
  621. self.build(extra_args=['-d', 'keeprsp', '-n'])
  622. for each in contents:
  623. # Extract the actual command from the rsp file
  624. compiler, rsp = each['command'].split(' @')
  625. rsp = os.path.join(self.builddir, rsp)
  626. # Replace the command with its contents
  627. with open(rsp, 'r', encoding='utf-8') as f:
  628. each['command'] = compiler + ' ' + f.read()
  629. return contents
  630. def get_meson_log(self):
  631. with open(os.path.join(self.builddir, 'meson-logs', 'meson-log.txt')) as f:
  632. return f.readlines()
  633. def get_meson_log_compiler_checks(self):
  634. '''
  635. Fetch a list command-lines run by meson for compiler checks.
  636. Each command-line is returned as a list of arguments.
  637. '''
  638. log = self.get_meson_log()
  639. prefix = 'Command line:'
  640. cmds = [l[len(prefix):].split() for l in log if l.startswith(prefix)]
  641. return cmds
  642. def introspect(self, args):
  643. if isinstance(args, str):
  644. args = [args]
  645. out = subprocess.check_output(self.mintro_command + args + [self.builddir],
  646. universal_newlines=True)
  647. return json.loads(out)
  648. def assertPathEqual(self, path1, path2):
  649. '''
  650. Handles a lot of platform-specific quirks related to paths such as
  651. separator, case-sensitivity, etc.
  652. '''
  653. self.assertEqual(PurePath(path1), PurePath(path2))
  654. def assertPathBasenameEqual(self, path, basename):
  655. msg = '{!r} does not end with {!r}'.format(path, basename)
  656. # We cannot use os.path.basename because it returns '' when the path
  657. # ends with '/' for some silly reason. This is not how the UNIX utility
  658. # `basename` works.
  659. path_basename = PurePath(path).parts[-1]
  660. self.assertEqual(PurePath(path_basename), PurePath(basename), msg)
  661. def assertBuildIsNoop(self):
  662. ret = self.build()
  663. if self.backend is Backend.ninja:
  664. self.assertEqual(ret.split('\n')[-2], self.no_rebuild_stdout)
  665. elif self.backend is Backend.vs:
  666. # Ensure that some target said that no rebuild was done
  667. self.assertIn('CustomBuild:\n All outputs are up-to-date.', ret)
  668. self.assertIn('ClCompile:\n All outputs are up-to-date.', ret)
  669. self.assertIn('Link:\n All outputs are up-to-date.', ret)
  670. # Ensure that no targets were built
  671. clre = re.compile('ClCompile:\n [^\n]*cl', flags=re.IGNORECASE)
  672. linkre = re.compile('Link:\n [^\n]*link', flags=re.IGNORECASE)
  673. self.assertNotRegex(ret, clre)
  674. self.assertNotRegex(ret, linkre)
  675. elif self.backend is Backend.xcode:
  676. raise unittest.SkipTest('Please help us fix this test on the xcode backend')
  677. else:
  678. raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name))
  679. def assertRebuiltTarget(self, target):
  680. ret = self.build()
  681. if self.backend is Backend.ninja:
  682. self.assertIn('Linking target {}'.format(target), ret)
  683. elif self.backend is Backend.vs:
  684. # Ensure that this target was rebuilt
  685. linkre = re.compile('Link:\n [^\n]*link[^\n]*' + target, flags=re.IGNORECASE)
  686. self.assertRegex(ret, linkre)
  687. elif self.backend is Backend.xcode:
  688. raise unittest.SkipTest('Please help us fix this test on the xcode backend')
  689. else:
  690. raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name))
  691. def assertPathExists(self, path):
  692. m = 'Path {!r} should exist'.format(path)
  693. self.assertTrue(os.path.exists(path), msg=m)
  694. def assertPathDoesNotExist(self, path):
  695. m = 'Path {!r} should not exist'.format(path)
  696. self.assertFalse(os.path.exists(path), msg=m)
  697. class AllPlatformTests(BasePlatformTests):
  698. '''
  699. Tests that should run on all platforms
  700. '''
  701. def test_default_options_prefix(self):
  702. '''
  703. Tests that setting a prefix in default_options in project() works.
  704. Can't be an ordinary test because we pass --prefix to meson there.
  705. https://github.com/mesonbuild/meson/issues/1349
  706. '''
  707. testdir = os.path.join(self.common_test_dir, '94 default options')
  708. self.init(testdir, default_args=False)
  709. opts = self.introspect('--buildoptions')
  710. for opt in opts:
  711. if opt['name'] == 'prefix':
  712. prefix = opt['value']
  713. self.assertEqual(prefix, '/absoluteprefix')
  714. def test_absolute_prefix_libdir(self):
  715. '''
  716. Tests that setting absolute paths for --prefix and --libdir work. Can't
  717. be an ordinary test because these are set via the command-line.
  718. https://github.com/mesonbuild/meson/issues/1341
  719. https://github.com/mesonbuild/meson/issues/1345
  720. '''
  721. testdir = os.path.join(self.common_test_dir, '94 default options')
  722. prefix = '/someabs'
  723. libdir = 'libdir'
  724. extra_args = ['--prefix=' + prefix,
  725. # This can just be a relative path, but we want to test
  726. # that passing this as an absolute path also works
  727. '--libdir=' + prefix + '/' + libdir]
  728. self.init(testdir, extra_args, default_args=False)
  729. opts = self.introspect('--buildoptions')
  730. for opt in opts:
  731. if opt['name'] == 'prefix':
  732. self.assertEqual(prefix, opt['value'])
  733. elif opt['name'] == 'libdir':
  734. self.assertEqual(libdir, opt['value'])
  735. def test_libdir_must_be_inside_prefix(self):
  736. '''
  737. Tests that libdir is forced to be inside prefix no matter how it is set.
  738. Must be a unit test for obvious reasons.
  739. '''
  740. testdir = os.path.join(self.common_test_dir, '1 trivial')
  741. # libdir being inside prefix is ok
  742. args = ['--prefix', '/opt', '--libdir', '/opt/lib32']
  743. self.init(testdir, args)
  744. self.wipe()
  745. # libdir not being inside prefix is not ok
  746. args = ['--prefix', '/usr', '--libdir', '/opt/lib32']
  747. self.assertRaises(subprocess.CalledProcessError, self.init, testdir, args)
  748. self.wipe()
  749. # libdir must be inside prefix even when set via mesonconf
  750. self.init(testdir)
  751. self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt', False)
  752. def test_prefix_dependent_defaults(self):
  753. '''
  754. Tests that configured directory paths are set to prefix dependent
  755. defaults.
  756. '''
  757. testdir = os.path.join(self.common_test_dir, '1 trivial')
  758. expected = {
  759. '/opt': {'prefix': '/opt',
  760. 'bindir': 'bin', 'datadir': 'share', 'includedir': 'include',
  761. 'infodir': 'share/info',
  762. 'libexecdir': 'libexec', 'localedir': 'share/locale',
  763. 'localstatedir': 'var', 'mandir': 'share/man',
  764. 'sbindir': 'sbin', 'sharedstatedir': 'com',
  765. 'sysconfdir': 'etc'},
  766. '/usr': {'prefix': '/usr',
  767. 'bindir': 'bin', 'datadir': 'share', 'includedir': 'include',
  768. 'infodir': 'share/info',
  769. 'libexecdir': 'libexec', 'localedir': 'share/locale',
  770. 'localstatedir': '/var', 'mandir': 'share/man',
  771. 'sbindir': 'sbin', 'sharedstatedir': '/var/lib',
  772. 'sysconfdir': '/etc'},
  773. '/usr/local': {'prefix': '/usr/local',
  774. 'bindir': 'bin', 'datadir': 'share',
  775. 'includedir': 'include', 'infodir': 'share/info',
  776. 'libexecdir': 'libexec',
  777. 'localedir': 'share/locale',
  778. 'localstatedir': '/var/local', 'mandir': 'share/man',
  779. 'sbindir': 'sbin', 'sharedstatedir': '/var/local/lib',
  780. 'sysconfdir': 'etc'},
  781. # N.B. We don't check 'libdir' as it's platform dependent, see
  782. # default_libdir():
  783. }
  784. for prefix in expected:
  785. args = ['--prefix', prefix]
  786. self.init(testdir, args, default_args=False)
  787. opts = self.introspect('--buildoptions')
  788. for opt in opts:
  789. name = opt['name']
  790. value = opt['value']
  791. if name in expected[prefix]:
  792. self.assertEqual(value, expected[prefix][name])
  793. self.wipe()
  794. def test_default_options_prefix_dependent_defaults(self):
  795. '''
  796. Tests that setting a prefix in default_options in project() sets prefix
  797. dependent defaults for other options, and that those defaults can
  798. be overridden in default_options or by the command line.
  799. '''
  800. testdir = os.path.join(self.common_test_dir, '173 default options prefix dependent defaults')
  801. expected = {
  802. '':
  803. {'prefix': '/usr',
  804. 'sysconfdir': '/etc',
  805. 'localstatedir': '/var',
  806. 'sharedstatedir': '/sharedstate'},
  807. '--prefix=/usr':
  808. {'prefix': '/usr',
  809. 'sysconfdir': '/etc',
  810. 'localstatedir': '/var',
  811. 'sharedstatedir': '/sharedstate'},
  812. '--sharedstatedir=/var/state':
  813. {'prefix': '/usr',
  814. 'sysconfdir': '/etc',
  815. 'localstatedir': '/var',
  816. 'sharedstatedir': '/var/state'},
  817. '--sharedstatedir=/var/state --prefix=/usr --sysconfdir=sysconf':
  818. {'prefix': '/usr',
  819. 'sysconfdir': 'sysconf',
  820. 'localstatedir': '/var',
  821. 'sharedstatedir': '/var/state'},
  822. }
  823. for args in expected:
  824. self.init(testdir, args.split(), default_args=False)
  825. opts = self.introspect('--buildoptions')
  826. for opt in opts:
  827. name = opt['name']
  828. value = opt['value']
  829. if name in expected[args]:
  830. self.assertEqual(value, expected[args][name])
  831. self.wipe()
  832. def test_static_library_overwrite(self):
  833. '''
  834. Tests that static libraries are never appended to, always overwritten.
  835. Has to be a unit test because this involves building a project,
  836. reconfiguring, and building it again so that `ar` is run twice on the
  837. same static library.
  838. https://github.com/mesonbuild/meson/issues/1355
  839. '''
  840. testdir = os.path.join(self.common_test_dir, '3 static')
  841. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  842. cc = env.detect_c_compiler(False)
  843. static_linker = env.detect_static_linker(cc)
  844. if is_windows():
  845. raise unittest.SkipTest('https://github.com/mesonbuild/meson/issues/1526')
  846. if not isinstance(static_linker, mesonbuild.linkers.ArLinker):
  847. raise unittest.SkipTest('static linker is not `ar`')
  848. # Configure
  849. self.init(testdir)
  850. # Get name of static library
  851. targets = self.introspect('--targets')
  852. self.assertEqual(len(targets), 1)
  853. libname = targets[0]['filename']
  854. # Build and get contents of static library
  855. self.build()
  856. before = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split()
  857. # Filter out non-object-file contents
  858. before = [f for f in before if f.endswith(('.o', '.obj'))]
  859. # Static library should contain only one object
  860. self.assertEqual(len(before), 1, msg=before)
  861. # Change the source to be built into the static library
  862. self.setconf('-Dsource=libfile2.c')
  863. self.build()
  864. after = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split()
  865. # Filter out non-object-file contents
  866. after = [f for f in after if f.endswith(('.o', '.obj'))]
  867. # Static library should contain only one object
  868. self.assertEqual(len(after), 1, msg=after)
  869. # and the object must have changed
  870. self.assertNotEqual(before, after)
  871. def test_static_compile_order(self):
  872. '''
  873. Test that the order of files in a compiler command-line while compiling
  874. and linking statically is deterministic. This can't be an ordinary test
  875. case because we need to inspect the compiler database.
  876. https://github.com/mesonbuild/meson/pull/951
  877. '''
  878. testdir = os.path.join(self.common_test_dir, '5 linkstatic')
  879. self.init(testdir)
  880. compdb = self.get_compdb()
  881. # Rules will get written out in this order
  882. self.assertTrue(compdb[0]['file'].endswith("libfile.c"))
  883. self.assertTrue(compdb[1]['file'].endswith("libfile2.c"))
  884. self.assertTrue(compdb[2]['file'].endswith("libfile3.c"))
  885. self.assertTrue(compdb[3]['file'].endswith("libfile4.c"))
  886. # FIXME: We don't have access to the linker command
  887. def test_run_target_files_path(self):
  888. '''
  889. Test that run_targets are run from the correct directory
  890. https://github.com/mesonbuild/meson/issues/957
  891. '''
  892. testdir = os.path.join(self.common_test_dir, '58 run target')
  893. self.init(testdir)
  894. self.run_target('check_exists')
  895. def test_install_introspection(self):
  896. '''
  897. Tests that the Meson introspection API exposes install filenames correctly
  898. https://github.com/mesonbuild/meson/issues/829
  899. '''
  900. if self.backend is not Backend.ninja:
  901. raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name))
  902. testdir = os.path.join(self.common_test_dir, '8 install')
  903. self.init(testdir)
  904. intro = self.introspect('--targets')
  905. if intro[0]['type'] == 'executable':
  906. intro = intro[::-1]
  907. self.assertPathEqual(intro[0]['install_filename'], '/usr/lib/libstat.a')
  908. self.assertPathEqual(intro[1]['install_filename'], '/usr/bin/prog' + exe_suffix)
  909. def test_uninstall(self):
  910. exename = os.path.join(self.installdir, 'usr/bin/prog' + exe_suffix)
  911. testdir = os.path.join(self.common_test_dir, '8 install')
  912. self.init(testdir)
  913. self.assertPathDoesNotExist(exename)
  914. self.install()
  915. self.assertPathExists(exename)
  916. self.uninstall()
  917. self.assertPathDoesNotExist(exename)
  918. def test_forcefallback(self):
  919. testdir = os.path.join(self.unit_test_dir, '27 forcefallback')
  920. self.init(testdir, ['--wrap-mode=forcefallback'])
  921. self.build()
  922. self.run_tests()
  923. def test_testsetups(self):
  924. if not shutil.which('valgrind'):
  925. raise unittest.SkipTest('Valgrind not installed.')
  926. testdir = os.path.join(self.unit_test_dir, '2 testsetups')
  927. self.init(testdir)
  928. self.build()
  929. # Run tests without setup
  930. self.run_tests()
  931. with open(os.path.join(self.logdir, 'testlog.txt')) as f:
  932. basic_log = f.read()
  933. # Run buggy test with setup that has env that will make it fail
  934. self.assertRaises(subprocess.CalledProcessError,
  935. self._run, self.mtest_command + ['--setup=valgrind'])
  936. with open(os.path.join(self.logdir, 'testlog-valgrind.txt')) as f:
  937. vg_log = f.read()
  938. self.assertFalse('TEST_ENV is set' in basic_log)
  939. self.assertFalse('Memcheck' in basic_log)
  940. self.assertTrue('TEST_ENV is set' in vg_log)
  941. self.assertTrue('Memcheck' in vg_log)
  942. # Run buggy test with setup without env that will pass
  943. self._run(self.mtest_command + ['--setup=wrapper'])
  944. # Setup with no properties works
  945. self._run(self.mtest_command + ['--setup=empty'])
  946. # Setup with only env works
  947. self._run(self.mtest_command + ['--setup=onlyenv'])
  948. self._run(self.mtest_command + ['--setup=onlyenv2'])
  949. self._run(self.mtest_command + ['--setup=onlyenv3'])
  950. # Setup with only a timeout works
  951. self._run(self.mtest_command + ['--setup=timeout'])
  952. def test_testsetup_selection(self):
  953. testdir = os.path.join(self.unit_test_dir, '13 testsetup selection')
  954. self.init(testdir)
  955. self.build()
  956. # Run tests without setup
  957. self.run_tests()
  958. self.assertRaises(subprocess.CalledProcessError, self._run, self.mtest_command + ['--setup=missingfromfoo'])
  959. self._run(self.mtest_command + ['--setup=missingfromfoo', '--no-suite=foo:'])
  960. self._run(self.mtest_command + ['--setup=worksforall'])
  961. self._run(self.mtest_command + ['--setup=main:worksforall'])
  962. self.assertRaises(subprocess.CalledProcessError, self._run,
  963. self.mtest_command + ['--setup=onlyinbar'])
  964. self.assertRaises(subprocess.CalledProcessError, self._run,
  965. self.mtest_command + ['--setup=onlyinbar', '--no-suite=main:'])
  966. self._run(self.mtest_command + ['--setup=onlyinbar', '--no-suite=main:', '--no-suite=foo:'])
  967. self._run(self.mtest_command + ['--setup=bar:onlyinbar'])
  968. self.assertRaises(subprocess.CalledProcessError, self._run,
  969. self.mtest_command + ['--setup=foo:onlyinbar'])
  970. self.assertRaises(subprocess.CalledProcessError, self._run,
  971. self.mtest_command + ['--setup=main:onlyinbar'])
  972. def assertFailedTestCount(self, failure_count, command):
  973. try:
  974. self._run(command)
  975. self.assertEqual(0, failure_count, 'Expected %d tests to fail.' % failure_count)
  976. except subprocess.CalledProcessError as e:
  977. self.assertEqual(e.returncode, failure_count)
  978. def test_suite_selection(self):
  979. testdir = os.path.join(self.unit_test_dir, '4 suite selection')
  980. self.init(testdir)
  981. self.build()
  982. self.assertFailedTestCount(3, self.mtest_command)
  983. self.assertFailedTestCount(0, self.mtest_command + ['--suite', ':success'])
  984. self.assertFailedTestCount(3, self.mtest_command + ['--suite', ':fail'])
  985. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', ':success'])
  986. self.assertFailedTestCount(0, self.mtest_command + ['--no-suite', ':fail'])
  987. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj'])
  988. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc'])
  989. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail'])
  990. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix'])
  991. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj'])
  992. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc'])
  993. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail'])
  994. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix'])
  995. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj:fail'])
  996. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'mainprj:success'])
  997. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj:fail'])
  998. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'mainprj:success'])
  999. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail:fail'])
  1000. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjfail:success'])
  1001. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail:fail'])
  1002. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail:success'])
  1003. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:fail'])
  1004. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:success'])
  1005. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:fail'])
  1006. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:success'])
  1007. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix:fail'])
  1008. self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjmix:success'])
  1009. self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix:fail'])
  1010. self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix:success'])
  1011. self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail'])
  1012. self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj'])
  1013. self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail'])
  1014. self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail', 'mainprj-failing_test'])
  1015. self.assertFailedTestCount(1, self.mtest_command + ['--no-suite', 'subprjfail:fail', '--no-suite', 'subprjmix:fail'])
  1016. def test_build_by_default(self):
  1017. testdir = os.path.join(self.common_test_dir, '137 build by default')
  1018. self.init(testdir)
  1019. self.build()
  1020. genfile1 = os.path.join(self.builddir, 'generated1.dat')
  1021. genfile2 = os.path.join(self.builddir, 'generated2.dat')
  1022. exe1 = os.path.join(self.builddir, 'fooprog' + exe_suffix)
  1023. exe2 = os.path.join(self.builddir, 'barprog' + exe_suffix)
  1024. self.assertPathExists(genfile1)
  1025. self.assertPathExists(genfile2)
  1026. self.assertPathDoesNotExist(exe1)
  1027. self.assertPathDoesNotExist(exe2)
  1028. self.build(target=('fooprog' + exe_suffix))
  1029. self.assertPathExists(exe1)
  1030. self.build(target=('barprog' + exe_suffix))
  1031. self.assertPathExists(exe2)
  1032. def test_internal_include_order(self):
  1033. testdir = os.path.join(self.common_test_dir, '138 include order')
  1034. self.init(testdir)
  1035. execmd = fxecmd = None
  1036. for cmd in self.get_compdb():
  1037. if 'someexe' in cmd['command']:
  1038. execmd = cmd['command']
  1039. continue
  1040. if 'somefxe' in cmd['command']:
  1041. fxecmd = cmd['command']
  1042. continue
  1043. if not execmd or not fxecmd:
  1044. raise Exception('Could not find someexe and somfxe commands')
  1045. # Check include order for 'someexe'
  1046. incs = [a for a in shlex.split(execmd) if a.startswith("-I")]
  1047. self.assertEqual(len(incs), 9)
  1048. # target private dir
  1049. self.assertPathEqual(incs[0], "-Isub4/sub4@@someexe@exe")
  1050. # target build subdir
  1051. self.assertPathEqual(incs[1], "-Isub4")
  1052. # target source subdir
  1053. self.assertPathBasenameEqual(incs[2], 'sub4')
  1054. # include paths added via per-target c_args: ['-I'...]
  1055. self.assertPathBasenameEqual(incs[3], 'sub3')
  1056. # target include_directories: build dir
  1057. self.assertPathEqual(incs[4], "-Isub2")
  1058. # target include_directories: source dir
  1059. self.assertPathBasenameEqual(incs[5], 'sub2')
  1060. # target internal dependency include_directories: build dir
  1061. self.assertPathEqual(incs[6], "-Isub1")
  1062. # target internal dependency include_directories: source dir
  1063. self.assertPathBasenameEqual(incs[7], 'sub1')
  1064. # custom target include dir
  1065. self.assertPathEqual(incs[8], '-Ictsub')
  1066. # Check include order for 'somefxe'
  1067. incs = [a for a in shlex.split(fxecmd) if a.startswith('-I')]
  1068. self.assertEqual(len(incs), 9)
  1069. # target private dir
  1070. self.assertPathEqual(incs[0], '-Isomefxe@exe')
  1071. # target build dir
  1072. self.assertPathEqual(incs[1], '-I.')
  1073. # target source dir
  1074. self.assertPathBasenameEqual(incs[2], os.path.basename(testdir))
  1075. # target internal dependency correct include_directories: build dir
  1076. self.assertPathEqual(incs[3], "-Isub4")
  1077. # target internal dependency correct include_directories: source dir
  1078. self.assertPathBasenameEqual(incs[4], 'sub4')
  1079. # target internal dependency dep include_directories: build dir
  1080. self.assertPathEqual(incs[5], "-Isub1")
  1081. # target internal dependency dep include_directories: source dir
  1082. self.assertPathBasenameEqual(incs[6], 'sub1')
  1083. # target internal dependency wrong include_directories: build dir
  1084. self.assertPathEqual(incs[7], "-Isub2")
  1085. # target internal dependency wrong include_directories: source dir
  1086. self.assertPathBasenameEqual(incs[8], 'sub2')
  1087. def test_compiler_detection(self):
  1088. '''
  1089. Test that automatic compiler detection and setting from the environment
  1090. both work just fine. This is needed because while running project tests
  1091. and other unit tests, we always read CC/CXX/etc from the environment.
  1092. '''
  1093. gnu = mesonbuild.compilers.GnuCompiler
  1094. clang = mesonbuild.compilers.ClangCompiler
  1095. intel = mesonbuild.compilers.IntelCompiler
  1096. msvc = mesonbuild.compilers.VisualStudioCCompiler
  1097. ar = mesonbuild.linkers.ArLinker
  1098. lib = mesonbuild.linkers.VisualStudioLinker
  1099. langs = [('c', 'CC'), ('cpp', 'CXX')]
  1100. if not is_windows():
  1101. langs += [('objc', 'OBJC'), ('objcpp', 'OBJCXX')]
  1102. testdir = os.path.join(self.unit_test_dir, '5 compiler detection')
  1103. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  1104. for lang, evar in langs:
  1105. # Detect with evar and do sanity checks on that
  1106. if evar in os.environ:
  1107. ecc = getattr(env, 'detect_{}_compiler'.format(lang))(False)
  1108. self.assertTrue(ecc.version)
  1109. elinker = env.detect_static_linker(ecc)
  1110. # Pop it so we don't use it for the next detection
  1111. evalue = os.environ.pop(evar)
  1112. # Very rough/strict heuristics. Would never work for actual
  1113. # compiler detection, but should be ok for the tests.
  1114. ebase = os.path.basename(evalue)
  1115. if ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')):
  1116. self.assertIsInstance(ecc, gnu)
  1117. self.assertIsInstance(elinker, ar)
  1118. elif 'clang' in ebase:
  1119. self.assertIsInstance(ecc, clang)
  1120. self.assertIsInstance(elinker, ar)
  1121. elif ebase.startswith('ic'):
  1122. self.assertIsInstance(ecc, intel)
  1123. self.assertIsInstance(elinker, ar)
  1124. elif ebase.startswith('cl'):
  1125. self.assertIsInstance(ecc, msvc)
  1126. self.assertIsInstance(elinker, lib)
  1127. else:
  1128. raise AssertionError('Unknown compiler {!r}'.format(evalue))
  1129. # Check that we actually used the evalue correctly as the compiler
  1130. self.assertEqual(ecc.get_exelist(), shlex.split(evalue))
  1131. # Do auto-detection of compiler based on platform, PATH, etc.
  1132. cc = getattr(env, 'detect_{}_compiler'.format(lang))(False)
  1133. self.assertTrue(cc.version)
  1134. linker = env.detect_static_linker(cc)
  1135. # Check compiler type
  1136. if isinstance(cc, gnu):
  1137. self.assertIsInstance(linker, ar)
  1138. if is_osx():
  1139. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_OSX)
  1140. elif is_windows():
  1141. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_MINGW)
  1142. elif is_cygwin():
  1143. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_CYGWIN)
  1144. else:
  1145. self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_STANDARD)
  1146. if isinstance(cc, clang):
  1147. self.assertIsInstance(linker, ar)
  1148. if is_osx():
  1149. self.assertEqual(cc.clang_type, mesonbuild.compilers.CLANG_OSX)
  1150. elif is_windows():
  1151. # Not implemented yet
  1152. self.assertEqual(cc.clang_type, mesonbuild.compilers.CLANG_WIN)
  1153. else:
  1154. self.assertEqual(cc.clang_type, mesonbuild.compilers.CLANG_STANDARD)
  1155. if isinstance(cc, intel):
  1156. self.assertIsInstance(linker, ar)
  1157. if is_osx():
  1158. self.assertEqual(cc.icc_type, mesonbuild.compilers.ICC_OSX)
  1159. elif is_windows():
  1160. self.assertEqual(cc.icc_type, mesonbuild.compilers.ICC_WIN)
  1161. else:
  1162. self.assertEqual(cc.icc_type, mesonbuild.compilers.ICC_STANDARD)
  1163. if isinstance(cc, msvc):
  1164. self.assertTrue(is_windows())
  1165. self.assertIsInstance(linker, lib)
  1166. self.assertEqual(cc.id, 'msvc')
  1167. self.assertTrue(hasattr(cc, 'is_64'))
  1168. # If we're in the appveyor CI, we know what the compiler will be
  1169. if 'arch' in os.environ:
  1170. if os.environ['arch'] == 'x64':
  1171. self.assertTrue(cc.is_64)
  1172. else:
  1173. self.assertFalse(cc.is_64)
  1174. # Set evar ourselves to a wrapper script that just calls the same
  1175. # exelist + some argument. This is meant to test that setting
  1176. # something like `ccache gcc -pipe` or `distcc ccache gcc` works.
  1177. wrapper = os.path.join(testdir, 'compiler wrapper.py')
  1178. wrappercc = python_command + [wrapper] + cc.get_exelist() + ['-DSOME_ARG']
  1179. wrappercc_s = ''
  1180. for w in wrappercc:
  1181. wrappercc_s += shlex.quote(w) + ' '
  1182. os.environ[evar] = wrappercc_s
  1183. wcc = getattr(env, 'detect_{}_compiler'.format(lang))(False)
  1184. # Check static linker too
  1185. wrapperlinker = python_command + [wrapper] + linker.get_exelist() + linker.get_always_args()
  1186. wrapperlinker_s = ''
  1187. for w in wrapperlinker:
  1188. wrapperlinker_s += shlex.quote(w) + ' '
  1189. os.environ['AR'] = wrapperlinker_s
  1190. wlinker = env.detect_static_linker(wcc)
  1191. # Must be the same type since it's a wrapper around the same exelist
  1192. self.assertIs(type(cc), type(wcc))
  1193. self.assertIs(type(linker), type(wlinker))
  1194. # Ensure that the exelist is correct
  1195. self.assertEqual(wcc.get_exelist(), wrappercc)
  1196. self.assertEqual(wlinker.get_exelist(), wrapperlinker)
  1197. # Ensure that the version detection worked correctly
  1198. self.assertEqual(cc.version, wcc.version)
  1199. if hasattr(cc, 'is_64'):
  1200. self.assertEqual(cc.is_64, wcc.is_64)
  1201. def test_always_prefer_c_compiler_for_asm(self):
  1202. testdir = os.path.join(self.common_test_dir, '141 c cpp and asm')
  1203. # Skip if building with MSVC
  1204. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  1205. if env.detect_c_compiler(False).get_id() == 'msvc':
  1206. raise unittest.SkipTest('MSVC can\'t compile assembly')
  1207. self.init(testdir)
  1208. commands = {'c-asm': {}, 'cpp-asm': {}, 'cpp-c-asm': {}, 'c-cpp-asm': {}}
  1209. for cmd in self.get_compdb():
  1210. # Get compiler
  1211. split = shlex.split(cmd['command'])
  1212. if split[0] == 'ccache':
  1213. compiler = split[1]
  1214. else:
  1215. compiler = split[0]
  1216. # Classify commands
  1217. if 'Ic-asm' in cmd['command']:
  1218. if cmd['file'].endswith('.S'):
  1219. commands['c-asm']['asm'] = compiler
  1220. elif cmd['file'].endswith('.c'):
  1221. commands['c-asm']['c'] = compiler
  1222. else:
  1223. raise AssertionError('{!r} found in cpp-asm?'.format(cmd['command']))
  1224. elif 'Icpp-asm' in cmd['command']:
  1225. if cmd['file'].endswith('.S'):
  1226. commands['cpp-asm']['asm'] = compiler
  1227. elif cmd['file'].endswith('.cpp'):
  1228. commands['cpp-asm']['cpp'] = compiler
  1229. else:
  1230. raise AssertionError('{!r} found in cpp-asm?'.format(cmd['command']))
  1231. elif 'Ic-cpp-asm' in cmd['command']:
  1232. if cmd['file'].endswith('.S'):
  1233. commands['c-cpp-asm']['asm'] = compiler
  1234. elif cmd['file'].endswith('.c'):
  1235. commands['c-cpp-asm']['c'] = compiler
  1236. elif cmd['file'].endswith('.cpp'):
  1237. commands['c-cpp-asm']['cpp'] = compiler
  1238. else:
  1239. raise AssertionError('{!r} found in c-cpp-asm?'.format(cmd['command']))
  1240. elif 'Icpp-c-asm' in cmd['command']:
  1241. if cmd['file'].endswith('.S'):
  1242. commands['cpp-c-asm']['asm'] = compiler
  1243. elif cmd['file'].endswith('.c'):
  1244. commands['cpp-c-asm']['c'] = compiler
  1245. elif cmd['file'].endswith('.cpp'):
  1246. commands['cpp-c-asm']['cpp'] = compiler
  1247. else:
  1248. raise AssertionError('{!r} found in cpp-c-asm?'.format(cmd['command']))
  1249. else:
  1250. raise AssertionError('Unknown command {!r} found'.format(cmd['command']))
  1251. # Check that .S files are always built with the C compiler
  1252. self.assertEqual(commands['c-asm']['asm'], commands['c-asm']['c'])
  1253. self.assertEqual(commands['c-asm']['asm'], commands['cpp-asm']['asm'])
  1254. self.assertEqual(commands['cpp-asm']['asm'], commands['c-cpp-asm']['c'])
  1255. self.assertEqual(commands['c-cpp-asm']['asm'], commands['c-cpp-asm']['c'])
  1256. self.assertEqual(commands['cpp-c-asm']['asm'], commands['cpp-c-asm']['c'])
  1257. self.assertNotEqual(commands['cpp-asm']['asm'], commands['cpp-asm']['cpp'])
  1258. self.assertNotEqual(commands['c-cpp-asm']['c'], commands['c-cpp-asm']['cpp'])
  1259. self.assertNotEqual(commands['cpp-c-asm']['c'], commands['cpp-c-asm']['cpp'])
  1260. # Check that the c-asm target is always linked with the C linker
  1261. build_ninja = os.path.join(self.builddir, 'build.ninja')
  1262. with open(build_ninja, 'r', encoding='utf-8') as f:
  1263. contents = f.read()
  1264. m = re.search('build c-asm.*: c_LINKER', contents)
  1265. self.assertIsNotNone(m, msg=contents)
  1266. def test_preprocessor_checks_CPPFLAGS(self):
  1267. '''
  1268. Test that preprocessor compiler checks read CPPFLAGS but not CFLAGS
  1269. '''
  1270. testdir = os.path.join(self.common_test_dir, '140 get define')
  1271. define = 'MESON_TEST_DEFINE_VALUE'
  1272. # NOTE: this list can't have \n, ' or "
  1273. # \n is never substituted by the GNU pre-processor via a -D define
  1274. # ' and " confuse shlex.split() even when they are escaped
  1275. # % and # confuse the MSVC preprocessor
  1276. # !, ^, *, and < confuse lcc preprocessor
  1277. value = 'spaces and fun@$&()-=_+{}[]:;>?,./~`'
  1278. os.environ['CPPFLAGS'] = '-D{}="{}"'.format(define, value)
  1279. os.environ['CFLAGS'] = '-DMESON_FAIL_VALUE=cflags-read'.format(define)
  1280. self.init(testdir, ['-D{}={}'.format(define, value)])
  1281. def test_custom_target_exe_data_deterministic(self):
  1282. testdir = os.path.join(self.common_test_dir, '117 custom target capture')
  1283. self.init(testdir)
  1284. meson_exe_dat1 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
  1285. self.wipe()
  1286. self.init(testdir)
  1287. meson_exe_dat2 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
  1288. self.assertListEqual(meson_exe_dat1, meson_exe_dat2)
  1289. def test_source_changes_cause_rebuild(self):
  1290. '''
  1291. Test that changes to sources and headers cause rebuilds, but not
  1292. changes to unused files (as determined by the dependency file) in the
  1293. input files list.
  1294. '''
  1295. testdir = os.path.join(self.common_test_dir, '22 header in file list')
  1296. self.init(testdir)
  1297. self.build()
  1298. # Immediately rebuilding should not do anything
  1299. self.assertBuildIsNoop()
  1300. # Changing mtime of header.h should rebuild everything
  1301. self.utime(os.path.join(testdir, 'header.h'))
  1302. self.assertRebuiltTarget('prog')
  1303. def test_custom_target_changes_cause_rebuild(self):
  1304. '''
  1305. Test that in a custom target, changes to the input files, the
  1306. ExternalProgram, and any File objects on the command-line cause
  1307. a rebuild.
  1308. '''
  1309. testdir = os.path.join(self.common_test_dir, '64 custom header generator')
  1310. self.init(testdir)
  1311. self.build()
  1312. # Immediately rebuilding should not do anything
  1313. self.assertBuildIsNoop()
  1314. # Changing mtime of these should rebuild everything
  1315. for f in ('input.def', 'makeheader.py', 'somefile.txt'):
  1316. self.utime(os.path.join(testdir, f))
  1317. self.assertRebuiltTarget('prog')
  1318. def test_static_library_lto(self):
  1319. '''
  1320. Test that static libraries can be built with LTO and linked to
  1321. executables. On Linux, this requires the use of gcc-ar.
  1322. https://github.com/mesonbuild/meson/issues/1646
  1323. '''
  1324. testdir = os.path.join(self.common_test_dir, '5 linkstatic')
  1325. self.init(testdir, extra_args='-Db_lto=true')
  1326. self.build()
  1327. self.run_tests()
  1328. def test_dist_git(self):
  1329. if not shutil.which('git'):
  1330. raise unittest.SkipTest('Git not found')
  1331. def git_init(project_dir):
  1332. subprocess.check_call(['git', 'init'], cwd=project_dir, stdout=subprocess.DEVNULL)
  1333. subprocess.check_call(['git', 'config',
  1334. 'user.name', 'Author Person'], cwd=project_dir)
  1335. subprocess.check_call(['git', 'config',
  1336. 'user.email', 'teh_coderz@example.com'], cwd=project_dir)
  1337. subprocess.check_call(['git', 'add', 'meson.build', 'distexe.c'], cwd=project_dir,
  1338. stdout=subprocess.DEVNULL)
  1339. subprocess.check_call(['git', 'commit', '-a', '-m', 'I am a project'], cwd=project_dir,
  1340. stdout=subprocess.DEVNULL)
  1341. try:
  1342. self.dist_impl(git_init)
  1343. except PermissionError:
  1344. # When run under Windows CI, something (virus scanner?)
  1345. # holds on to the git files so cleaning up the dir
  1346. # fails sometimes.
  1347. pass
  1348. def test_dist_hg(self):
  1349. if not shutil.which('hg'):
  1350. raise unittest.SkipTest('Mercurial not found')
  1351. if self.backend is not Backend.ninja:
  1352. raise unittest.SkipTest('Dist is only supported with Ninja')
  1353. def hg_init(project_dir):
  1354. subprocess.check_call(['hg', 'init'], cwd=project_dir)
  1355. with open(os.path.join(project_dir, '.hg', 'hgrc'), 'w') as f:
  1356. print('[ui]', file=f)
  1357. print('username=Author Person <teh_coderz@example.com>', file=f)
  1358. subprocess.check_call(['hg', 'add', 'meson.build', 'distexe.c'], cwd=project_dir)
  1359. subprocess.check_call(['hg', 'commit', '-m', 'I am a project'], cwd=project_dir)
  1360. try:
  1361. self.dist_impl(hg_init)
  1362. except PermissionError:
  1363. # When run under Windows CI, something (virus scanner?)
  1364. # holds on to the hg files so cleaning up the dir
  1365. # fails sometimes.
  1366. pass
  1367. def dist_impl(self, vcs_init):
  1368. # Create this on the fly because having rogue .git directories inside
  1369. # the source tree leads to all kinds of trouble.
  1370. with tempfile.TemporaryDirectory() as project_dir:
  1371. with open(os.path.join(project_dir, 'meson.build'), 'w') as ofile:
  1372. ofile.write('''project('disttest', 'c', version : '1.4.3')
  1373. e = executable('distexe', 'distexe.c')
  1374. test('dist test', e)
  1375. ''')
  1376. with open(os.path.join(project_dir, 'distexe.c'), 'w') as ofile:
  1377. ofile.write('''#include<stdio.h>
  1378. int main(int argc, char **argv) {
  1379. printf("I am a distribution test.\\n");
  1380. return 0;
  1381. }
  1382. ''')
  1383. vcs_init(project_dir)
  1384. self.init(project_dir)
  1385. self.build('dist')
  1386. distfile = os.path.join(self.distdir, 'disttest-1.4.3.tar.xz')
  1387. checksumfile = distfile + '.sha256sum'
  1388. self.assertPathExists(distfile)
  1389. self.assertPathExists(checksumfile)
  1390. def test_rpath_uses_ORIGIN(self):
  1391. '''
  1392. Test that built targets use $ORIGIN in rpath, which ensures that they
  1393. are relocatable and ensures that builds are reproducible since the
  1394. build directory won't get embedded into the built binaries.
  1395. '''
  1396. if is_windows() or is_cygwin():
  1397. raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH')
  1398. testdir = os.path.join(self.common_test_dir, '46 library chain')
  1399. self.init(testdir)
  1400. self.build()
  1401. for each in ('prog', 'subdir/liblib1.so', ):
  1402. rpath = get_rpath(os.path.join(self.builddir, each))
  1403. self.assertTrue(rpath)
  1404. if is_dragonflybsd():
  1405. # DragonflyBSD will prepend /usr/lib/gccVERSION to the rpath,
  1406. # so ignore that.
  1407. self.assertTrue(rpath.startswith('/usr/lib/gcc'))
  1408. rpaths = rpath.split(':')[1:]
  1409. else:
  1410. rpaths = rpath.split(':')
  1411. for path in rpaths:
  1412. self.assertTrue(path.startswith('$ORIGIN'), msg=(each, path))
  1413. # These two don't link to anything else, so they do not need an rpath entry.
  1414. for each in ('subdir/subdir2/liblib2.so', 'subdir/subdir3/liblib3.so'):
  1415. rpath = get_rpath(os.path.join(self.builddir, each))
  1416. if is_dragonflybsd():
  1417. # The rpath should be equal to /usr/lib/gccVERSION
  1418. self.assertTrue(rpath.startswith('/usr/lib/gcc'))
  1419. self.assertEqual(len(rpath.split(':')), 1)
  1420. else:
  1421. self.assertTrue(rpath is None)
  1422. def test_dash_d_dedup(self):
  1423. testdir = os.path.join(self.unit_test_dir, '10 d dedup')
  1424. self.init(testdir)
  1425. cmd = self.get_compdb()[0]['command']
  1426. self.assertTrue('-D FOO -D BAR' in cmd or
  1427. '"-D" "FOO" "-D" "BAR"' in cmd or
  1428. '/D FOO /D BAR' in cmd or
  1429. '"/D" "FOO" "/D" "BAR"' in cmd)
  1430. def test_all_forbidden_targets_tested(self):
  1431. '''
  1432. Test that all forbidden targets are tested in the '159 reserved targets'
  1433. test. Needs to be a unit test because it accesses Meson internals.
  1434. '''
  1435. testdir = os.path.join(self.common_test_dir, '159 reserved targets')
  1436. targets = mesonbuild.coredata.forbidden_target_names
  1437. # We don't actually define a target with this name
  1438. targets.pop('build.ninja')
  1439. # Remove this to avoid multiple entries with the same name
  1440. # but different case.
  1441. targets.pop('PHONY')
  1442. for i in targets:
  1443. self.assertPathExists(os.path.join(testdir, i))
  1444. def detect_prebuild_env(self):
  1445. env = Environment('', self.builddir, get_fake_options(self.prefix))
  1446. cc = env.detect_c_compiler(False)
  1447. stlinker = env.detect_static_linker(cc)
  1448. if mesonbuild.mesonlib.is_windows():
  1449. object_suffix = 'obj'
  1450. shared_suffix = 'dll'
  1451. elif mesonbuild.mesonlib.is_cygwin():
  1452. object_suffix = 'o'
  1453. shared_suffix = 'dll'
  1454. elif mesonbuild.mesonlib.is_osx():
  1455. object_suffix = 'o'
  1456. shared_suffix = 'dylib'
  1457. else:
  1458. object_suffix = 'o'
  1459. shared_suffix = 'so'
  1460. return (cc, stlinker, object_suffix, shared_suffix)
  1461. def pbcompile(self, compiler, source, objectfile, extra_args=[]):
  1462. cmd = compiler.get_exelist()
  1463. if compiler.id == 'msvc':
  1464. cmd += ['/nologo', '/Fo' + objectfile, '/c', source] + extra_args
  1465. else:
  1466. cmd += ['-c', source, '-o', objectfile] + extra_args
  1467. subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
  1468. def test_prebuilt_object(self):
  1469. (compiler, _, object_suffix, _) = self.detect_prebuild_env()
  1470. tdir = os.path.join(self.unit_test_dir, '14 prebuilt object')
  1471. source = os.path.join(tdir, 'source.c')
  1472. objectfile = os.path.join(tdir, 'prebuilt.' + object_suffix)
  1473. self.pbcompile(compiler, source, objectfile)
  1474. try:
  1475. self.init(tdir)
  1476. self.build()
  1477. self.run_tests()
  1478. finally:
  1479. os.unlink(objectfile)
  1480. def build_static_lib(self, compiler, linker, source, objectfile, outfile, extra_args=None):
  1481. if extra_args is None:
  1482. extra_args = []
  1483. if compiler.id == 'msvc':
  1484. link_cmd = ['lib', '/NOLOGO', '/OUT:' + outfile, objectfile]
  1485. else:
  1486. link_cmd = ['ar', 'csr', outfile, objectfile]
  1487. link_cmd = linker.get_exelist()
  1488. link_cmd += linker.get_always_args()
  1489. link_cmd += linker.get_std_link_args()
  1490. link_cmd += linker.get_output_args(outfile)
  1491. link_cmd += [objectfile]
  1492. self.pbcompile(compiler, source, objectfile, extra_args=extra_args)
  1493. try:
  1494. subprocess.check_call(link_cmd)
  1495. finally:
  1496. os.unlink(objectfile)
  1497. def test_prebuilt_static_lib(self):
  1498. (cc, stlinker, object_suffix, _) = self.detect_prebuild_env()
  1499. tdir = os.path.join(self.unit_test_dir, '15 prebuilt static')
  1500. source = os.path.join(tdir, 'libdir/best.c')
  1501. objectfile = os.path.join(tdir, 'libdir/best.' + object_suffix)
  1502. stlibfile = os.path.join(tdir, 'libdir/libbest.a')
  1503. self.build_static_lib(cc, stlinker, source, objectfile, stlibfile)
  1504. # Run the test
  1505. try:
  1506. self.init(tdir)
  1507. self.build()
  1508. self.run_tests()
  1509. finally:
  1510. os.unlink(stlibfile)
  1511. def build_shared_lib(self, compiler, source, objectfile, outfile, impfile, extra_args=None):
  1512. if extra_args is None:
  1513. extra_args = []
  1514. if compiler.id == 'msvc':
  1515. link_cmd = ['link', '/NOLOGO', '/DLL', '/DEBUG',
  1516. '/IMPLIB:' + impfile, '/OUT:' + outfile, objectfile]
  1517. else:
  1518. extra_args += ['-fPIC']
  1519. link_cmd = compiler.get_exelist() + ['-shared', '-o', outfile, objectfile]
  1520. if not mesonbuild.mesonlib.is_osx():
  1521. link_cmd += ['-Wl,-soname=' + os.path.basename(outfile)]
  1522. self.pbcompile(compiler, source, objectfile, extra_args=extra_args)
  1523. try:
  1524. subprocess.check_call(link_cmd)
  1525. finally:
  1526. os.unlink(objectfile)
  1527. def test_prebuilt_shared_lib(self):
  1528. (cc, _, object_suffix, shared_suffix) = self.detect_prebuild_env()
  1529. tdir = os.path.join(self.unit_test_dir, '16 prebuilt shared')
  1530. source = os.path.join(tdir, 'alexandria.c')
  1531. objectfile = os.path.join(tdir, 'alexandria.' + object_suffix)
  1532. impfile = os.path.join(tdir, 'alexandria.lib')
  1533. if cc.id == 'msvc':
  1534. shlibfile = os.path.join(tdir, 'alexandria.' + shared_suffix)
  1535. elif is_cygwin():
  1536. shlibfile = os.path.join(tdir, 'cygalexandria.' + shared_suffix)
  1537. else:
  1538. shlibfile = os.path.join(tdir, 'libalexandria.' + shared_suffix)
  1539. self.build_shared_lib(cc, source, objectfile, shlibfile, impfile)
  1540. # Run the test
  1541. try:
  1542. self.init(tdir)
  1543. self.build()
  1544. self.run_tests()
  1545. finally:
  1546. os.unlink(shlibfile)
  1547. if mesonbuild.mesonlib.is_windows():
  1548. # Clean up all the garbage MSVC writes in the
  1549. # source tree.
  1550. for fname in glob(os.path.join(tdir, 'alexandria.*')):
  1551. if os.path.splitext(fname)[1] not in ['.c', '.h']:
  1552. os.unlink(fname)
  1553. @skipIfNoPkgconfig
  1554. def test_pkgconfig_static(self):
  1555. '''
  1556. Test that the we prefer static libraries when `static: true` is
  1557. passed to dependency() with pkg-config. Can't be an ordinary test
  1558. because we need to build libs and try to find them from meson.build
  1559. Also test that it's not a hard error to have unsatisfiable library deps
  1560. since system libraries -lm will never be found statically.
  1561. https://github.com/mesonbuild/meson/issues/2785
  1562. '''
  1563. (cc, stlinker, objext, shext) = self.detect_prebuild_env()
  1564. testdir = os.path.join(self.unit_test_dir, '17 pkgconfig static')
  1565. source = os.path.join(testdir, 'foo.c')
  1566. objectfile = os.path.join(testdir, 'foo.' + objext)
  1567. stlibfile = os.path.join(testdir, 'libfoo.a')
  1568. impfile = os.path.join(testdir, 'foo.lib')
  1569. if cc.id == 'msvc':
  1570. shlibfile = os.path.join(testdir, 'foo.' + shext)
  1571. elif is_cygwin():
  1572. shlibfile = os.path.join(testdir, 'cygfoo.' + shext)
  1573. else:
  1574. shlibfile = os.path.join(testdir, 'libfoo.' + shext)
  1575. # Build libs
  1576. self.build_static_lib(cc, stlinker, source, objectfile, stlibfile, extra_args=['-DFOO_STATIC'])
  1577. self.build_shared_lib(cc, source, objectfile, shlibfile, impfile)
  1578. # Run test
  1579. os.environ['PKG_CONFIG_LIBDIR'] = self.builddir
  1580. try:
  1581. self.init(testdir)
  1582. self.build()
  1583. self.run_tests()
  1584. finally:
  1585. os.unlink(stlibfile)
  1586. os.unlink(shlibfile)
  1587. if mesonbuild.mesonlib.is_windows():
  1588. # Clean up all the garbage MSVC writes in the
  1589. # source tree.
  1590. for fname in glob(os.path.join(testdir, 'foo.*')):
  1591. if os.path.splitext(fname)[1] not in ['.c', '.h', '.in']:
  1592. os.unlink(fname)
  1593. @skipIfNoPkgconfig
  1594. def test_pkgconfig_gen_escaping(self):
  1595. testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
  1596. prefix = '/usr/with spaces'
  1597. libdir = 'lib'
  1598. self.init(testdir, extra_args=['--prefix=' + prefix,
  1599. '--libdir=' + libdir])
  1600. # Find foo dependency
  1601. os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
  1602. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  1603. kwargs = {'required': True, 'silent': True}
  1604. foo_dep = PkgConfigDependency('libfoo', env, kwargs)
  1605. # Ensure link_args are properly quoted
  1606. libdir = PurePath(prefix) / PurePath(libdir)
  1607. link_args = ['-L' + libdir.as_posix(), '-lfoo']
  1608. self.assertEqual(foo_dep.get_link_args(), link_args)
  1609. # Ensure include args are properly quoted
  1610. incdir = PurePath(prefix) / PurePath('include')
  1611. cargs = ['-I' + incdir.as_posix()]
  1612. self.assertEqual(foo_dep.get_compile_args(), cargs)
  1613. def test_array_option_change(self):
  1614. def get_opt():
  1615. opts = self.introspect('--buildoptions')
  1616. for x in opts:
  1617. if x.get('name') == 'list':
  1618. return x
  1619. raise Exception(opts)
  1620. expected = {
  1621. 'name': 'list',
  1622. 'description': 'list',
  1623. 'type': 'array',
  1624. 'value': ['foo', 'bar'],
  1625. }
  1626. tdir = os.path.join(self.unit_test_dir, '18 array option')
  1627. self.init(tdir)
  1628. original = get_opt()
  1629. self.assertDictEqual(original, expected)
  1630. expected['value'] = ['oink', 'boink']
  1631. self.setconf('-Dlist=oink,boink')
  1632. changed = get_opt()
  1633. self.assertEqual(changed, expected)
  1634. def test_array_option_bad_change(self):
  1635. def get_opt():
  1636. opts = self.introspect('--buildoptions')
  1637. for x in opts:
  1638. if x.get('name') == 'list':
  1639. return x
  1640. raise Exception(opts)
  1641. expected = {
  1642. 'name': 'list',
  1643. 'description': 'list',
  1644. 'type': 'array',
  1645. 'value': ['foo', 'bar'],
  1646. }
  1647. tdir = os.path.join(self.unit_test_dir, '18 array option')
  1648. self.init(tdir)
  1649. original = get_opt()
  1650. self.assertDictEqual(original, expected)
  1651. with self.assertRaises(subprocess.CalledProcessError):
  1652. self.setconf('-Dlist=bad')
  1653. changed = get_opt()
  1654. self.assertDictEqual(changed, expected)
  1655. def test_array_option_empty_equivalents(self):
  1656. """Array options treat -Dopt=[] and -Dopt= as equivalent."""
  1657. def get_opt():
  1658. opts = self.introspect('--buildoptions')
  1659. for x in opts:
  1660. if x.get('name') == 'list':
  1661. return x
  1662. raise Exception(opts)
  1663. expected = {
  1664. 'name': 'list',
  1665. 'description': 'list',
  1666. 'type': 'array',
  1667. 'value': [],
  1668. }
  1669. tdir = os.path.join(self.unit_test_dir, '18 array option')
  1670. self.init(tdir, extra_args='-Dlist=')
  1671. original = get_opt()
  1672. self.assertDictEqual(original, expected)
  1673. def opt_has(self, name, value):
  1674. res = self.introspect('--buildoptions')
  1675. found = False
  1676. for i in res:
  1677. if i['name'] == name:
  1678. self.assertEqual(i['value'], value)
  1679. found = True
  1680. break
  1681. self.assertTrue(found, "Array option not found in introspect data.")
  1682. def test_free_stringarray_setting(self):
  1683. testdir = os.path.join(self.common_test_dir, '47 options')
  1684. self.init(testdir)
  1685. self.opt_has('free_array_opt', [])
  1686. self.setconf('-Dfree_array_opt=foo,bar', will_build=False)
  1687. self.opt_has('free_array_opt', ['foo', 'bar'])
  1688. self.setconf("-Dfree_array_opt=['a,b', 'c,d']", will_build=False)
  1689. self.opt_has('free_array_opt', ['a,b', 'c,d'])
  1690. def test_subproject_promotion(self):
  1691. testdir = os.path.join(self.unit_test_dir, '13 promote')
  1692. workdir = os.path.join(self.builddir, 'work')
  1693. shutil.copytree(testdir, workdir)
  1694. spdir = os.path.join(workdir, 'subprojects')
  1695. s3dir = os.path.join(spdir, 's3')
  1696. scommondir = os.path.join(spdir, 'scommon')
  1697. self.assertFalse(os.path.isdir(s3dir))
  1698. subprocess.check_call(self.wrap_command + ['promote', 's3'], cwd=workdir)
  1699. self.assertTrue(os.path.isdir(s3dir))
  1700. self.assertFalse(os.path.isdir(scommondir))
  1701. self.assertNotEqual(subprocess.call(self.wrap_command + ['promote', 'scommon'],
  1702. cwd=workdir,
  1703. stdout=subprocess.DEVNULL), 0)
  1704. self.assertFalse(os.path.isdir(scommondir))
  1705. subprocess.check_call(self.wrap_command + ['promote', 'subprojects/s2/subprojects/scommon'], cwd=workdir)
  1706. self.assertTrue(os.path.isdir(scommondir))
  1707. promoted_wrap = os.path.join(spdir, 'athing.wrap')
  1708. self.assertFalse(os.path.isfile(promoted_wrap))
  1709. subprocess.check_call(self.wrap_command + ['promote', 'athing'], cwd=workdir)
  1710. self.assertTrue(os.path.isfile(promoted_wrap))
  1711. self.init(workdir)
  1712. self.build()
  1713. def test_warning_location(self):
  1714. tdir = os.path.join(self.unit_test_dir, '21 warning location')
  1715. out = self.init(tdir)
  1716. for expected in [
  1717. r'meson.build:4: WARNING: Keyword argument "link_with" defined multiple times.',
  1718. r'sub' + os.path.sep + r'meson.build:3: WARNING: Keyword argument "link_with" defined multiple times.',
  1719. r'meson.build:6: WARNING: a warning of some sort',
  1720. r'sub' + os.path.sep + r'meson.build:4: WARNING: subdir warning',
  1721. r'meson.build:7: WARNING: Module unstable-simd has no backwards or forwards compatibility and might not exist in future releases.',
  1722. r"meson.build:11: WARNING: The variable(s) 'MISSING' in the input file conf.in are not present in the given configuration data.",
  1723. r'meson.build:1: WARNING: Passed invalid keyword argument "invalid".',
  1724. ]:
  1725. self.assertRegex(out, re.escape(expected))
  1726. def test_permitted_method_kwargs(self):
  1727. tdir = os.path.join(self.unit_test_dir, '23 non-permitted kwargs')
  1728. out = self.init(tdir)
  1729. for expected in [
  1730. r'WARNING: Passed invalid keyword argument "prefixxx".',
  1731. r'WARNING: Passed invalid keyword argument "argsxx".',
  1732. r'WARNING: Passed invalid keyword argument "invalidxx".',
  1733. ]:
  1734. self.assertRegex(out, re.escape(expected))
  1735. def test_templates(self):
  1736. ninja = detect_ninja()
  1737. if ninja is None:
  1738. raise unittest.SkipTest('This test currently requires ninja. Fix this once "meson build" works.')
  1739. for lang in ('c', 'cpp'):
  1740. for type in ('executable', 'library'):
  1741. with tempfile.TemporaryDirectory() as tmpdir:
  1742. self._run(self.meson_command + ['init', '--language', lang, '--type', type],
  1743. workdir=tmpdir)
  1744. self._run(self.setup_command + ['--backend=ninja', 'builddir'],
  1745. workdir=tmpdir)
  1746. self._run(ninja,
  1747. workdir=os.path.join(tmpdir, 'builddir'))
  1748. with tempfile.TemporaryDirectory() as tmpdir:
  1749. with open(os.path.join(tmpdir, 'foo.' + lang), 'w') as f:
  1750. f.write('int main() {}')
  1751. self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
  1752. # The test uses mocking and thus requires that
  1753. # the current process is the one to run the Meson steps.
  1754. # If we are using an external test executable (most commonly
  1755. # in Debian autopkgtests) then the mocking won't work.
  1756. @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.')
  1757. def test_cross_file_system_paths(self):
  1758. if is_windows():
  1759. raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)')
  1760. testdir = os.path.join(self.common_test_dir, '1 trivial')
  1761. cross_content = textwrap.dedent("""\
  1762. [binaries]
  1763. c = '/usr/bin/cc'
  1764. ar = '/usr/bin/ar'
  1765. strip = '/usr/bin/ar'
  1766. [properties]
  1767. [host_machine]
  1768. system = 'linux'
  1769. cpu_family = 'x86'
  1770. cpu = 'i686'
  1771. endian = 'little'
  1772. """)
  1773. with tempfile.TemporaryDirectory() as d:
  1774. dir_ = os.path.join(d, 'meson', 'cross')
  1775. os.makedirs(dir_)
  1776. with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
  1777. f.write(cross_content)
  1778. name = os.path.basename(f.name)
  1779. with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}):
  1780. self.init(testdir, ['--cross-file=' + name], inprocess=True)
  1781. self.wipe()
  1782. with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}):
  1783. os.environ.pop('XDG_DATA_HOME', None)
  1784. self.init(testdir, ['--cross-file=' + name], inprocess=True)
  1785. self.wipe()
  1786. with tempfile.TemporaryDirectory() as d:
  1787. dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross')
  1788. os.makedirs(dir_)
  1789. with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
  1790. f.write(cross_content)
  1791. name = os.path.basename(f.name)
  1792. with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)):
  1793. self.init(testdir, ['--cross-file=' + name], inprocess=True)
  1794. self.wipe()
  1795. def test_compiler_run_command(self):
  1796. '''
  1797. The test checks that the compiler object can be passed to
  1798. run_command().
  1799. '''
  1800. testdir = os.path.join(self.unit_test_dir, '23 compiler run_command')
  1801. self.init(testdir)
  1802. def test_identical_target_name_in_subproject_flat_layout(self):
  1803. '''
  1804. Test that identical targets in different subprojects do not collide
  1805. if layout is flat.
  1806. '''
  1807. testdir = os.path.join(self.common_test_dir, '182 identical target name in subproject flat layout')
  1808. self.init(testdir, extra_args=['--layout=flat'])
  1809. self.build()
  1810. def test_identical_target_name_in_subdir_flat_layout(self):
  1811. '''
  1812. Test that identical targets in different subdirs do not collide
  1813. if layout is flat.
  1814. '''
  1815. testdir = os.path.join(self.common_test_dir, '192 same target name flat layout')
  1816. self.init(testdir, extra_args=['--layout=flat'])
  1817. self.build()
  1818. def test_flock(self):
  1819. exception_raised = False
  1820. with tempfile.TemporaryDirectory() as tdir:
  1821. os.mkdir(os.path.join(tdir, 'meson-private'))
  1822. with BuildDirLock(tdir):
  1823. try:
  1824. with BuildDirLock(tdir):
  1825. pass
  1826. except MesonException:
  1827. exception_raised = True
  1828. self.assertTrue(exception_raised, 'Double locking did not raise exception.')
  1829. @unittest.skipIf(is_osx(), 'Test not applicable to OSX')
  1830. def test_check_module_linking(self):
  1831. """
  1832. Test that link_with: a shared module issues a warning
  1833. https://github.com/mesonbuild/meson/issues/2865
  1834. (That an error is raised on OSX is exercised by test failing/78)
  1835. """
  1836. tdir = os.path.join(self.unit_test_dir, '26 shared_mod linking')
  1837. out = self.init(tdir)
  1838. msg = ('''WARNING: target links against shared modules. This is not
  1839. recommended as it is not supported on some platforms''')
  1840. self.assertIn(msg, out)
  1841. def test_ndebug_if_release_disabled(self):
  1842. testdir = os.path.join(self.unit_test_dir, '25 ndebug if-release')
  1843. self.init(testdir, extra_args=['--buildtype=release', '-Db_ndebug=if-release'])
  1844. self.build()
  1845. exe = os.path.join(self.builddir, 'main')
  1846. self.assertEqual(b'NDEBUG=1', subprocess.check_output(exe).strip())
  1847. def test_ndebug_if_release_enabled(self):
  1848. testdir = os.path.join(self.unit_test_dir, '25 ndebug if-release')
  1849. self.init(testdir, extra_args=['--buildtype=debugoptimized', '-Db_ndebug=if-release'])
  1850. self.build()
  1851. exe = os.path.join(self.builddir, 'main')
  1852. self.assertEqual(b'NDEBUG=0', subprocess.check_output(exe).strip())
  1853. def test_guessed_linker_dependencies(self):
  1854. '''
  1855. Test that meson adds dependencies for libraries based on the final
  1856. linker command line.
  1857. '''
  1858. # build library
  1859. testdirbase = os.path.join(self.unit_test_dir, '26 guessed linker dependencies')
  1860. testdirlib = os.path.join(testdirbase, 'lib')
  1861. extra_args = None
  1862. env = Environment(testdirlib, self.builddir, get_fake_options(self.prefix))
  1863. if env.detect_c_compiler(False).get_id() != 'msvc':
  1864. # static libraries are not linkable with -l with msvc because meson installs them
  1865. # as .a files which unix_args_to_native will not know as it expects libraries to use
  1866. # .lib as extension. For a DLL the import library is installed as .lib. Thus for msvc
  1867. # this tests needs to use shared libraries to test the path resolving logic in the
  1868. # dependency generation code path.
  1869. extra_args = ['--default-library', 'static']
  1870. self.init(testdirlib, extra_args=extra_args)
  1871. self.build()
  1872. self.install()
  1873. libbuilddir = self.builddir
  1874. installdir = self.installdir
  1875. libdir = os.path.join(self.installdir, self.prefix.lstrip('/').lstrip('\\'), 'lib')
  1876. # build user of library
  1877. self.new_builddir()
  1878. # replace is needed because meson mangles platform pathes passed via LDFLAGS
  1879. os.environ["LDFLAGS"] = '-L{}'.format(libdir.replace('\\', '/'))
  1880. self.init(os.path.join(testdirbase, 'exe'))
  1881. del os.environ["LDFLAGS"]
  1882. self.build()
  1883. self.assertBuildIsNoop()
  1884. # rebuild library
  1885. exebuilddir = self.builddir
  1886. self.installdir = installdir
  1887. self.builddir = libbuilddir
  1888. # Microsoft's compiler is quite smart about touching import libs on changes,
  1889. # so ensure that there is actually a change in symbols.
  1890. self.setconf('-Dmore_exports=true')
  1891. self.build()
  1892. self.install()
  1893. # no ensure_backend_detects_changes needed because self.setconf did that already
  1894. # assert user of library will be rebuild
  1895. self.builddir = exebuilddir
  1896. self.assertRebuiltTarget('app')
  1897. def test_conflicting_d_dash_option(self):
  1898. testdir = os.path.join(self.unit_test_dir, '30 mixed command line args')
  1899. with self.assertRaises(subprocess.CalledProcessError) as e:
  1900. self.init(testdir, extra_args=['-Dbindir=foo', '--bindir=bar'])
  1901. # Just to ensure that we caught the correct error
  1902. self.assertIn('passed as both', e.stderr)
  1903. def _test_same_option_twice(self, arg, args):
  1904. testdir = os.path.join(self.unit_test_dir, '30 mixed command line args')
  1905. self.init(testdir, extra_args=args)
  1906. opts = self.introspect('--buildoptions')
  1907. for item in opts:
  1908. if item['name'] == arg:
  1909. self.assertEqual(item['value'], 'bar')
  1910. return
  1911. raise Exception('Missing {} value?'.format(arg))
  1912. def test_same_dash_option_twice(self):
  1913. self._test_same_option_twice('bindir', ['--bindir=foo', '--bindir=bar'])
  1914. def test_same_d_option_twice(self):
  1915. self._test_same_option_twice('bindir', ['-Dbindir=foo', '-Dbindir=bar'])
  1916. def test_same_project_d_option_twice(self):
  1917. self._test_same_option_twice('one', ['-Done=foo', '-Done=bar'])
  1918. def _test_same_option_twice_configure(self, arg, args):
  1919. testdir = os.path.join(self.unit_test_dir, '30 mixed command line args')
  1920. self.init(testdir)
  1921. self.setconf(args)
  1922. opts = self.introspect('--buildoptions')
  1923. for item in opts:
  1924. if item['name'] == arg:
  1925. self.assertEqual(item['value'], 'bar')
  1926. return
  1927. raise Exception('Missing {} value?'.format(arg))
  1928. def test_same_dash_option_twice_configure(self):
  1929. self._test_same_option_twice_configure(
  1930. 'bindir', ['--bindir=foo', '--bindir=bar'])
  1931. def test_same_d_option_twice_configure(self):
  1932. self._test_same_option_twice_configure(
  1933. 'bindir', ['-Dbindir=foo', '-Dbindir=bar'])
  1934. def test_same_project_d_option_twice_configure(self):
  1935. self._test_same_option_twice_configure(
  1936. 'one', ['-Done=foo', '-Done=bar'])
  1937. def test_command_line(self):
  1938. testdir = os.path.join(self.unit_test_dir, '30 command line')
  1939. # Verify default values when passing no args
  1940. self.init(testdir)
  1941. obj = mesonbuild.coredata.load(self.builddir)
  1942. self.assertEqual(obj.builtins['default_library'].value, 'static')
  1943. self.assertEqual(obj.builtins['warning_level'].value, '1')
  1944. self.assertEqual(obj.user_options['set_sub_opt'].value, True)
  1945. self.assertEqual(obj.user_options['subp:subp_opt'].value, 'default3')
  1946. self.wipe()
  1947. # warning_level is special, it's --warnlevel instead of --warning-level
  1948. # for historical reasons
  1949. self.init(testdir, extra_args=['--warnlevel=2'])
  1950. obj = mesonbuild.coredata.load(self.builddir)
  1951. self.assertEqual(obj.builtins['warning_level'].value, '2')
  1952. self.setconf('--warnlevel=3')
  1953. obj = mesonbuild.coredata.load(self.builddir)
  1954. self.assertEqual(obj.builtins['warning_level'].value, '3')
  1955. self.wipe()
  1956. # But when using -D syntax, it should be 'warning_level'
  1957. self.init(testdir, extra_args=['-Dwarning_level=2'])
  1958. obj = mesonbuild.coredata.load(self.builddir)
  1959. self.assertEqual(obj.builtins['warning_level'].value, '2')
  1960. self.setconf('-Dwarning_level=3')
  1961. obj = mesonbuild.coredata.load(self.builddir)
  1962. self.assertEqual(obj.builtins['warning_level'].value, '3')
  1963. self.wipe()
  1964. # Mixing --option and -Doption is forbidden
  1965. with self.assertRaises(subprocess.CalledProcessError) as cm:
  1966. self.init(testdir, extra_args=['--warnlevel=1', '-Dwarning_level=3'])
  1967. self.assertNotEqual(0, cm.exception.returncode)
  1968. self.assertIn('as both', cm.exception.output)
  1969. self.init(testdir)
  1970. with self.assertRaises(subprocess.CalledProcessError) as cm:
  1971. self.setconf(['--warnlevel=1', '-Dwarning_level=3'])
  1972. self.assertNotEqual(0, cm.exception.returncode)
  1973. self.assertIn('as both', cm.exception.output)
  1974. self.wipe()
  1975. # --default-library should override default value from project()
  1976. self.init(testdir, extra_args=['--default-library=both'])
  1977. obj = mesonbuild.coredata.load(self.builddir)
  1978. self.assertEqual(obj.builtins['default_library'].value, 'both')
  1979. self.setconf('--default-library=shared')
  1980. obj = mesonbuild.coredata.load(self.builddir)
  1981. self.assertEqual(obj.builtins['default_library'].value, 'shared')
  1982. if self.backend is Backend.ninja:
  1983. # reconfigure target works only with ninja backend
  1984. self.build('reconfigure')
  1985. obj = mesonbuild.coredata.load(self.builddir)
  1986. self.assertEqual(obj.builtins['default_library'].value, 'shared')
  1987. self.wipe()
  1988. # Should warn on unknown options
  1989. out = self.init(testdir, extra_args=['-Dbad=1', '-Dfoo=2', '-Dwrong_link_args=foo'])
  1990. self.assertIn('Unknown options: "bad, foo, wrong_link_args"', out)
  1991. self.wipe()
  1992. # Should fail on malformed option
  1993. with self.assertRaises(subprocess.CalledProcessError) as cm:
  1994. self.init(testdir, extra_args=['-Dfoo'])
  1995. self.assertNotEqual(0, cm.exception.returncode)
  1996. self.assertIn('Option \'foo\' must have a value separated by equals sign.', cm.exception.output)
  1997. self.init(testdir)
  1998. with self.assertRaises(subprocess.CalledProcessError) as cm:
  1999. self.setconf('-Dfoo')
  2000. self.assertNotEqual(0, cm.exception.returncode)
  2001. self.assertIn('Option \'foo\' must have a value separated by equals sign.', cm.exception.output)
  2002. self.wipe()
  2003. # It is not an error to set wrong option for unknown subprojects or
  2004. # language because we don't have control on which one will be selected.
  2005. self.init(testdir, extra_args=['-Dc_wrong=1', '-Dwrong:bad=1', '-Db_wrong=1'])
  2006. self.wipe()
  2007. # Test we can set subproject option
  2008. self.init(testdir, extra_args=['-Dsubp:subp_opt=foo'])
  2009. obj = mesonbuild.coredata.load(self.builddir)
  2010. self.assertEqual(obj.user_options['subp:subp_opt'].value, 'foo')
  2011. self.wipe()
  2012. # c_args value should be parsed with shlex
  2013. self.init(testdir, extra_args=['-Dc_args=foo bar "one two"'])
  2014. obj = mesonbuild.coredata.load(self.builddir)
  2015. self.assertEqual(obj.compiler_options['c_args'].value, ['foo', 'bar', 'one two'])
  2016. self.setconf('-Dc_args="foo bar" one two')
  2017. obj = mesonbuild.coredata.load(self.builddir)
  2018. self.assertEqual(obj.compiler_options['c_args'].value, ['foo bar', 'one', 'two'])
  2019. self.wipe()
  2020. # Setting a 2nd time the same option should override the first value
  2021. try:
  2022. self.init(testdir, extra_args=['--bindir=foo', '--bindir=bar',
  2023. '-Dbuildtype=plain', '-Dbuildtype=release',
  2024. '-Db_sanitize=address', '-Db_sanitize=thread',
  2025. '-Dc_args=foo', '-Dc_args=bar'])
  2026. obj = mesonbuild.coredata.load(self.builddir)
  2027. self.assertEqual(obj.builtins['bindir'].value, 'bar')
  2028. self.assertEqual(obj.builtins['buildtype'].value, 'release')
  2029. self.assertEqual(obj.base_options['b_sanitize'].value, 'thread')
  2030. self.assertEqual(obj.compiler_options['c_args'].value, ['bar'])
  2031. self.setconf(['--bindir=bar', '--bindir=foo',
  2032. '-Dbuildtype=release', '-Dbuildtype=plain',
  2033. '-Db_sanitize=thread', '-Db_sanitize=address',
  2034. '-Dc_args=bar', '-Dc_args=foo'])
  2035. obj = mesonbuild.coredata.load(self.builddir)
  2036. self.assertEqual(obj.builtins['bindir'].value, 'foo')
  2037. self.assertEqual(obj.builtins['buildtype'].value, 'plain')
  2038. self.assertEqual(obj.base_options['b_sanitize'].value, 'address')
  2039. self.assertEqual(obj.compiler_options['c_args'].value, ['foo'])
  2040. self.wipe()
  2041. except KeyError:
  2042. # Ignore KeyError, it happens on CI for compilers that does not
  2043. # support b_sanitize. We have to test with a base option because
  2044. # they used to fail this test with Meson 0.46 an earlier versions.
  2045. pass
  2046. @unittest.skipIf(not os.path.isdir('docs'), 'Doc dir not found, presumably because this is a tarball release.')
  2047. def test_compiler_options_documented(self):
  2048. '''
  2049. Test that C and C++ compiler options and base options are documented in
  2050. Builtin-Options.md. Only tests the default compiler for the current
  2051. platform on the CI.
  2052. '''
  2053. md = None
  2054. with open('docs/markdown/Builtin-options.md') as f:
  2055. md = f.read()
  2056. self.assertIsNotNone(md)
  2057. env = Environment('.', self.builddir, get_fake_options(self.prefix))
  2058. # FIXME: Support other compilers
  2059. cc = env.detect_c_compiler(False)
  2060. cpp = env.detect_cpp_compiler(False)
  2061. for comp in (cc, cpp):
  2062. for opt in comp.get_options().keys():
  2063. self.assertIn(opt, md)
  2064. for opt in comp.base_options:
  2065. self.assertIn(opt, md)
  2066. self.assertNotIn('b_unknown', md)
  2067. @unittest.skipIf(not os.path.isdir('docs'), 'Doc dir not found, presumably because this is a tarball release.')
  2068. def test_cpu_families_documented(self):
  2069. with open("docs/markdown/Reference-tables.md") as f:
  2070. md = f.read()
  2071. self.assertIsNotNone(md)
  2072. sections = list(re.finditer(r"^## (.+)$", md, re.MULTILINE))
  2073. for s1, s2 in zip(sections[::2], sections[1::2]):
  2074. if s1.group(1) == "CPU families":
  2075. # Extract the content for this section
  2076. content = md[s1.end():s2.start()]
  2077. # Find the list entries
  2078. arches = [m.group(1) for m in re.finditer(r"^\| (\w+) +\|", content, re.MULTILINE)]
  2079. # Drop the header
  2080. arches = set(arches[1:])
  2081. self.assertEqual(arches, set(mesonbuild.environment.known_cpu_families))
  2082. class FailureTests(BasePlatformTests):
  2083. '''
  2084. Tests that test failure conditions. Build files here should be dynamically
  2085. generated and static tests should go into `test cases/failing*`.
  2086. This is useful because there can be many ways in which a particular
  2087. function can fail, and creating failing tests for all of them is tedious
  2088. and slows down testing.
  2089. '''
  2090. dnf = "[Dd]ependency.*not found"
  2091. def setUp(self):
  2092. super().setUp()
  2093. self.srcdir = os.path.realpath(tempfile.mkdtemp())
  2094. self.mbuild = os.path.join(self.srcdir, 'meson.build')
  2095. def tearDown(self):
  2096. super().tearDown()
  2097. windows_proof_rmtree(self.srcdir)
  2098. def assertMesonRaises(self, contents, match, extra_args=None, langs=None):
  2099. '''
  2100. Assert that running meson configure on the specified @contents raises
  2101. a error message matching regex @match.
  2102. '''
  2103. if langs is None:
  2104. langs = []
  2105. with open(self.mbuild, 'w') as f:
  2106. f.write("project('failure test', 'c', 'cpp')\n")
  2107. for lang in langs:
  2108. f.write("add_languages('{}', required : false)\n".format(lang))
  2109. f.write(contents)
  2110. # Force tracebacks so we can detect them properly
  2111. os.environ['MESON_FORCE_BACKTRACE'] = '1'
  2112. with self.assertRaisesRegex(MesonException, match, msg=contents):
  2113. # Must run in-process or we'll get a generic CalledProcessError
  2114. self.init(self.srcdir, extra_args=extra_args, inprocess=True)
  2115. def assertMesonOutputs(self, contents, match, extra_args=None, langs=None):
  2116. '''
  2117. Assert that running meson configure on the specified @contents outputs
  2118. something that matches regex @match.
  2119. '''
  2120. if langs is None:
  2121. langs = []
  2122. with open(self.mbuild, 'w') as f:
  2123. f.write("project('output test', 'c', 'cpp')\n")
  2124. for lang in langs:
  2125. f.write("add_languages('{}', required : false)\n".format(lang))
  2126. f.write(contents)
  2127. # Run in-process for speed and consistency with assertMesonRaises
  2128. out = self.init(self.srcdir, extra_args=extra_args, inprocess=True)
  2129. self.assertRegex(out, match)
  2130. @skipIfNoPkgconfig
  2131. def test_dependency(self):
  2132. if subprocess.call(['pkg-config', '--exists', 'zlib']) != 0:
  2133. raise unittest.SkipTest('zlib not found with pkg-config')
  2134. a = (("dependency('zlib', method : 'fail')", "'fail' is invalid"),
  2135. ("dependency('zlib', static : '1')", "[Ss]tatic.*boolean"),
  2136. ("dependency('zlib', version : 1)", "[Vv]ersion.*string or list"),
  2137. ("dependency('zlib', required : 1)", "[Rr]equired.*boolean"),
  2138. ("dependency('zlib', method : 1)", "[Mm]ethod.*string"),
  2139. ("dependency('zlibfail')", self.dnf),)
  2140. for contents, match in a:
  2141. self.assertMesonRaises(contents, match)
  2142. def test_apple_frameworks_dependency(self):
  2143. if not is_osx():
  2144. raise unittest.SkipTest('only run on macOS')
  2145. self.assertMesonRaises("dependency('appleframeworks')",
  2146. "requires at least one module")
  2147. def test_sdl2_notfound_dependency(self):
  2148. # Want to test failure, so skip if available
  2149. if shutil.which('sdl2-config'):
  2150. raise unittest.SkipTest('sdl2-config found')
  2151. self.assertMesonRaises("dependency('sdl2', method : 'sdlconfig')", self.dnf)
  2152. self.assertMesonRaises("dependency('sdl2', method : 'pkg-config')", self.dnf)
  2153. def test_gnustep_notfound_dependency(self):
  2154. # Want to test failure, so skip if available
  2155. if shutil.which('gnustep-config'):
  2156. raise unittest.SkipTest('gnustep-config found')
  2157. self.assertMesonRaises("dependency('gnustep')",
  2158. "(requires a Objc compiler|{})".format(self.dnf),
  2159. langs = ['objc'])
  2160. def test_wx_notfound_dependency(self):
  2161. # Want to test failure, so skip if available
  2162. if shutil.which('wx-config-3.0') or shutil.which('wx-config'):
  2163. raise unittest.SkipTest('wx-config or wx-config-3.0 found')
  2164. self.assertMesonRaises("dependency('wxwidgets')", self.dnf)
  2165. self.assertMesonOutputs("dependency('wxwidgets', required : false)",
  2166. "Dependency .*WxWidgets.* found: .*NO.*")
  2167. def test_wx_dependency(self):
  2168. if not shutil.which('wx-config-3.0') and not shutil.which('wx-config'):
  2169. raise unittest.SkipTest('Neither wx-config nor wx-config-3.0 found')
  2170. self.assertMesonRaises("dependency('wxwidgets', modules : 1)",
  2171. "module argument is not a string")
  2172. def test_llvm_dependency(self):
  2173. self.assertMesonRaises("dependency('llvm', modules : 'fail')",
  2174. "(required.*fail|{})".format(self.dnf))
  2175. def test_boost_notfound_dependency(self):
  2176. # Can be run even if Boost is found or not
  2177. self.assertMesonRaises("dependency('boost', modules : 1)",
  2178. "module.*not a string")
  2179. self.assertMesonRaises("dependency('boost', modules : 'fail')",
  2180. "(fail.*not found|{})".format(self.dnf))
  2181. def test_boost_BOOST_ROOT_dependency(self):
  2182. # Test BOOST_ROOT; can be run even if Boost is found or not
  2183. os.environ['BOOST_ROOT'] = 'relative/path'
  2184. self.assertMesonRaises("dependency('boost')",
  2185. "(BOOST_ROOT.*absolute|{})".format(self.dnf))
  2186. def test_dependency_invalid_method(self):
  2187. code = '''zlib_dep = dependency('zlib', required : false)
  2188. zlib_dep.get_configtool_variable('foo')
  2189. '''
  2190. self.assertMesonRaises(code, "'zlib' is not a config-tool dependency")
  2191. code = '''zlib_dep = dependency('zlib', required : false)
  2192. dep = declare_dependency(dependencies : zlib_dep)
  2193. dep.get_pkgconfig_variable('foo')
  2194. '''
  2195. self.assertMesonRaises(code, "Method.*pkgconfig.*is invalid.*internal")
  2196. code = '''zlib_dep = dependency('zlib', required : false)
  2197. dep = declare_dependency(dependencies : zlib_dep)
  2198. dep.get_configtool_variable('foo')
  2199. '''
  2200. self.assertMesonRaises(code, "Method.*configtool.*is invalid.*internal")
  2201. def test_objc_cpp_detection(self):
  2202. '''
  2203. Test that when we can't detect objc or objcpp, we fail gracefully.
  2204. '''
  2205. env = Environment('', self.builddir, get_fake_options(self.prefix))
  2206. try:
  2207. env.detect_objc_compiler(False)
  2208. env.detect_objcpp_compiler(False)
  2209. except EnvironmentException:
  2210. code = "add_languages('objc')\nadd_languages('objcpp')"
  2211. self.assertMesonRaises(code, "Unknown compiler")
  2212. return
  2213. raise unittest.SkipTest("objc and objcpp found, can't test detection failure")
  2214. def test_subproject_variables(self):
  2215. '''
  2216. Test that:
  2217. 1. The correct message is outputted when a not-required dep is not
  2218. found and the fallback subproject is also not found.
  2219. 2. A not-found not-required dep with a fallback subproject outputs the
  2220. correct message when the fallback subproject is found but the
  2221. variable inside it is not.
  2222. 3. A fallback dependency is found from the subproject parsed in (2)
  2223. 4. A not-required fallback dependency is not found because the
  2224. subproject failed to parse.
  2225. '''
  2226. tdir = os.path.join(self.unit_test_dir, '20 subproj dep variables')
  2227. out = self.init(tdir, inprocess=True)
  2228. self.assertRegex(out, r"Couldn't use fallback subproject "
  2229. "in.*subprojects.*nosubproj.*for the dependency.*somedep")
  2230. self.assertRegex(out, r'Dependency.*somenotfounddep.*from subproject.*'
  2231. 'subprojects.*somesubproj.*found:.*NO')
  2232. self.assertRegex(out, r'Dependency.*zlibproxy.*from subproject.*'
  2233. 'subprojects.*somesubproj.*found:.*YES.*(cached)')
  2234. self.assertRegex(out, r'Couldn\'t use fallback subproject in '
  2235. '.*subprojects.*failingsubproj.*for the dependency.*somedep')
  2236. def test_exception_exit_status(self):
  2237. '''
  2238. Test exit status on python exception
  2239. '''
  2240. tdir = os.path.join(self.unit_test_dir, '21 exit status')
  2241. os.environ['MESON_UNIT_TEST'] = '1'
  2242. with self.assertRaises(subprocess.CalledProcessError) as cm:
  2243. self.init(tdir, inprocess=False)
  2244. self.assertEqual(cm.exception.returncode, 2)
  2245. self.wipe()
  2246. def test_dict_requires_key_value_pairs(self):
  2247. self.assertMesonRaises("dict = {3, 'foo': 'bar'}",
  2248. 'Only key:value pairs are valid in dict construction.')
  2249. self.assertMesonRaises("{'foo': 'bar', 3}",
  2250. 'Only key:value pairs are valid in dict construction.')
  2251. def test_dict_forbids_duplicate_keys(self):
  2252. self.assertMesonRaises("dict = {'a': 41, 'a': 42}",
  2253. 'Duplicate dictionary key: a.*')
  2254. def test_dict_forbids_integer_key(self):
  2255. self.assertMesonRaises("dict = {3: 'foo'}",
  2256. 'Key must be a string.*')
  2257. class WindowsTests(BasePlatformTests):
  2258. '''
  2259. Tests that should run on Cygwin, MinGW, and MSVC
  2260. '''
  2261. def setUp(self):
  2262. super().setUp()
  2263. self.platform_test_dir = os.path.join(self.src_root, 'test cases/windows')
  2264. @unittest.skipIf(is_cygwin(), 'Test only applicable to Windows')
  2265. def test_find_program(self):
  2266. '''
  2267. Test that Windows-specific edge-cases in find_program are functioning
  2268. correctly. Cannot be an ordinary test because it involves manipulating
  2269. PATH to point to a directory with Python scripts.
  2270. '''
  2271. testdir = os.path.join(self.platform_test_dir, '9 find program')
  2272. # Find `cmd` and `cmd.exe`
  2273. prog1 = ExternalProgram('cmd')
  2274. self.assertTrue(prog1.found(), msg='cmd not found')
  2275. prog2 = ExternalProgram('cmd.exe')
  2276. self.assertTrue(prog2.found(), msg='cmd.exe not found')
  2277. self.assertPathEqual(prog1.get_path(), prog2.get_path())
  2278. # Find cmd with an absolute path that's missing the extension
  2279. cmd_path = prog2.get_path()[:-4]
  2280. prog = ExternalProgram(cmd_path)
  2281. self.assertTrue(prog.found(), msg='{!r} not found'.format(cmd_path))
  2282. # Finding a script with no extension inside a directory works
  2283. prog = ExternalProgram(os.path.join(testdir, 'test-script'))
  2284. self.assertTrue(prog.found(), msg='test-script not found')
  2285. # Finding a script with an extension inside a directory works
  2286. prog = ExternalProgram(os.path.join(testdir, 'test-script-ext.py'))
  2287. self.assertTrue(prog.found(), msg='test-script-ext.py not found')
  2288. # Finding a script in PATH w/o extension works and adds the interpreter
  2289. os.environ['PATH'] += os.pathsep + testdir
  2290. prog = ExternalProgram('test-script-ext')
  2291. self.assertTrue(prog.found(), msg='test-script-ext not found in PATH')
  2292. self.assertPathEqual(prog.get_command()[0], python_command[0])
  2293. self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
  2294. # Finding a script in PATH with extension works and adds the interpreter
  2295. prog = ExternalProgram('test-script-ext.py')
  2296. self.assertTrue(prog.found(), msg='test-script-ext.py not found in PATH')
  2297. self.assertPathEqual(prog.get_command()[0], python_command[0])
  2298. self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
  2299. def test_ignore_libs(self):
  2300. '''
  2301. Test that find_library on libs that are to be ignored returns an empty
  2302. array of arguments. Must be a unit test because we cannot inspect
  2303. ExternalLibraryHolder from build files.
  2304. '''
  2305. testdir = os.path.join(self.platform_test_dir, '1 basic')
  2306. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  2307. cc = env.detect_c_compiler(False)
  2308. if cc.id != 'msvc':
  2309. raise unittest.SkipTest('Not using MSVC')
  2310. # To force people to update this test, and also test
  2311. self.assertEqual(set(cc.ignore_libs), {'c', 'm', 'pthread'})
  2312. for l in cc.ignore_libs:
  2313. self.assertEqual(cc.find_library(l, env, []), [])
  2314. def test_rc_depends_files(self):
  2315. testdir = os.path.join(self.platform_test_dir, '5 resources')
  2316. # resource compiler depfile generation is not yet implemented for msvc
  2317. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  2318. depfile_works = env.detect_c_compiler(False).get_id() != 'msvc'
  2319. self.init(testdir)
  2320. self.build()
  2321. # Immediately rebuilding should not do anything
  2322. self.assertBuildIsNoop()
  2323. # Test compile_resources(depend_file:)
  2324. # Changing mtime of sample.ico should rebuild prog
  2325. self.utime(os.path.join(testdir, 'res', 'sample.ico'))
  2326. self.assertRebuiltTarget('prog')
  2327. # Test depfile generation by compile_resources
  2328. # Changing mtime of resource.h should rebuild myres.rc and then prog
  2329. if depfile_works:
  2330. self.utime(os.path.join(testdir, 'inc', 'resource', 'resource.h'))
  2331. self.assertRebuiltTarget('prog')
  2332. self.wipe()
  2333. if depfile_works:
  2334. testdir = os.path.join(self.platform_test_dir, '13 resources with custom targets')
  2335. self.init(testdir)
  2336. self.build()
  2337. # Immediately rebuilding should not do anything
  2338. self.assertBuildIsNoop()
  2339. # Changing mtime of resource.h should rebuild myres_1.rc and then prog_1
  2340. self.utime(os.path.join(testdir, 'res', 'resource.h'))
  2341. self.assertRebuiltTarget('prog_1')
  2342. class LinuxlikeTests(BasePlatformTests):
  2343. '''
  2344. Tests that should run on Linux and *BSD
  2345. '''
  2346. def test_basic_soname(self):
  2347. '''
  2348. Test that the soname is set correctly for shared libraries. This can't
  2349. be an ordinary test case because we need to run `readelf` and actually
  2350. check the soname.
  2351. https://github.com/mesonbuild/meson/issues/785
  2352. '''
  2353. testdir = os.path.join(self.common_test_dir, '4 shared')
  2354. self.init(testdir)
  2355. self.build()
  2356. lib1 = os.path.join(self.builddir, 'libmylib.so')
  2357. soname = get_soname(lib1)
  2358. self.assertEqual(soname, 'libmylib.so')
  2359. def test_custom_soname(self):
  2360. '''
  2361. Test that the soname is set correctly for shared libraries when
  2362. a custom prefix and/or suffix is used. This can't be an ordinary test
  2363. case because we need to run `readelf` and actually check the soname.
  2364. https://github.com/mesonbuild/meson/issues/785
  2365. '''
  2366. testdir = os.path.join(self.common_test_dir, '27 library versions')
  2367. self.init(testdir)
  2368. self.build()
  2369. lib1 = os.path.join(self.builddir, 'prefixsomelib.suffix')
  2370. soname = get_soname(lib1)
  2371. self.assertEqual(soname, 'prefixsomelib.suffix')
  2372. def test_pic(self):
  2373. '''
  2374. Test that -fPIC is correctly added to static libraries when b_staticpic
  2375. is true and not when it is false. This can't be an ordinary test case
  2376. because we need to inspect the compiler database.
  2377. '''
  2378. if is_cygwin() or is_osx():
  2379. raise unittest.SkipTest('PIC not relevant')
  2380. testdir = os.path.join(self.common_test_dir, '3 static')
  2381. self.init(testdir)
  2382. compdb = self.get_compdb()
  2383. self.assertIn('-fPIC', compdb[0]['command'])
  2384. self.setconf('-Db_staticpic=false')
  2385. # Regenerate build
  2386. self.build()
  2387. compdb = self.get_compdb()
  2388. self.assertNotIn('-fPIC', compdb[0]['command'])
  2389. def test_pkgconfig_gen(self):
  2390. '''
  2391. Test that generated pkg-config files can be found and have the correct
  2392. version and link args. This can't be an ordinary test case because we
  2393. need to run pkg-config outside of a Meson build file.
  2394. https://github.com/mesonbuild/meson/issues/889
  2395. '''
  2396. testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
  2397. self.init(testdir)
  2398. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  2399. kwargs = {'required': True, 'silent': True}
  2400. os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
  2401. foo_dep = PkgConfigDependency('libfoo', env, kwargs)
  2402. self.assertTrue(foo_dep.found())
  2403. self.assertEqual(foo_dep.get_version(), '1.0')
  2404. self.assertIn('-lfoo', foo_dep.get_link_args())
  2405. self.assertEqual(foo_dep.get_pkgconfig_variable('foo', {}), 'bar')
  2406. self.assertPathEqual(foo_dep.get_pkgconfig_variable('datadir', {}), '/usr/data')
  2407. def test_pkgconfig_gen_deps(self):
  2408. '''
  2409. Test that generated pkg-config files correctly handle dependencies
  2410. '''
  2411. testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
  2412. self.init(testdir)
  2413. privatedir1 = self.privatedir
  2414. self.new_builddir()
  2415. os.environ['PKG_CONFIG_LIBDIR'] = privatedir1
  2416. testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen', 'dependencies')
  2417. self.init(testdir)
  2418. privatedir2 = self.privatedir
  2419. os.environ['PKG_CONFIG_LIBDIR'] = os.pathsep.join([privatedir1, privatedir2])
  2420. cmd = ['pkg-config', 'dependency-test']
  2421. out = self._run(cmd + ['--print-requires']).strip().split('\n')
  2422. self.assertEqual(sorted(out), sorted(['libexposed']))
  2423. out = self._run(cmd + ['--print-requires-private']).strip().split('\n')
  2424. self.assertEqual(sorted(out), sorted(['libfoo >= 1.0']))
  2425. out = self._run(cmd + ['--cflags-only-other']).strip().split()
  2426. self.assertEqual(sorted(out), sorted(['-pthread', '-DCUSTOM']))
  2427. out = self._run(cmd + ['--libs-only-l', '--libs-only-other']).strip().split()
  2428. self.assertEqual(sorted(out), sorted(['-pthread', '-lcustom',
  2429. '-llibmain', '-llibexposed']))
  2430. out = self._run(cmd + ['--libs-only-l', '--libs-only-other', '--static']).strip().split()
  2431. self.assertEqual(sorted(out), sorted(['-pthread', '-lcustom',
  2432. '-llibmain', '-llibexposed',
  2433. '-llibinternal', '-lcustom2',
  2434. '-lfoo']))
  2435. cmd = ['pkg-config', 'requires-test']
  2436. out = self._run(cmd + ['--print-requires']).strip().split('\n')
  2437. self.assertEqual(sorted(out), sorted(['libexposed', 'libfoo >= 1.0', 'libhello']))
  2438. cmd = ['pkg-config', 'requires-private-test']
  2439. out = self._run(cmd + ['--print-requires-private']).strip().split('\n')
  2440. self.assertEqual(sorted(out), sorted(['libexposed', 'libfoo >= 1.0', 'libhello']))
  2441. def test_pkg_unfound(self):
  2442. testdir = os.path.join(self.unit_test_dir, '22 unfound pkgconfig')
  2443. self.init(testdir)
  2444. with open(os.path.join(self.privatedir, 'somename.pc')) as f:
  2445. pcfile = f.read()
  2446. self.assertFalse('blub_blob_blib' in pcfile)
  2447. def test_vala_c_warnings(self):
  2448. '''
  2449. Test that no warnings are emitted for C code generated by Vala. This
  2450. can't be an ordinary test case because we need to inspect the compiler
  2451. database.
  2452. https://github.com/mesonbuild/meson/issues/864
  2453. '''
  2454. if not shutil.which('valac'):
  2455. raise unittest.SkipTest('valac not installed.')
  2456. testdir = os.path.join(self.vala_test_dir, '5 target glib')
  2457. self.init(testdir)
  2458. compdb = self.get_compdb()
  2459. vala_command = None
  2460. c_command = None
  2461. for each in compdb:
  2462. if each['file'].endswith('GLib.Thread.c'):
  2463. vala_command = each['command']
  2464. elif each['file'].endswith('GLib.Thread.vala'):
  2465. continue
  2466. elif each['file'].endswith('retcode.c'):
  2467. c_command = each['command']
  2468. else:
  2469. m = 'Unknown file {!r} in vala_c_warnings test'.format(each['file'])
  2470. raise AssertionError(m)
  2471. self.assertIsNotNone(vala_command)
  2472. self.assertIsNotNone(c_command)
  2473. # -w suppresses all warnings, should be there in Vala but not in C
  2474. self.assertIn(" -w ", vala_command)
  2475. self.assertNotIn(" -w ", c_command)
  2476. # -Wall enables all warnings, should be there in C but not in Vala
  2477. self.assertNotIn(" -Wall ", vala_command)
  2478. self.assertIn(" -Wall ", c_command)
  2479. # -Werror converts warnings to errors, should always be there since it's
  2480. # injected by an unrelated piece of code and the project has werror=true
  2481. self.assertIn(" -Werror ", vala_command)
  2482. self.assertIn(" -Werror ", c_command)
  2483. @skipIfNoPkgconfig
  2484. def test_qt5dependency_pkgconfig_detection(self):
  2485. '''
  2486. Test that qt4 and qt5 detection with pkgconfig works.
  2487. '''
  2488. # Verify Qt4 or Qt5 can be found with pkg-config
  2489. qt4 = subprocess.call(['pkg-config', '--exists', 'QtCore'])
  2490. qt5 = subprocess.call(['pkg-config', '--exists', 'Qt5Core'])
  2491. if qt4 != 0 or qt5 != 0:
  2492. raise unittest.SkipTest('Qt not found with pkg-config')
  2493. testdir = os.path.join(self.framework_test_dir, '4 qt')
  2494. self.init(testdir, ['-Dmethod=pkg-config'])
  2495. # Confirm that the dependency was found with qmake
  2496. msg = 'Qt4 native `pkg-config` dependency (modules: Core, Gui) found: YES\n'
  2497. msg2 = 'Qt5 native `pkg-config` dependency (modules: Core, Gui) found: YES\n'
  2498. mesonlog = self.get_meson_log()
  2499. self.assertTrue(msg in mesonlog or msg2 in mesonlog)
  2500. def test_qt5dependency_qmake_detection(self):
  2501. '''
  2502. Test that qt5 detection with qmake works. This can't be an ordinary
  2503. test case because it involves setting the environment.
  2504. '''
  2505. # Verify that qmake is for Qt5
  2506. if not shutil.which('qmake-qt5'):
  2507. if not shutil.which('qmake'):
  2508. raise unittest.SkipTest('QMake not found')
  2509. output = subprocess.getoutput('qmake --version')
  2510. if 'Qt version 5' not in output:
  2511. raise unittest.SkipTest('Qmake found, but it is not for Qt 5.')
  2512. # Disable pkg-config codepath and force searching with qmake/qmake-qt5
  2513. testdir = os.path.join(self.framework_test_dir, '4 qt')
  2514. self.init(testdir, ['-Dmethod=qmake'])
  2515. # Confirm that the dependency was found with qmake
  2516. msg = 'Qt5 native `qmake-qt5` dependency (modules: Core) found: YES\n'
  2517. msg2 = 'Qt5 native `qmake` dependency (modules: Core) found: YES\n'
  2518. mesonlog = self.get_meson_log()
  2519. self.assertTrue(msg in mesonlog or msg2 in mesonlog)
  2520. def _test_soname_impl(self, libpath, install):
  2521. if is_cygwin() or is_osx():
  2522. raise unittest.SkipTest('Test only applicable to ELF and linuxlike sonames')
  2523. testdir = os.path.join(self.unit_test_dir, '1 soname')
  2524. self.init(testdir)
  2525. self.build()
  2526. if install:
  2527. self.install()
  2528. # File without aliases set.
  2529. nover = os.path.join(libpath, 'libnover.so')
  2530. self.assertPathExists(nover)
  2531. self.assertFalse(os.path.islink(nover))
  2532. self.assertEqual(get_soname(nover), 'libnover.so')
  2533. self.assertEqual(len(glob(nover[:-3] + '*')), 1)
  2534. # File with version set
  2535. verset = os.path.join(libpath, 'libverset.so')
  2536. self.assertPathExists(verset + '.4.5.6')
  2537. self.assertEqual(os.readlink(verset), 'libverset.so.4')
  2538. self.assertEqual(get_soname(verset), 'libverset.so.4')
  2539. self.assertEqual(len(glob(verset[:-3] + '*')), 3)
  2540. # File with soversion set
  2541. soverset = os.path.join(libpath, 'libsoverset.so')
  2542. self.assertPathExists(soverset + '.1.2.3')
  2543. self.assertEqual(os.readlink(soverset), 'libsoverset.so.1.2.3')
  2544. self.assertEqual(get_soname(soverset), 'libsoverset.so.1.2.3')
  2545. self.assertEqual(len(glob(soverset[:-3] + '*')), 2)
  2546. # File with version and soversion set to same values
  2547. settosame = os.path.join(libpath, 'libsettosame.so')
  2548. self.assertPathExists(settosame + '.7.8.9')
  2549. self.assertEqual(os.readlink(settosame), 'libsettosame.so.7.8.9')
  2550. self.assertEqual(get_soname(settosame), 'libsettosame.so.7.8.9')
  2551. self.assertEqual(len(glob(settosame[:-3] + '*')), 2)
  2552. # File with version and soversion set to different values
  2553. bothset = os.path.join(libpath, 'libbothset.so')
  2554. self.assertPathExists(bothset + '.1.2.3')
  2555. self.assertEqual(os.readlink(bothset), 'libbothset.so.1.2.3')
  2556. self.assertEqual(os.readlink(bothset + '.1.2.3'), 'libbothset.so.4.5.6')
  2557. self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3')
  2558. self.assertEqual(len(glob(bothset[:-3] + '*')), 3)
  2559. def test_soname(self):
  2560. self._test_soname_impl(self.builddir, False)
  2561. def test_installed_soname(self):
  2562. libdir = self.installdir + os.path.join(self.prefix, self.libdir)
  2563. self._test_soname_impl(libdir, True)
  2564. def test_compiler_check_flags_order(self):
  2565. '''
  2566. Test that compiler check flags override all other flags. This can't be
  2567. an ordinary test case because it needs the environment to be set.
  2568. '''
  2569. Oflag = '-O3'
  2570. os.environ['CFLAGS'] = os.environ['CXXFLAGS'] = Oflag
  2571. testdir = os.path.join(self.common_test_dir, '43 has function')
  2572. self.init(testdir)
  2573. cmds = self.get_meson_log_compiler_checks()
  2574. for cmd in cmds:
  2575. if cmd[0] == 'ccache':
  2576. cmd = cmd[1:]
  2577. # Verify that -I flags from the `args` kwarg are first
  2578. # This is set in the '43 has function' test case
  2579. self.assertEqual(cmd[1], '-I/tmp')
  2580. # Verify that -O3 set via the environment is overridden by -O0
  2581. Oargs = [arg for arg in cmd if arg.startswith('-O')]
  2582. self.assertEqual(Oargs, [Oflag, '-O0'])
  2583. def _test_stds_impl(self, testdir, compiler, p):
  2584. lang_std = p + '_std'
  2585. # Check that all the listed -std=xxx options for this compiler work
  2586. # just fine when used
  2587. for v in compiler.get_options()[lang_std].choices:
  2588. if (compiler.get_id() == 'clang' and '17' in v and
  2589. (version_compare(compiler.version, '<5.0.0') or
  2590. (compiler.clang_type == mesonbuild.compilers.CLANG_OSX and version_compare(compiler.version, '<9.2')))):
  2591. continue
  2592. std_opt = '{}={}'.format(lang_std, v)
  2593. self.init(testdir, ['-D' + std_opt])
  2594. cmd = self.get_compdb()[0]['command']
  2595. if v != 'none':
  2596. cmd_std = " -std={} ".format(v)
  2597. self.assertIn(cmd_std, cmd)
  2598. try:
  2599. self.build()
  2600. except:
  2601. print('{} was {!r}'.format(lang_std, v))
  2602. raise
  2603. self.wipe()
  2604. # Check that an invalid std option in CFLAGS/CPPFLAGS fails
  2605. # Needed because by default ICC ignores invalid options
  2606. cmd_std = '-std=FAIL'
  2607. env_flags = p.upper() + 'FLAGS'
  2608. os.environ[env_flags] = cmd_std
  2609. self.init(testdir)
  2610. cmd = self.get_compdb()[0]['command']
  2611. qcmd_std = " {} ".format(cmd_std)
  2612. self.assertIn(qcmd_std, cmd)
  2613. with self.assertRaises(subprocess.CalledProcessError,
  2614. msg='{} should have failed'.format(qcmd_std)):
  2615. self.build()
  2616. def test_compiler_c_stds(self):
  2617. '''
  2618. Test that C stds specified for this compiler can all be used. Can't be
  2619. an ordinary test because it requires passing options to meson.
  2620. '''
  2621. testdir = os.path.join(self.common_test_dir, '1 trivial')
  2622. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  2623. cc = env.detect_c_compiler(False)
  2624. self._test_stds_impl(testdir, cc, 'c')
  2625. def test_compiler_cpp_stds(self):
  2626. '''
  2627. Test that C++ stds specified for this compiler can all be used. Can't
  2628. be an ordinary test because it requires passing options to meson.
  2629. '''
  2630. testdir = os.path.join(self.common_test_dir, '2 cpp')
  2631. env = Environment(testdir, self.builddir, get_fake_options(self.prefix))
  2632. cpp = env.detect_cpp_compiler(False)
  2633. self._test_stds_impl(testdir, cpp, 'cpp')
  2634. def test_unity_subproj(self):
  2635. testdir = os.path.join(self.common_test_dir, '49 subproject')
  2636. self.init(testdir, extra_args='--unity=subprojects')
  2637. self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/subprojects@sublib@@simpletest@exe/simpletest-unity.c'))
  2638. self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/subprojects@sublib@@sublib@sha/sublib-unity.c'))
  2639. self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c'))
  2640. self.build()
  2641. def test_installed_modes(self):
  2642. '''
  2643. Test that files installed by these tests have the correct permissions.
  2644. Can't be an ordinary test because our installed_files.txt is very basic.
  2645. '''
  2646. # Test file modes
  2647. testdir = os.path.join(self.common_test_dir, '12 data')
  2648. self.init(testdir)
  2649. self.install()
  2650. f = os.path.join(self.installdir, 'etc', 'etcfile.dat')
  2651. found_mode = stat.filemode(os.stat(f).st_mode)
  2652. want_mode = 'rw------T'
  2653. self.assertEqual(want_mode, found_mode[1:])
  2654. f = os.path.join(self.installdir, 'usr', 'bin', 'runscript.sh')
  2655. statf = os.stat(f)
  2656. found_mode = stat.filemode(statf.st_mode)
  2657. want_mode = 'rwxr-sr-x'
  2658. self.assertEqual(want_mode, found_mode[1:])
  2659. if os.getuid() == 0:
  2660. # The chown failed nonfatally if we're not root
  2661. self.assertEqual(0, statf.st_uid)
  2662. self.assertEqual(0, statf.st_gid)
  2663. f = os.path.join(self.installdir, 'usr', 'share', 'progname',
  2664. 'fileobject_datafile.dat')
  2665. orig = os.path.join(testdir, 'fileobject_datafile.dat')
  2666. statf = os.stat(f)
  2667. statorig = os.stat(orig)
  2668. found_mode = stat.filemode(statf.st_mode)
  2669. orig_mode = stat.filemode(statorig.st_mode)
  2670. self.assertEqual(orig_mode[1:], found_mode[1:])
  2671. self.assertEqual(os.getuid(), statf.st_uid)
  2672. if os.getuid() == 0:
  2673. # The chown failed nonfatally if we're not root
  2674. self.assertEqual(0, statf.st_gid)
  2675. self.wipe()
  2676. # Test directory modes
  2677. testdir = os.path.join(self.common_test_dir, '66 install subdir')
  2678. self.init(testdir)
  2679. self.install()
  2680. f = os.path.join(self.installdir, 'usr', 'share', 'sub1', 'second.dat')
  2681. statf = os.stat(f)
  2682. found_mode = stat.filemode(statf.st_mode)
  2683. want_mode = 'rwxr-x--t'
  2684. self.assertEqual(want_mode, found_mode[1:])
  2685. if os.getuid() == 0:
  2686. # The chown failed nonfatally if we're not root
  2687. self.assertEqual(0, statf.st_uid)
  2688. def test_installed_modes_extended(self):
  2689. '''
  2690. Test that files are installed with correct permissions using install_mode.
  2691. '''
  2692. testdir = os.path.join(self.common_test_dir, '201 install_mode')
  2693. self.init(testdir)
  2694. self.build()
  2695. self.install()
  2696. for fsobj, want_mode in [
  2697. ('bin', 'drwxr-x---'),
  2698. ('bin/runscript.sh', '-rwxr-sr-x'),
  2699. ('bin/trivialprog', '-rwxr-sr-x'),
  2700. ('include', 'drwxr-x---'),
  2701. ('include/config.h', '-rw-rwSr--'),
  2702. ('include/rootdir.h', '-r--r--r-T'),
  2703. ('lib', 'drwxr-x---'),
  2704. ('lib/libstat.a', '-rw---Sr--'),
  2705. ('share', 'drwxr-x---'),
  2706. ('share/man', 'drwxr-x---'),
  2707. ('share/man/man1', 'drwxr-x---'),
  2708. ('share/man/man1/foo.1.gz', '-r--r--r-T'),
  2709. ('share/sub1', 'drwxr-x---'),
  2710. ('share/sub1/second.dat', '-rwxr-x--t'),
  2711. ('subdir', 'drwxr-x---'),
  2712. ('subdir/data.dat', '-rw-rwSr--'),
  2713. ]:
  2714. f = os.path.join(self.installdir, 'usr', *fsobj.split('/'))
  2715. found_mode = stat.filemode(os.stat(f).st_mode)
  2716. self.assertEqual(want_mode, found_mode,
  2717. msg=('Expected file %s to have mode %s but found %s instead.' %
  2718. (fsobj, want_mode, found_mode)))
  2719. # Ensure that introspect --installed works on all types of files
  2720. # FIXME: also verify the files list
  2721. self.introspect('--installed')
  2722. def test_install_umask(self):
  2723. '''
  2724. Test that files are installed with correct permissions using default
  2725. install umask of 022, regardless of the umask at time the worktree
  2726. was checked out or the build was executed.
  2727. '''
  2728. # Copy source tree to a temporary directory and change permissions
  2729. # there to simulate a checkout with umask 002.
  2730. orig_testdir = os.path.join(self.unit_test_dir, '24 install umask')
  2731. # Create a new testdir under tmpdir.
  2732. tmpdir = os.path.realpath(tempfile.mkdtemp())
  2733. self.addCleanup(windows_proof_rmtree, tmpdir)
  2734. testdir = os.path.join(tmpdir, '24 install umask')
  2735. # Copy the tree using shutil.copyfile, which will use the current umask
  2736. # instead of preserving permissions of the old tree.
  2737. save_umask = os.umask(0o002)
  2738. self.addCleanup(os.umask, save_umask)
  2739. shutil.copytree(orig_testdir, testdir, copy_function=shutil.copyfile)
  2740. # Preserve the executable status of subdir/sayhello though.
  2741. os.chmod(os.path.join(testdir, 'subdir', 'sayhello'), 0o775)
  2742. self.init(testdir)
  2743. # Run the build under a 027 umask now.
  2744. os.umask(0o027)
  2745. self.build()
  2746. # And keep umask 027 for the install step too.
  2747. self.install()
  2748. for executable in [
  2749. 'bin/prog',
  2750. 'share/subdir/sayhello',
  2751. ]:
  2752. f = os.path.join(self.installdir, 'usr', *executable.split('/'))
  2753. found_mode = stat.filemode(os.stat(f).st_mode)
  2754. want_mode = '-rwxr-xr-x'
  2755. self.assertEqual(want_mode, found_mode,
  2756. msg=('Expected file %s to have mode %s but found %s instead.' %
  2757. (executable, want_mode, found_mode)))
  2758. for directory in [
  2759. 'usr',
  2760. 'usr/bin',
  2761. 'usr/include',
  2762. 'usr/share',
  2763. 'usr/share/man',
  2764. 'usr/share/man/man1',
  2765. 'usr/share/subdir',
  2766. ]:
  2767. f = os.path.join(self.installdir, *directory.split('/'))
  2768. found_mode = stat.filemode(os.stat(f).st_mode)
  2769. want_mode = 'drwxr-xr-x'
  2770. self.assertEqual(want_mode, found_mode,
  2771. msg=('Expected directory %s to have mode %s but found %s instead.' %
  2772. (directory, want_mode, found_mode)))
  2773. for datafile in [
  2774. 'include/sample.h',
  2775. 'share/datafile.cat',
  2776. 'share/file.dat',
  2777. 'share/man/man1/prog.1.gz',
  2778. 'share/subdir/datafile.dog',
  2779. ]:
  2780. f = os.path.join(self.installdir, 'usr', *datafile.split('/'))
  2781. found_mode = stat.filemode(os.stat(f).st_mode)
  2782. want_mode = '-rw-r--r--'
  2783. self.assertEqual(want_mode, found_mode,
  2784. msg=('Expected file %s to have mode %s but found %s instead.' %
  2785. (datafile, want_mode, found_mode)))
  2786. def test_cpp_std_override(self):
  2787. testdir = os.path.join(self.unit_test_dir, '6 std override')
  2788. self.init(testdir)
  2789. compdb = self.get_compdb()
  2790. for i in compdb:
  2791. if 'prog03' in i['file']:
  2792. c03_comp = i['command']
  2793. if 'prog11' in i['file']:
  2794. c11_comp = i['command']
  2795. if 'progp' in i['file']:
  2796. plain_comp = i['command']
  2797. self.assertNotEqual(len(plain_comp), 0)
  2798. self.assertIn('-std=c++03', c03_comp)
  2799. self.assertNotIn('-std=c++11', c03_comp)
  2800. self.assertIn('-std=c++11', c11_comp)
  2801. self.assertNotIn('-std=c++03', c11_comp)
  2802. self.assertNotIn('-std=c++03', plain_comp)
  2803. self.assertNotIn('-std=c++11', plain_comp)
  2804. # Now werror
  2805. self.assertIn('-Werror', plain_comp)
  2806. self.assertNotIn('-Werror', c03_comp)
  2807. def test_run_installed(self):
  2808. if is_cygwin() or is_osx():
  2809. raise unittest.SkipTest('LD_LIBRARY_PATH and RPATH not applicable')
  2810. testdir = os.path.join(self.unit_test_dir, '7 run installed')
  2811. self.init(testdir)
  2812. self.build()
  2813. self.install()
  2814. installed_exe = os.path.join(self.installdir, 'usr/bin/prog')
  2815. installed_libdir = os.path.join(self.installdir, 'usr/foo')
  2816. installed_lib = os.path.join(installed_libdir, 'libfoo.so')
  2817. self.assertTrue(os.path.isfile(installed_exe))
  2818. self.assertTrue(os.path.isdir(installed_libdir))
  2819. self.assertTrue(os.path.isfile(installed_lib))
  2820. # Must fail when run without LD_LIBRARY_PATH to ensure that
  2821. # rpath has been properly stripped rather than pointing to the builddir.
  2822. self.assertNotEqual(subprocess.call(installed_exe, stderr=subprocess.DEVNULL), 0)
  2823. # When LD_LIBRARY_PATH is set it should start working.
  2824. # For some reason setting LD_LIBRARY_PATH in os.environ fails
  2825. # when all tests are run (but works when only this test is run),
  2826. # but doing this explicitly works.
  2827. env = os.environ.copy()
  2828. env['LD_LIBRARY_PATH'] = installed_libdir
  2829. self.assertEqual(subprocess.call(installed_exe, env=env), 0)
  2830. # Ensure that introspect --installed works
  2831. installed = self.introspect('--installed')
  2832. for v in installed.values():
  2833. self.assertTrue('prog' in v or 'foo' in v)
  2834. def test_order_of_l_arguments(self):
  2835. testdir = os.path.join(self.unit_test_dir, '9 -L -l order')
  2836. os.environ['PKG_CONFIG_PATH'] = testdir
  2837. self.init(testdir)
  2838. # NOTE: .pc file has -Lfoo -lfoo -Lbar -lbar but pkg-config reorders
  2839. # the flags before returning them to -Lfoo -Lbar -lfoo -lbar
  2840. # but pkgconf seems to not do that. Sigh. Support both.
  2841. expected_order = [('-L/me/first', '-lfoo1'),
  2842. ('-L/me/second', '-lfoo2'),
  2843. ('-L/me/first', '-L/me/second'),
  2844. ('-lfoo1', '-lfoo2'),
  2845. ('-L/me/second', '-L/me/third'),
  2846. ('-L/me/third', '-L/me/fourth',),
  2847. ('-L/me/third', '-lfoo3'),
  2848. ('-L/me/fourth', '-lfoo4'),
  2849. ('-lfoo3', '-lfoo4'),
  2850. ]
  2851. with open(os.path.join(self.builddir, 'build.ninja')) as ifile:
  2852. for line in ifile:
  2853. if expected_order[0][0] in line:
  2854. for first, second in expected_order:
  2855. self.assertLess(line.index(first), line.index(second))
  2856. return
  2857. raise RuntimeError('Linker entries not found in the Ninja file.')
  2858. def test_introspect_dependencies(self):
  2859. '''
  2860. Tests that mesonintrospect --dependencies returns expected output.
  2861. '''
  2862. testdir = os.path.join(self.framework_test_dir, '7 gnome')
  2863. self.init(testdir)
  2864. glib_found = False
  2865. gobject_found = False
  2866. deps = self.introspect('--dependencies')
  2867. self.assertIsInstance(deps, list)
  2868. for dep in deps:
  2869. self.assertIsInstance(dep, dict)
  2870. self.assertIn('name', dep)
  2871. self.assertIn('compile_args', dep)
  2872. self.assertIn('link_args', dep)
  2873. if dep['name'] == 'glib-2.0':
  2874. glib_found = True
  2875. elif dep['name'] == 'gobject-2.0':
  2876. gobject_found = True
  2877. self.assertTrue(glib_found)
  2878. self.assertTrue(gobject_found)
  2879. if subprocess.call(['pkg-config', '--exists', 'glib-2.0 >= 2.56.2']) != 0:
  2880. raise unittest.SkipTest('glib >= 2.56.2 needed for the rest')
  2881. targets = self.introspect('--targets')
  2882. docbook_target = None
  2883. for t in targets:
  2884. if t['name'] == 'generated-gdbus-docbook':
  2885. docbook_target = t
  2886. break
  2887. self.assertIsInstance(docbook_target, dict)
  2888. ifile = self.introspect(['--target-files', 'generated-gdbus-docbook@cus'])[0]
  2889. self.assertEqual(t['filename'], 'gdbus/generated-gdbus-doc-' + ifile)
  2890. def test_build_rpath(self):
  2891. if is_cygwin():
  2892. raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH')
  2893. testdir = os.path.join(self.unit_test_dir, '11 build_rpath')
  2894. self.init(testdir)
  2895. self.build()
  2896. # C program RPATH
  2897. build_rpath = get_rpath(os.path.join(self.builddir, 'prog'))
  2898. self.assertEqual(build_rpath, '$ORIGIN/sub:/foo/bar')
  2899. self.install()
  2900. install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/prog'))
  2901. self.assertEqual(install_rpath, '/baz')
  2902. # C++ program RPATH
  2903. build_rpath = get_rpath(os.path.join(self.builddir, 'progcxx'))
  2904. self.assertEqual(build_rpath, '$ORIGIN/sub:/foo/bar')
  2905. self.install()
  2906. install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx'))
  2907. self.assertEqual(install_rpath, 'baz')
  2908. def test_pch_with_address_sanitizer(self):
  2909. if is_cygwin():
  2910. raise unittest.SkipTest('asan not available on Cygwin')
  2911. testdir = os.path.join(self.common_test_dir, '13 pch')
  2912. self.init(testdir, ['-Db_sanitize=address'])
  2913. self.build()
  2914. compdb = self.get_compdb()
  2915. for i in compdb:
  2916. self.assertIn("-fsanitize=address", i["command"])
  2917. def test_coverage(self):
  2918. gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
  2919. if not gcovr_exe:
  2920. raise unittest.SkipTest('gcovr not found')
  2921. if not shutil.which('genhtml') and not gcovr_new_rootdir:
  2922. raise unittest.SkipTest('genhtml not found and gcovr is too old')
  2923. if 'clang' in os.environ.get('CC', ''):
  2924. # We need to use llvm-cov instead of gcovr with clang
  2925. raise unittest.SkipTest('Coverage does not work with clang right now, help wanted!')
  2926. testdir = os.path.join(self.common_test_dir, '1 trivial')
  2927. self.init(testdir, ['-Db_coverage=true'])
  2928. self.build()
  2929. self.run_tests()
  2930. self.run_target('coverage-html')
  2931. def test_cross_find_program(self):
  2932. testdir = os.path.join(self.unit_test_dir, '12 cross prog')
  2933. crossfile = tempfile.NamedTemporaryFile(mode='w')
  2934. print(os.path.join(testdir, 'some_cross_tool.py'))
  2935. crossfile.write('''[binaries]
  2936. c = '/usr/bin/cc'
  2937. ar = '/usr/bin/ar'
  2938. strip = '/usr/bin/ar'
  2939. sometool.py = ['{0}']
  2940. someothertool.py = '{0}'
  2941. [properties]
  2942. [host_machine]
  2943. system = 'linux'
  2944. cpu_family = 'arm'
  2945. cpu = 'armv7' # Not sure if correct.
  2946. endian = 'little'
  2947. '''.format(os.path.join(testdir, 'some_cross_tool.py')))
  2948. crossfile.flush()
  2949. self.meson_cross_file = crossfile.name
  2950. self.init(testdir)
  2951. def test_reconfigure(self):
  2952. testdir = os.path.join(self.unit_test_dir, '13 reconfigure')
  2953. self.init(testdir, ['-Db_coverage=true'], default_args=False)
  2954. self.build('reconfigure')
  2955. def test_vala_generated_source_buildir_inside_source_tree(self):
  2956. '''
  2957. Test that valac outputs generated C files in the expected location when
  2958. the builddir is a subdir of the source tree.
  2959. '''
  2960. if not shutil.which('valac'):
  2961. raise unittest.SkipTest('valac not installed.')
  2962. testdir = os.path.join(self.vala_test_dir, '8 generated sources')
  2963. newdir = os.path.join(self.builddir, 'srctree')
  2964. shutil.copytree(testdir, newdir)
  2965. testdir = newdir
  2966. # New builddir
  2967. builddir = os.path.join(testdir, 'subdir/_build')
  2968. os.makedirs(builddir, exist_ok=True)
  2969. self.change_builddir(builddir)
  2970. self.init(testdir)
  2971. self.build()
  2972. def test_old_gnome_module_codepaths(self):
  2973. '''
  2974. A lot of code in the GNOME module is conditional on the version of the
  2975. glib tools that are installed, and breakages in the old code can slip
  2976. by once the CI has a newer glib version. So we force the GNOME module
  2977. to pretend that it's running on an ancient glib so the fallback code is
  2978. also tested.
  2979. '''
  2980. testdir = os.path.join(self.framework_test_dir, '7 gnome')
  2981. os.environ['MESON_UNIT_TEST_PRETEND_GLIB_OLD'] = "1"
  2982. mesonbuild.modules.gnome.native_glib_version = '2.20'
  2983. self.init(testdir, inprocess=True)
  2984. self.build()
  2985. mesonbuild.modules.gnome.native_glib_version = None
  2986. @skipIfNoPkgconfig
  2987. def test_pkgconfig_usage(self):
  2988. testdir1 = os.path.join(self.unit_test_dir, '24 pkgconfig usage/dependency')
  2989. testdir2 = os.path.join(self.unit_test_dir, '24 pkgconfig usage/dependee')
  2990. if subprocess.call(['pkg-config', '--cflags', 'glib-2.0'],
  2991. stdout=subprocess.DEVNULL,
  2992. stderr=subprocess.DEVNULL) != 0:
  2993. raise unittest.SkipTest('Glib 2.0 dependency not available.')
  2994. with tempfile.TemporaryDirectory() as tempdirname:
  2995. self.init(testdir1, ['--prefix=' + tempdirname, '--libdir=lib'], default_args=False)
  2996. self.install(use_destdir=False)
  2997. shutil.rmtree(self.builddir)
  2998. os.mkdir(self.builddir)
  2999. pkg_dir = os.path.join(tempdirname, 'lib/pkgconfig')
  3000. self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'libpkgdep.pc')))
  3001. lib_dir = os.path.join(tempdirname, 'lib')
  3002. os.environ['PKG_CONFIG_PATH'] = pkg_dir
  3003. # Private internal libraries must not leak out.
  3004. pkg_out = subprocess.check_output(['pkg-config', '--static', '--libs', 'libpkgdep'])
  3005. self.assertFalse(b'libpkgdep-int' in pkg_out, 'Internal library leaked out.')
  3006. # Dependencies must not leak to cflags when building only a shared library.
  3007. pkg_out = subprocess.check_output(['pkg-config', '--cflags', 'libpkgdep'])
  3008. self.assertFalse(b'glib' in pkg_out, 'Internal dependency leaked to headers.')
  3009. # Test that the result is usable.
  3010. self.init(testdir2)
  3011. self.build()
  3012. myenv = os.environ.copy()
  3013. myenv['LD_LIBRARY_PATH'] = lib_dir
  3014. if is_cygwin():
  3015. bin_dir = os.path.join(tempdirname, 'bin')
  3016. myenv['PATH'] = bin_dir + os.pathsep + myenv['PATH']
  3017. self.assertTrue(os.path.isdir(lib_dir))
  3018. test_exe = os.path.join(self.builddir, 'pkguser')
  3019. self.assertTrue(os.path.isfile(test_exe))
  3020. subprocess.check_call(test_exe, env=myenv)
  3021. @skipIfNoPkgconfig
  3022. def test_pkgconfig_internal_libraries(self):
  3023. '''
  3024. '''
  3025. with tempfile.TemporaryDirectory() as tempdirname:
  3026. # build library
  3027. testdirbase = os.path.join(self.unit_test_dir, '28 pkgconfig use libraries')
  3028. testdirlib = os.path.join(testdirbase, 'lib')
  3029. self.init(testdirlib, extra_args=['--prefix=' + tempdirname,
  3030. '--libdir=lib',
  3031. '--default-library=static'], default_args=False)
  3032. self.build()
  3033. self.install(use_destdir=False)
  3034. # build user of library
  3035. pkg_dir = os.path.join(tempdirname, 'lib/pkgconfig')
  3036. os.environ['PKG_CONFIG_PATH'] = pkg_dir
  3037. self.new_builddir()
  3038. self.init(os.path.join(testdirbase, 'app'))
  3039. self.build()
  3040. @skipIfNoPkgconfig
  3041. def test_pkgconfig_formatting(self):
  3042. testdir = os.path.join(self.unit_test_dir, '31 pkgconfig format')
  3043. self.init(testdir)
  3044. myenv = os.environ.copy()
  3045. myenv['PKG_CONFIG_PATH'] = self.privatedir
  3046. stdo = subprocess.check_output(['pkg-config', '--libs-only-l', 'libsomething'], env=myenv)
  3047. deps = [b'-lgobject-2.0', b'-lgio-2.0', b'-lglib-2.0', b'-lsomething']
  3048. if is_windows() or is_cygwin() or is_osx():
  3049. # On Windows, libintl is a separate library
  3050. deps.append(b'-lintl')
  3051. self.assertEqual(set(deps), set(stdo.split()))
  3052. def test_apple_bitcode(self):
  3053. '''
  3054. Test that -fembed-bitcode is correctly added while compiling and
  3055. -bitcode_bundle is added while linking when b_bitcode is true and not
  3056. when it is false. This can't be an ordinary test case because we need
  3057. to inspect the compiler database.
  3058. '''
  3059. if not is_osx():
  3060. raise unittest.SkipTest('Apple bitcode only works on macOS')
  3061. testdir = os.path.join(self.common_test_dir, '4 shared')
  3062. # Try with bitcode enabled
  3063. out = self.init(testdir, extra_args='-Db_bitcode=true')
  3064. # Warning was printed
  3065. self.assertRegex(out, 'WARNING:.*b_bitcode')
  3066. # Compiler options were added
  3067. compdb = self.get_compdb()
  3068. self.assertIn('-fembed-bitcode', compdb[0]['command'])
  3069. build_ninja = os.path.join(self.builddir, 'build.ninja')
  3070. # Linker options were added
  3071. with open(build_ninja, 'r', encoding='utf-8') as f:
  3072. contents = f.read()
  3073. m = re.search('LINK_ARGS =.*-bitcode_bundle', contents)
  3074. self.assertIsNotNone(m, msg=contents)
  3075. # Try with bitcode disabled
  3076. self.setconf('-Db_bitcode=false')
  3077. # Regenerate build
  3078. self.build()
  3079. compdb = self.get_compdb()
  3080. self.assertNotIn('-fembed-bitcode', compdb[0]['command'])
  3081. build_ninja = os.path.join(self.builddir, 'build.ninja')
  3082. with open(build_ninja, 'r', encoding='utf-8') as f:
  3083. contents = f.read()
  3084. m = re.search('LINK_ARGS =.*-bitcode_bundle', contents)
  3085. self.assertIsNone(m, msg=contents)
  3086. def test_apple_bitcode_modules(self):
  3087. '''
  3088. Same as above, just for shared_module()
  3089. '''
  3090. if not is_osx():
  3091. raise unittest.SkipTest('Apple bitcode not relevant')
  3092. testdir = os.path.join(self.common_test_dir, '156 shared module resolving symbol in executable')
  3093. # Ensure that it builds even with bitcode enabled
  3094. self.init(testdir, extra_args='-Db_bitcode=true')
  3095. self.build()
  3096. self.run_tests()
  3097. @skipIfNoPkgconfig
  3098. def test_usage_external_library(self):
  3099. '''
  3100. Test that uninstalled usage of an external library (from the system or
  3101. PkgConfigDependency) works. On macOS, this workflow works out of the
  3102. box. On Linux, BSDs, Windows, etc, you need to set extra arguments such
  3103. as LD_LIBRARY_PATH, etc, so this test is skipped.
  3104. The system library is found with cc.find_library() and pkg-config deps.
  3105. '''
  3106. if not is_osx():
  3107. raise unittest.SkipTest('workflow currently only works on macOS')
  3108. oldprefix = self.prefix
  3109. # Install external library so we can find it
  3110. testdir = os.path.join(self.unit_test_dir, '33 external, internal library rpath', 'external library')
  3111. # install into installdir without using DESTDIR
  3112. installdir = self.installdir
  3113. self.prefix = installdir
  3114. self.init(testdir)
  3115. self.prefix = oldprefix
  3116. self.build()
  3117. self.install(use_destdir=False)
  3118. ## New builddir for the consumer
  3119. self.new_builddir()
  3120. os.environ['LIBRARY_PATH'] = os.path.join(installdir, self.libdir)
  3121. os.environ['PKG_CONFIG_PATH'] = os.path.join(installdir, self.libdir, 'pkgconfig')
  3122. testdir = os.path.join(self.unit_test_dir, '33 external, internal library rpath', 'built library')
  3123. # install into installdir without using DESTDIR
  3124. self.prefix = self.installdir
  3125. self.init(testdir)
  3126. self.prefix = oldprefix
  3127. self.build()
  3128. # test uninstalled
  3129. self.run_tests()
  3130. # test running after installation
  3131. self.install(use_destdir=False)
  3132. prog = os.path.join(self.installdir, 'bin', 'prog')
  3133. self._run([prog])
  3134. out = self._run(['otool', '-L', prog])
  3135. self.assertNotIn('@rpath', out)
  3136. ## New builddir for testing that DESTDIR is not added to install_name
  3137. self.new_builddir()
  3138. # install into installdir with DESTDIR
  3139. self.init(testdir)
  3140. self.build()
  3141. # test running after installation
  3142. self.install()
  3143. prog = self.installdir + os.path.join(self.prefix, 'bin', 'prog')
  3144. lib = self.installdir + os.path.join(self.prefix, 'lib', 'libbar_built.dylib')
  3145. for f in prog, lib:
  3146. out = self._run(['otool', '-L', f])
  3147. # Ensure that the otool output does not contain self.installdir
  3148. self.assertNotRegex(out, self.installdir + '.*dylib ')
  3149. class LinuxArmCrossCompileTests(BasePlatformTests):
  3150. '''
  3151. Tests that verify cross-compilation to Linux/ARM
  3152. '''
  3153. def setUp(self):
  3154. super().setUp()
  3155. src_root = os.path.dirname(__file__)
  3156. self.meson_cross_file = os.path.join(src_root, 'cross', 'ubuntu-armhf.txt')
  3157. def test_cflags_cross_environment_pollution(self):
  3158. '''
  3159. Test that the CFLAGS environment variable does not pollute the cross
  3160. environment. This can't be an ordinary test case because we need to
  3161. inspect the compiler database.
  3162. '''
  3163. testdir = os.path.join(self.common_test_dir, '3 static')
  3164. os.environ['CFLAGS'] = '-DBUILD_ENVIRONMENT_ONLY'
  3165. self.init(testdir)
  3166. compdb = self.get_compdb()
  3167. self.assertNotIn('-DBUILD_ENVIRONMENT_ONLY', compdb[0]['command'])
  3168. def test_cross_file_overrides_always_args(self):
  3169. '''
  3170. Test that $lang_args in cross files always override get_always_args().
  3171. Needed for overriding the default -D_FILE_OFFSET_BITS=64 on some
  3172. architectures such as some Android versions and Raspbian.
  3173. https://github.com/mesonbuild/meson/issues/3049
  3174. https://github.com/mesonbuild/meson/issues/3089
  3175. '''
  3176. testdir = os.path.join(self.unit_test_dir, '29 cross file overrides always args')
  3177. self.meson_cross_file = os.path.join(testdir, 'ubuntu-armhf-overrides.txt')
  3178. self.init(testdir)
  3179. compdb = self.get_compdb()
  3180. self.assertRegex(compdb[0]['command'], '-D_FILE_OFFSET_BITS=64.*-U_FILE_OFFSET_BITS')
  3181. self.build()
  3182. class PythonTests(BasePlatformTests):
  3183. '''
  3184. Tests that verify compilation of python extension modules
  3185. '''
  3186. def test_versions(self):
  3187. if self.backend is not Backend.ninja:
  3188. raise unittest.SkipTest('Skipping python tests with {} backend'.format(self.backend.name))
  3189. testdir = os.path.join(self.src_root, 'test cases', 'unit', '32 python extmodule')
  3190. # No python version specified, this will use meson's python
  3191. self.init(testdir)
  3192. self.build()
  3193. self.run_tests()
  3194. self.wipe()
  3195. # When specifying a known name, (python2 / python3) the module
  3196. # will also try 'python' as a fallback and use it if the major
  3197. # version matches
  3198. try:
  3199. self.init(testdir, ['-Dpython=python2'])
  3200. self.build()
  3201. self.run_tests()
  3202. except unittest.SkipTest:
  3203. # python2 is not necessarily installed on the test machine,
  3204. # if it is not, or the python headers can't be found, the test
  3205. # will raise MESON_SKIP_TEST, we could check beforehand what version
  3206. # of python is available, but it's a bit of a chicken and egg situation,
  3207. # as that is the job of the module, so we just ask for forgiveness rather
  3208. # than permission.
  3209. pass
  3210. self.wipe()
  3211. for py in ('pypy', 'pypy3'):
  3212. try:
  3213. self.init(testdir, ['-Dpython=%s' % py])
  3214. except unittest.SkipTest:
  3215. # Same as above, pypy2 and pypy3 are not expected to be present
  3216. # on the test system, the test project only raises in these cases
  3217. continue
  3218. # We have a pypy, this is expected to work
  3219. self.build()
  3220. self.run_tests()
  3221. self.wipe()
  3222. # The test is configured to error out with MESON_SKIP_TEST
  3223. # in case it could not find python
  3224. with self.assertRaises(unittest.SkipTest):
  3225. self.init(testdir, ['-Dpython=not-python'])
  3226. self.wipe()
  3227. # While dir is an external command on both Windows and Linux,
  3228. # it certainly isn't python
  3229. with self.assertRaises(unittest.SkipTest):
  3230. self.init(testdir, ['-Dpython=dir'])
  3231. self.wipe()
  3232. class RewriterTests(unittest.TestCase):
  3233. def setUp(self):
  3234. super().setUp()
  3235. src_root = os.path.dirname(__file__)
  3236. self.testroot = os.path.realpath(tempfile.mkdtemp())
  3237. self.rewrite_command = python_command + [os.path.join(src_root, 'mesonrewriter.py')]
  3238. self.tmpdir = os.path.realpath(tempfile.mkdtemp())
  3239. self.workdir = os.path.join(self.tmpdir, 'foo')
  3240. self.test_dir = os.path.join(src_root, 'test cases/rewrite')
  3241. def tearDown(self):
  3242. windows_proof_rmtree(self.tmpdir)
  3243. def read_contents(self, fname):
  3244. with open(os.path.join(self.workdir, fname)) as f:
  3245. return f.read()
  3246. def check_effectively_same(self, mainfile, truth):
  3247. mf = self.read_contents(mainfile)
  3248. t = self.read_contents(truth)
  3249. # Rewriting is not guaranteed to do a perfect job of
  3250. # maintaining whitespace.
  3251. self.assertEqual(mf.replace(' ', ''), t.replace(' ', ''))
  3252. def prime(self, dirname):
  3253. shutil.copytree(os.path.join(self.test_dir, dirname), self.workdir)
  3254. def test_basic(self):
  3255. self.prime('1 basic')
  3256. subprocess.check_call(self.rewrite_command + ['remove',
  3257. '--target=trivialprog',
  3258. '--filename=notthere.c',
  3259. '--sourcedir', self.workdir],
  3260. universal_newlines=True)
  3261. self.check_effectively_same('meson.build', 'removed.txt')
  3262. subprocess.check_call(self.rewrite_command + ['add',
  3263. '--target=trivialprog',
  3264. '--filename=notthere.c',
  3265. '--sourcedir', self.workdir],
  3266. universal_newlines=True)
  3267. self.check_effectively_same('meson.build', 'added.txt')
  3268. subprocess.check_call(self.rewrite_command + ['remove',
  3269. '--target=trivialprog',
  3270. '--filename=notthere.c',
  3271. '--sourcedir', self.workdir],
  3272. universal_newlines=True)
  3273. self.check_effectively_same('meson.build', 'removed.txt')
  3274. def test_subdir(self):
  3275. self.prime('2 subdirs')
  3276. top = self.read_contents('meson.build')
  3277. s2 = self.read_contents('sub2/meson.build')
  3278. subprocess.check_call(self.rewrite_command + ['remove',
  3279. '--target=something',
  3280. '--filename=second.c',
  3281. '--sourcedir', self.workdir],
  3282. universal_newlines=True)
  3283. self.check_effectively_same('sub1/meson.build', 'sub1/after.txt')
  3284. self.assertEqual(top, self.read_contents('meson.build'))
  3285. self.assertEqual(s2, self.read_contents('sub2/meson.build'))
  3286. def unset_envs():
  3287. # For unit tests we must fully control all command lines
  3288. # so that there are no unexpected changes coming from the
  3289. # environment, for example when doing a package build.
  3290. varnames = ['CPPFLAGS', 'LDFLAGS'] + list(mesonbuild.compilers.compilers.cflags_mapping.values())
  3291. for v in varnames:
  3292. if v in os.environ:
  3293. del os.environ[v]
  3294. if __name__ == '__main__':
  3295. unset_envs()
  3296. cases = ['InternalTests', 'AllPlatformTests', 'FailureTests', 'PythonTests']
  3297. if not is_windows():
  3298. cases += ['LinuxlikeTests']
  3299. if should_run_linux_cross_tests():
  3300. cases += ['LinuxArmCrossCompileTests']
  3301. if is_windows() or is_cygwin():
  3302. cases += ['WindowsTests']
  3303. unittest.main(defaultTest=cases, buffer=True)