devtool.py 162 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033
  1. #
  2. # Copyright OpenEmbedded Contributors
  3. #
  4. # SPDX-License-Identifier: MIT
  5. #
  6. import errno
  7. import os
  8. import re
  9. import shutil
  10. import tempfile
  11. import glob
  12. import fnmatch
  13. import unittest
  14. import json
  15. from oeqa.selftest.case import OESelftestTestCase
  16. from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
  17. from oeqa.utils.commands import get_bb_vars, runqemu, runqemu_check_taps, get_test_layer
  18. from oeqa.core.decorator import OETestTag
  19. from bb.utils import mkdirhier, edit_bblayers_conf
  20. oldmetapath = None
  21. def setUpModule():
  22. global templayerdir
  23. templayerdir = tempfile.mkdtemp(prefix='devtoolqa')
  24. corecopydir = os.path.join(templayerdir, 'core-copy')
  25. bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
  26. edited_layers = []
  27. # make sure user doesn't have a local workspace
  28. result = runCmd('bitbake-layers show-layers')
  29. assert "workspacelayer" not in result.output, "Devtool test suite cannot be run with a local workspace directory"
  30. # We need to take a copy of the meta layer so we can modify it and not
  31. # have any races against other tests that might be running in parallel
  32. # however things like COREBASE mean that you can't just copy meta, you
  33. # need the whole repository.
  34. def bblayers_edit_cb(layerpath, canonical_layerpath):
  35. global oldmetapath
  36. if not canonical_layerpath.endswith('/'):
  37. # This helps us match exactly when we're using this path later
  38. canonical_layerpath += '/'
  39. if not edited_layers and canonical_layerpath.endswith('/meta/'):
  40. canonical_layerpath = os.path.realpath(canonical_layerpath) + '/'
  41. edited_layers.append(layerpath)
  42. oldmetapath = os.path.realpath(layerpath)
  43. # when downloading poky from tar.gz some tests will be skipped (BUG 12389)
  44. try:
  45. runCmd('git rev-parse --is-inside-work-tree', cwd=canonical_layerpath)
  46. except:
  47. raise unittest.SkipTest("devtool tests require folder to be a git repo")
  48. result = runCmd('git rev-parse --show-toplevel', cwd=canonical_layerpath)
  49. oldreporoot = result.output.rstrip()
  50. newmetapath = os.path.join(corecopydir, os.path.relpath(oldmetapath, oldreporoot))
  51. runCmd('git clone file://%s %s' % (oldreporoot, corecopydir), cwd=templayerdir)
  52. # Now we need to copy any modified files
  53. # You might ask "why not just copy the entire tree instead of
  54. # cloning and doing this?" - well, the problem with that is
  55. # TMPDIR or an equally large subdirectory might exist
  56. # under COREBASE and we don't want to copy that, so we have
  57. # to be selective.
  58. result = runCmd('git status --porcelain', cwd=oldreporoot)
  59. # Also copy modifications to the 'scripts/' directory
  60. canonical_layerpath_scripts = os.path.normpath(canonical_layerpath + "../scripts")
  61. for line in result.output.splitlines():
  62. if line.startswith(' M ') or line.startswith('?? '):
  63. relpth = line.split()[1]
  64. pth = os.path.join(oldreporoot, relpth)
  65. if pth.startswith(canonical_layerpath) or pth.startswith(canonical_layerpath_scripts):
  66. if relpth.endswith('/'):
  67. destdir = os.path.join(corecopydir, relpth)
  68. # avoid race condition by not copying .pyc files YPBZ#13421,13803
  69. shutil.copytree(pth, destdir, ignore=shutil.ignore_patterns('*.pyc', '__pycache__'))
  70. else:
  71. destdir = os.path.join(corecopydir, os.path.dirname(relpth))
  72. mkdirhier(destdir)
  73. shutil.copy2(pth, destdir)
  74. return newmetapath
  75. else:
  76. return layerpath
  77. edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
  78. def tearDownModule():
  79. if oldmetapath:
  80. edited_layers = []
  81. def bblayers_edit_cb(layerpath, canonical_layerpath):
  82. if not edited_layers and canonical_layerpath.endswith('/meta'):
  83. edited_layers.append(layerpath)
  84. return oldmetapath
  85. else:
  86. return layerpath
  87. bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
  88. edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
  89. shutil.rmtree(templayerdir)
  90. class DevtoolTestCase(OESelftestTestCase):
  91. def setUp(self):
  92. """Test case setup function"""
  93. super(DevtoolTestCase, self).setUp()
  94. self.workspacedir = os.path.join(self.builddir, 'workspace')
  95. self.assertTrue(not os.path.exists(self.workspacedir),
  96. 'This test cannot be run with a workspace directory '
  97. 'under the build directory')
  98. def _check_src_repo(self, repo_dir):
  99. """Check srctree git repository"""
  100. self.assertTrue(os.path.isdir(os.path.join(repo_dir, '.git')),
  101. 'git repository for external source tree not found')
  102. result = runCmd('git status --porcelain', cwd=repo_dir)
  103. self.assertEqual(result.output.strip(), "",
  104. 'Created git repo is not clean')
  105. result = runCmd('git symbolic-ref HEAD', cwd=repo_dir)
  106. self.assertEqual(result.output.strip(), "refs/heads/devtool",
  107. 'Wrong branch in git repo')
  108. def _check_repo_status(self, repo_dir, expected_status):
  109. """Check the worktree status of a repository"""
  110. result = runCmd('git status . --porcelain',
  111. cwd=repo_dir)
  112. for line in result.output.splitlines():
  113. for ind, (f_status, fn_re) in enumerate(expected_status):
  114. if re.match(fn_re, line[3:]):
  115. if f_status != line[:2]:
  116. self.fail('Unexpected status in line: %s' % line)
  117. expected_status.pop(ind)
  118. break
  119. else:
  120. self.fail('Unexpected modified file in line: %s' % line)
  121. if expected_status:
  122. self.fail('Missing file changes: %s' % expected_status)
  123. def _test_recipe_contents(self, recipefile, checkvars, checkinherits):
  124. with open(recipefile, 'r') as f:
  125. invar = None
  126. invalue = None
  127. inherits = set()
  128. for line in f:
  129. var = None
  130. if invar:
  131. value = line.strip().strip('"')
  132. if value.endswith('\\'):
  133. invalue += ' ' + value[:-1].strip()
  134. continue
  135. else:
  136. invalue += ' ' + value.strip()
  137. var = invar
  138. value = invalue
  139. invar = None
  140. elif '=' in line:
  141. splitline = re.split(r"[?+:]*=[+]?", line, 1)
  142. var = splitline[0].rstrip()
  143. value = splitline[1].strip().strip('"')
  144. if value.endswith('\\'):
  145. invalue = value[:-1].strip()
  146. invar = var
  147. continue
  148. elif line.startswith('inherit '):
  149. inherits.update(line.split()[1:])
  150. if var and var in checkvars:
  151. needvalue = checkvars.pop(var)
  152. if needvalue is None:
  153. self.fail('Variable %s should not appear in recipe, but value is being set to "%s"' % (var, value))
  154. if isinstance(needvalue, set):
  155. if var == 'LICENSE':
  156. value = set(value.split(' & '))
  157. else:
  158. value = set(value.split())
  159. self.assertEqual(value, needvalue, 'values for %s do not match' % var)
  160. missingvars = {}
  161. for var, value in checkvars.items():
  162. if value is not None:
  163. missingvars[var] = value
  164. self.assertEqual(missingvars, {}, 'Some expected variables not found in recipe: %s' % checkvars)
  165. for inherit in checkinherits:
  166. self.assertIn(inherit, inherits, 'Missing inherit of %s' % inherit)
  167. def _check_bbappend(self, testrecipe, recipefile, appenddir):
  168. result = runCmd('bitbake-layers show-appends', cwd=self.builddir)
  169. resultlines = result.output.splitlines()
  170. inrecipe = False
  171. bbappends = []
  172. bbappendfile = None
  173. for line in resultlines:
  174. if inrecipe:
  175. if line.startswith(' '):
  176. bbappends.append(line.strip())
  177. else:
  178. break
  179. elif line == '%s:' % os.path.basename(recipefile):
  180. inrecipe = True
  181. self.assertLessEqual(len(bbappends), 2, '%s recipe is being bbappended by another layer - bbappends found:\n %s' % (testrecipe, '\n '.join(bbappends)))
  182. for bbappend in bbappends:
  183. if bbappend.startswith(appenddir):
  184. bbappendfile = bbappend
  185. break
  186. else:
  187. self.fail('bbappend for recipe %s does not seem to be created in test layer' % testrecipe)
  188. return bbappendfile
  189. def _create_temp_layer(self, templayerdir, addlayer, templayername, priority=999, recipepathspec='recipes-*/*'):
  190. create_temp_layer(templayerdir, templayername, priority, recipepathspec)
  191. if addlayer:
  192. self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
  193. result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
  194. def _process_ls_output(self, output):
  195. """
  196. Convert ls -l output to a format we can reasonably compare from one context
  197. to another (e.g. from host to target)
  198. """
  199. filelist = []
  200. for line in output.splitlines():
  201. splitline = line.split()
  202. if len(splitline) < 8:
  203. self.fail('_process_ls_output: invalid output line: %s' % line)
  204. # Remove trailing . on perms
  205. splitline[0] = splitline[0].rstrip('.')
  206. # Remove leading . on paths
  207. splitline[-1] = splitline[-1].lstrip('.')
  208. # Drop fields we don't want to compare
  209. del splitline[7]
  210. del splitline[6]
  211. del splitline[5]
  212. del splitline[4]
  213. del splitline[1]
  214. filelist.append(' '.join(splitline))
  215. return filelist
  216. def _check_diff(self, diffoutput, addlines, removelines):
  217. """Check output from 'git diff' matches expectation"""
  218. remaining_addlines = addlines[:]
  219. remaining_removelines = removelines[:]
  220. for line in diffoutput.splitlines():
  221. if line.startswith('+++') or line.startswith('---'):
  222. continue
  223. elif line.startswith('+'):
  224. matched = False
  225. for item in addlines:
  226. if re.match(item, line[1:].strip()):
  227. matched = True
  228. remaining_addlines.remove(item)
  229. break
  230. self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
  231. elif line.startswith('-'):
  232. matched = False
  233. for item in removelines:
  234. if re.match(item, line[1:].strip()):
  235. matched = True
  236. remaining_removelines.remove(item)
  237. break
  238. self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
  239. if remaining_addlines:
  240. self.fail('Expected added lines not found: %s' % remaining_addlines)
  241. if remaining_removelines:
  242. self.fail('Expected removed lines not found: %s' % remaining_removelines)
  243. def _check_runqemu_prerequisites(self):
  244. """Check runqemu is available
  245. Whilst some tests would seemingly be better placed as a runtime test,
  246. unfortunately the runtime tests run under bitbake and you can't run
  247. devtool within bitbake (since devtool needs to run bitbake itself).
  248. Additionally we are testing build-time functionality as well, so
  249. really this has to be done as an oe-selftest test.
  250. """
  251. machine = get_bb_var('MACHINE')
  252. if not machine.startswith('qemu'):
  253. self.skipTest('This test only works with qemu machines')
  254. if not runqemu_check_taps():
  255. self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
  256. def _test_devtool_add_git_url(self, git_url, version, pn, resulting_src_uri, srcrev=None):
  257. self.track_for_cleanup(self.workspacedir)
  258. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  259. command = 'devtool add --version %s %s %s' % (version, pn, git_url)
  260. if srcrev :
  261. command += ' --srcrev %s' %srcrev
  262. result = runCmd(command)
  263. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  264. # Check the recipe name is correct
  265. recipefile = get_bb_var('FILE', pn)
  266. self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
  267. self.assertIn(recipefile, result.output)
  268. # Test devtool status
  269. result = runCmd('devtool status')
  270. self.assertIn(pn, result.output)
  271. self.assertIn(recipefile, result.output)
  272. checkvars = {}
  273. checkvars['SRC_URI'] = resulting_src_uri
  274. self._test_recipe_contents(recipefile, checkvars, [])
  275. class DevtoolBase(DevtoolTestCase):
  276. @classmethod
  277. def setUpClass(cls):
  278. super(DevtoolBase, cls).setUpClass()
  279. bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
  280. cls.original_sstate = bb_vars['SSTATE_DIR']
  281. cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
  282. cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
  283. cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
  284. % cls.original_sstate)
  285. cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yoctoproject.org:8686"\n')
  286. @classmethod
  287. def tearDownClass(cls):
  288. cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
  289. runCmd('rm -rf %s' % cls.devtool_sstate)
  290. super(DevtoolBase, cls).tearDownClass()
  291. def setUp(self):
  292. """Test case setup function"""
  293. super(DevtoolBase, self).setUp()
  294. self.append_config(self.sstate_conf)
  295. class DevtoolTests(DevtoolBase):
  296. def test_create_workspace(self):
  297. # Check preconditions
  298. result = runCmd('bitbake-layers show-layers')
  299. self.assertTrue('\nworkspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf')
  300. # remove conf/devtool.conf to avoid it corrupting tests
  301. devtoolconf = os.path.join(self.builddir, 'conf', 'devtool.conf')
  302. self.track_for_cleanup(devtoolconf)
  303. # Try creating a workspace layer with a specific path
  304. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  305. self.track_for_cleanup(tempdir)
  306. result = runCmd('devtool create-workspace %s' % tempdir)
  307. self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
  308. result = runCmd('bitbake-layers show-layers')
  309. self.assertIn(tempdir, result.output)
  310. # Try creating a workspace layer with the default path
  311. self.track_for_cleanup(self.workspacedir)
  312. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  313. result = runCmd('devtool create-workspace')
  314. self.assertTrue(os.path.isfile(os.path.join(self.workspacedir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
  315. result = runCmd('bitbake-layers show-layers')
  316. self.assertNotIn(tempdir, result.output)
  317. self.assertIn(self.workspacedir, result.output)
  318. class DevtoolAddTests(DevtoolBase):
  319. def test_devtool_add(self):
  320. # Fetch source
  321. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  322. self.track_for_cleanup(tempdir)
  323. pn = 'pv'
  324. pv = '1.5.3'
  325. url = 'http://downloads.yoctoproject.org/mirror/sources/pv-1.5.3.tar.bz2'
  326. result = runCmd('wget %s' % url, cwd=tempdir)
  327. result = runCmd('tar xfv %s' % os.path.basename(url), cwd=tempdir)
  328. srcdir = os.path.join(tempdir, '%s-%s' % (pn, pv))
  329. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
  330. # Test devtool add
  331. self.track_for_cleanup(self.workspacedir)
  332. self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
  333. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  334. result = runCmd('devtool add %s %s' % (pn, srcdir))
  335. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  336. # Test devtool status
  337. result = runCmd('devtool status')
  338. recipepath = '%s/recipes/%s/%s_%s.bb' % (self.workspacedir, pn, pn, pv)
  339. self.assertIn(recipepath, result.output)
  340. self.assertIn(srcdir, result.output)
  341. # Test devtool find-recipe
  342. result = runCmd('devtool -q find-recipe %s' % pn)
  343. self.assertEqual(recipepath, result.output.strip())
  344. # Test devtool edit-recipe
  345. result = runCmd('VISUAL="echo 123" devtool -q edit-recipe %s' % pn)
  346. self.assertEqual('123 %s' % recipepath, result.output.strip())
  347. # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
  348. bitbake('%s -c cleansstate' % pn)
  349. # Test devtool build
  350. result = runCmd('devtool build %s' % pn)
  351. bb_vars = get_bb_vars(['D', 'bindir'], pn)
  352. installdir = bb_vars['D']
  353. self.assertTrue(installdir, 'Could not query installdir variable')
  354. bindir = bb_vars['bindir']
  355. self.assertTrue(bindir, 'Could not query bindir variable')
  356. if bindir[0] == '/':
  357. bindir = bindir[1:]
  358. self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
  359. def test_devtool_add_binary(self):
  360. # Create a binary package containing a known test file
  361. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  362. self.track_for_cleanup(tempdir)
  363. pn = 'tst-bin'
  364. pv = '1.0'
  365. test_file_dir = "var/lib/%s/" % pn
  366. test_file_name = "test_file"
  367. test_file_content = "TEST CONTENT"
  368. test_file_package_root = os.path.join(tempdir, pn)
  369. test_file_dir_full = os.path.join(test_file_package_root, test_file_dir)
  370. mkdirhier(test_file_dir_full)
  371. with open(os.path.join(test_file_dir_full, test_file_name), "w") as f:
  372. f.write(test_file_content)
  373. bin_package_path = os.path.join(tempdir, "%s.tar.gz" % pn)
  374. runCmd("tar czf %s -C %s ." % (bin_package_path, test_file_package_root))
  375. # Test devtool add -b on the binary package
  376. self.track_for_cleanup(self.workspacedir)
  377. self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
  378. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  379. result = runCmd('devtool add -b %s %s' % (pn, bin_package_path))
  380. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  381. # Build the resulting recipe
  382. result = runCmd('devtool build %s' % pn)
  383. installdir = get_bb_var('D', pn)
  384. self.assertTrue(installdir, 'Could not query installdir variable')
  385. # Check that a known file from the binary package has indeed been installed
  386. self.assertTrue(os.path.isfile(os.path.join(installdir, test_file_dir, test_file_name)), '%s not found in D' % test_file_name)
  387. def test_devtool_add_git_local(self):
  388. # We need dbus built so that DEPENDS recognition works
  389. bitbake('dbus')
  390. # Fetch source from a remote URL, but do it outside of devtool
  391. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  392. self.track_for_cleanup(tempdir)
  393. pn = 'dbus-wait'
  394. srcrev = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
  395. # We choose an https:// git URL here to check rewriting the URL works
  396. url = 'https://git.yoctoproject.org/git/dbus-wait'
  397. # Force fetching to "noname" subdir so we verify we're picking up the name from autoconf
  398. # instead of the directory name
  399. result = runCmd('git clone %s noname' % url, cwd=tempdir)
  400. srcdir = os.path.join(tempdir, 'noname')
  401. result = runCmd('git reset --hard %s' % srcrev, cwd=srcdir)
  402. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory')
  403. # Test devtool add
  404. self.track_for_cleanup(self.workspacedir)
  405. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  406. # Don't specify a name since we should be able to auto-detect it
  407. result = runCmd('devtool add %s' % srcdir)
  408. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  409. # Check the recipe name is correct
  410. recipefile = get_bb_var('FILE', pn)
  411. self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
  412. self.assertIn(recipefile, result.output)
  413. # Test devtool status
  414. result = runCmd('devtool status')
  415. self.assertIn(pn, result.output)
  416. self.assertIn(srcdir, result.output)
  417. self.assertIn(recipefile, result.output)
  418. checkvars = {}
  419. checkvars['LICENSE'] = 'GPL-2.0-only'
  420. checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
  421. checkvars['S'] = None
  422. checkvars['PV'] = '0.1+git'
  423. checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https;branch=master'
  424. checkvars['SRCREV'] = srcrev
  425. checkvars['DEPENDS'] = set(['dbus'])
  426. self._test_recipe_contents(recipefile, checkvars, [])
  427. def test_devtool_add_git_style1(self):
  428. version = 'v3.1.0'
  429. pn = 'mbedtls'
  430. # this will trigger reformat_git_uri with branch parameter in url
  431. git_url = "'git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https'"
  432. resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https"
  433. self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri)
  434. def test_devtool_add_git_style2(self):
  435. version = 'v3.1.0'
  436. srcrev = 'v3.1.0'
  437. pn = 'mbedtls'
  438. # this will trigger reformat_git_uri with branch parameter in url
  439. git_url = "'git://git@github.com/ARMmbed/mbedtls.git;protocol=https'"
  440. resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;protocol=https;branch=master"
  441. self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri, srcrev)
  442. def test_devtool_add_library(self):
  443. # Fetch source
  444. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  445. self.track_for_cleanup(tempdir)
  446. version = '1.1'
  447. url = 'https://www.intra2net.com/en/developer/libftdi/download/libftdi1-%s.tar.bz2' % version
  448. result = runCmd('wget %s' % url, cwd=tempdir)
  449. result = runCmd('tar xfv libftdi1-%s.tar.bz2' % version, cwd=tempdir)
  450. srcdir = os.path.join(tempdir, 'libftdi1-%s' % version)
  451. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory')
  452. # Test devtool add (and use -V so we test that too)
  453. self.track_for_cleanup(self.workspacedir)
  454. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  455. result = runCmd('devtool add libftdi %s -V %s' % (srcdir, version))
  456. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  457. # Test devtool status
  458. result = runCmd('devtool status')
  459. self.assertIn('libftdi', result.output)
  460. self.assertIn(srcdir, result.output)
  461. # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
  462. bitbake('libftdi -c cleansstate')
  463. # libftdi's python/CMakeLists.txt is a bit broken, so let's just disable it
  464. # There's also the matter of it installing cmake files to a path we don't
  465. # normally cover, which triggers the installed-vs-shipped QA test we have
  466. # within do_package
  467. recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version)
  468. # There is no upstream release that supports building with CMake 4+ yet, so we explicitly
  469. # set the policy minimum version via EXTRA_OECMAKE. That's easier than applying backported
  470. # patches.
  471. result = runCmd(
  472. "recipetool setvar %s EXTRA_OECMAKE -- '-DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules'"
  473. % recipefile
  474. )
  475. with open(recipefile, 'a') as f:
  476. f.write('\nFILES:${PN}-dev += "${datadir}/cmake/Modules"\n')
  477. # We don't have the ability to pick up this dependency automatically yet...
  478. f.write('\nDEPENDS += "libusb1"\n')
  479. f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
  480. # Test devtool build
  481. result = runCmd('devtool build libftdi')
  482. bb_vars = get_bb_vars(['TESTLIBOUTPUT', 'STAMP'], 'libftdi')
  483. staging_libdir = bb_vars['TESTLIBOUTPUT']
  484. self.assertTrue(staging_libdir, 'Could not query TESTLIBOUTPUT variable')
  485. self.assertTrue(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), "libftdi binary not found in STAGING_LIBDIR. Output of devtool build libftdi %s" % result.output)
  486. # Test devtool reset
  487. stampprefix = bb_vars['STAMP']
  488. result = runCmd('devtool reset libftdi')
  489. result = runCmd('devtool status')
  490. self.assertNotIn('libftdi', result.output)
  491. self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi')
  492. matches = glob.glob(stampprefix + '*')
  493. self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned')
  494. self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning')
  495. def test_devtool_add_fetch(self):
  496. # Fetch source
  497. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  498. self.track_for_cleanup(tempdir)
  499. testver = '0.23'
  500. url = 'https://files.pythonhosted.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-%s.tar.gz' % testver
  501. testrecipe = 'python-markupsafe'
  502. srcdir = os.path.join(tempdir, testrecipe)
  503. # Test devtool add
  504. self.track_for_cleanup(self.workspacedir)
  505. self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
  506. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  507. result = runCmd('devtool add --no-pypi %s %s -f %s' % (testrecipe, srcdir, url))
  508. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output)
  509. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
  510. self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
  511. # Test devtool status
  512. result = runCmd('devtool status')
  513. self.assertIn(testrecipe, result.output)
  514. self.assertIn(srcdir, result.output)
  515. # Check recipe
  516. recipefile = get_bb_var('FILE', testrecipe)
  517. self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
  518. checkvars = {}
  519. checkvars['S'] = '${UNPACKDIR}/MarkupSafe-${PV}'
  520. checkvars['SRC_URI'] = url.replace(testver, '${PV}')
  521. self._test_recipe_contents(recipefile, checkvars, [])
  522. # Try with version specified
  523. result = runCmd('devtool reset -n %s' % testrecipe)
  524. shutil.rmtree(srcdir)
  525. fakever = '1.9'
  526. result = runCmd('devtool add --no-pypi %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever))
  527. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
  528. # Test devtool status
  529. result = runCmd('devtool status')
  530. self.assertIn(testrecipe, result.output)
  531. self.assertIn(srcdir, result.output)
  532. # Check recipe
  533. recipefile = get_bb_var('FILE', testrecipe)
  534. self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named')
  535. checkvars = {}
  536. checkvars['S'] = '${UNPACKDIR}/MarkupSafe-%s' % testver
  537. checkvars['SRC_URI'] = url
  538. self._test_recipe_contents(recipefile, checkvars, [])
  539. def test_devtool_add_fetch_git(self):
  540. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  541. self.track_for_cleanup(tempdir)
  542. url = 'gitsm://git.yoctoproject.org/mraa'
  543. url_branch = '%s;branch=master' % url
  544. checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
  545. testrecipe = 'mraa'
  546. srcdir = os.path.join(tempdir, testrecipe)
  547. # Test devtool add
  548. self.track_for_cleanup(self.workspacedir)
  549. self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
  550. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  551. result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url))
  552. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created: %s' % result.output)
  553. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
  554. # Test devtool status
  555. result = runCmd('devtool status')
  556. self.assertIn(testrecipe, result.output)
  557. self.assertIn(srcdir, result.output)
  558. # Check recipe
  559. recipefile = get_bb_var('FILE', testrecipe)
  560. self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
  561. checkvars = {}
  562. checkvars['S'] = None
  563. checkvars['PV'] = '1.0+git'
  564. checkvars['SRC_URI'] = url_branch
  565. checkvars['SRCREV'] = '${AUTOREV}'
  566. self._test_recipe_contents(recipefile, checkvars, [])
  567. # Try with revision and version specified
  568. result = runCmd('devtool reset -n %s' % testrecipe)
  569. shutil.rmtree(srcdir)
  570. url_rev = '%s;rev=%s' % (url, checkrev)
  571. result = runCmd('devtool add %s %s -f "%s" -V 1.5' % (testrecipe, srcdir, url_rev))
  572. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
  573. # Test devtool status
  574. result = runCmd('devtool status')
  575. self.assertIn(testrecipe, result.output)
  576. self.assertIn(srcdir, result.output)
  577. # Check recipe
  578. recipefile = get_bb_var('FILE', testrecipe)
  579. self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
  580. checkvars = {}
  581. checkvars['S'] = None
  582. checkvars['PV'] = '1.5+git'
  583. checkvars['SRC_URI'] = url_branch
  584. checkvars['SRCREV'] = checkrev
  585. self._test_recipe_contents(recipefile, checkvars, [])
  586. def test_devtool_add_fetch_simple(self):
  587. # Fetch source from a remote URL, auto-detecting name
  588. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  589. self.track_for_cleanup(tempdir)
  590. testver = '1.6.0'
  591. url = 'http://www.ivarch.com/programs/sources/pv-%s.tar.bz2' % testver
  592. testrecipe = 'pv'
  593. srcdir = os.path.join(self.workspacedir, 'sources', testrecipe)
  594. # Test devtool add
  595. self.track_for_cleanup(self.workspacedir)
  596. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  597. result = runCmd('devtool add %s' % url)
  598. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output)
  599. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
  600. self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
  601. # Test devtool status
  602. result = runCmd('devtool status')
  603. self.assertIn(testrecipe, result.output)
  604. self.assertIn(srcdir, result.output)
  605. # Check recipedevtool add
  606. recipefile = get_bb_var('FILE', testrecipe)
  607. self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
  608. checkvars = {}
  609. checkvars['S'] = None
  610. checkvars['SRC_URI'] = url.replace(testver, '${PV}')
  611. self._test_recipe_contents(recipefile, checkvars, [])
  612. def test_devtool_add_npm(self):
  613. collections = get_bb_var('BBFILE_COLLECTIONS').split()
  614. if "openembedded-layer" not in collections:
  615. self.skipTest("Test needs meta-oe for nodejs")
  616. pn = 'savoirfairelinux-node-server-example'
  617. pv = '1.0.0'
  618. url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=' + pv
  619. # Test devtool add
  620. self.track_for_cleanup(self.workspacedir)
  621. self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
  622. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  623. result = runCmd('devtool add \'%s\'' % url)
  624. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  625. self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, '%s_%s.bb' % (pn, pv)), 'Recipe not created')
  626. self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, pn, 'npm-shrinkwrap.json'), 'Shrinkwrap not created')
  627. # Test devtool status
  628. result = runCmd('devtool status')
  629. self.assertIn(pn, result.output)
  630. # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
  631. bitbake('%s -c cleansstate' % pn)
  632. # Test devtool build
  633. result = runCmd('devtool build %s' % pn)
  634. def test_devtool_add_python_egg_requires(self):
  635. # Fetch source
  636. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  637. self.track_for_cleanup(tempdir)
  638. testver = '0.14.0'
  639. url = 'https://files.pythonhosted.org/packages/e9/9e/25d59f5043cf763833b2581c8027fa92342c4cf8ee523b498ecdf460c16d/uvicorn-%s.tar.gz' % testver
  640. testrecipe = 'python3-uvicorn'
  641. srcdir = os.path.join(tempdir, testrecipe)
  642. # Test devtool add
  643. self.track_for_cleanup(self.workspacedir)
  644. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  645. result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
  646. class DevtoolModifyTests(DevtoolBase):
  647. def test_devtool_modify(self):
  648. import oe.path
  649. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  650. self.track_for_cleanup(tempdir)
  651. self.track_for_cleanup(self.workspacedir)
  652. self.add_command_to_tearDown('bitbake -c clean mdadm')
  653. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  654. result = runCmd('devtool modify mdadm -x %s' % tempdir)
  655. self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
  656. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  657. matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mdadm_*.bbappend'))
  658. self.assertTrue(matches, 'bbappend not created %s' % result.output)
  659. # Test devtool status
  660. result = runCmd('devtool status')
  661. self.assertIn('mdadm', result.output)
  662. self.assertIn(tempdir, result.output)
  663. self._check_src_repo(tempdir)
  664. bitbake('mdadm -C unpack')
  665. def check_line(checkfile, expected, message, present=True):
  666. # Check for $expected, on a line on its own, in checkfile.
  667. with open(checkfile, 'r') as f:
  668. if present:
  669. self.assertIn(expected + '\n', f, message)
  670. else:
  671. self.assertNotIn(expected + '\n', f, message)
  672. modfile = os.path.join(tempdir, 'mdadm.8.in')
  673. bb_vars = get_bb_vars(['PKGD', 'mandir'], 'mdadm')
  674. pkgd = bb_vars['PKGD']
  675. self.assertTrue(pkgd, 'Could not query PKGD variable')
  676. mandir = bb_vars['mandir']
  677. self.assertTrue(mandir, 'Could not query mandir variable')
  678. manfile = oe.path.join(pkgd, mandir, 'man8', 'mdadm.8')
  679. check_line(modfile, 'Linux Software RAID', 'Could not find initial string')
  680. check_line(modfile, 'antique pin sardine', 'Unexpectedly found replacement string', present=False)
  681. result = runCmd("sed -i 's!^Linux Software RAID$!antique pin sardine!' %s" % modfile)
  682. check_line(modfile, 'antique pin sardine', 'mdadm.8.in file not modified (sed failed)')
  683. bitbake('mdadm -c package')
  684. check_line(manfile, 'antique pin sardine', 'man file not modified. man searched file path: %s' % manfile)
  685. result = runCmd('git checkout -- %s' % modfile, cwd=tempdir)
  686. check_line(modfile, 'Linux Software RAID', 'man .in file not restored (git failed)')
  687. bitbake('mdadm -c package')
  688. check_line(manfile, 'Linux Software RAID', 'man file not updated. man searched file path: %s' % manfile)
  689. result = runCmd('devtool reset mdadm')
  690. result = runCmd('devtool status')
  691. self.assertNotIn('mdadm', result.output)
  692. def test_devtool_modify_go(self):
  693. import oe.path
  694. from tempfile import TemporaryDirectory
  695. with TemporaryDirectory(prefix='devtoolqa') as tempdir:
  696. self.track_for_cleanup(self.workspacedir)
  697. self.add_command_to_tearDown('bitbake -c clean go-helloworld')
  698. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  699. result = runCmd('devtool modify go-helloworld -x %s' % tempdir)
  700. self.assertExists(
  701. oe.path.join(tempdir, 'src', 'golang.org', 'x', 'example', 'go.mod'),
  702. 'Extracted source could not be found'
  703. )
  704. self.assertExists(
  705. oe.path.join(self.workspacedir, 'conf', 'layer.conf'),
  706. 'Workspace directory not created'
  707. )
  708. matches = glob.glob(oe.path.join(self.workspacedir, 'appends', 'go-helloworld_*.bbappend'))
  709. self.assertTrue(matches, 'bbappend not created %s' % result.output)
  710. def test_devtool_buildclean(self):
  711. def assertFile(path, *paths):
  712. f = os.path.join(path, *paths)
  713. self.assertExists(f)
  714. def assertNoFile(path, *paths):
  715. f = os.path.join(path, *paths)
  716. self.assertNotExists(f)
  717. # Clean up anything in the workdir/sysroot/sstate cache
  718. bitbake('mdadm m4 -c cleansstate')
  719. # Try modifying a recipe
  720. tempdir_mdadm = tempfile.mkdtemp(prefix='devtoolqa')
  721. tempdir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
  722. builddir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
  723. self.track_for_cleanup(tempdir_mdadm)
  724. self.track_for_cleanup(tempdir_m4)
  725. self.track_for_cleanup(builddir_m4)
  726. self.track_for_cleanup(self.workspacedir)
  727. self.add_command_to_tearDown('bitbake -c clean mdadm m4')
  728. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  729. self.write_recipeinc('m4', 'EXTERNALSRC_BUILD = "%s"\ndo_clean() {\n\t:\n}\n' % builddir_m4)
  730. try:
  731. runCmd('devtool modify mdadm -x %s' % tempdir_mdadm)
  732. runCmd('devtool modify m4 -x %s' % tempdir_m4)
  733. assertNoFile(tempdir_mdadm, 'mdadm')
  734. assertNoFile(builddir_m4, 'src/m4')
  735. result = bitbake('m4 -e')
  736. result = bitbake('mdadm m4 -c compile')
  737. self.assertEqual(result.status, 0)
  738. assertFile(tempdir_mdadm, 'mdadm')
  739. assertFile(builddir_m4, 'src/m4')
  740. # Check that buildclean task exists and does call make clean
  741. bitbake('mdadm m4 -c buildclean')
  742. assertNoFile(tempdir_mdadm, 'mdadm')
  743. assertNoFile(builddir_m4, 'src/m4')
  744. runCmd('echo "#Trigger rebuild" >> %s/Makefile' % tempdir_mdadm)
  745. bitbake('mdadm m4 -c compile')
  746. assertFile(tempdir_mdadm, 'mdadm')
  747. assertFile(builddir_m4, 'src/m4')
  748. bitbake('mdadm m4 -c clean')
  749. # Check that buildclean task is run before clean for B == S
  750. assertNoFile(tempdir_mdadm, 'mdadm')
  751. # Check that buildclean task is not run before clean for B != S
  752. assertFile(builddir_m4, 'src/m4')
  753. finally:
  754. self.delete_recipeinc('m4')
  755. def test_devtool_modify_invalid(self):
  756. # Try modifying some recipes
  757. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  758. self.track_for_cleanup(tempdir)
  759. self.track_for_cleanup(self.workspacedir)
  760. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  761. testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk'.split()
  762. # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose
  763. result = runCmd('bitbake-layers show-recipes gcc-source*')
  764. for line in result.output.splitlines():
  765. # just match those lines that contain a real target
  766. m = re.match('(?P<recipe>^[a-zA-Z0-9.-]+)(?P<colon>:$)', line)
  767. if m:
  768. testrecipes.append(m.group('recipe'))
  769. for testrecipe in testrecipes:
  770. # Check it's a valid recipe
  771. bitbake('%s -e' % testrecipe)
  772. # devtool extract should fail
  773. result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
  774. self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed. devtool output: %s' % (testrecipe, result.output))
  775. self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe)
  776. self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe)
  777. # devtool modify should fail
  778. result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
  779. self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed. devtool output: %s' % (testrecipe, result.output))
  780. self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe)
  781. def test_devtool_modify_native(self):
  782. # Check preconditions
  783. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  784. # Try modifying some recipes
  785. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  786. self.track_for_cleanup(tempdir)
  787. self.track_for_cleanup(self.workspacedir)
  788. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  789. bbclassextended = False
  790. inheritnative = False
  791. testrecipes = 'cdrtools-native mtools-native apt-native desktop-file-utils-native'.split()
  792. for testrecipe in testrecipes:
  793. checkextend = 'native' in (get_bb_var('BBCLASSEXTEND', testrecipe) or '').split()
  794. if not bbclassextended:
  795. bbclassextended = checkextend
  796. if not inheritnative:
  797. inheritnative = not checkextend
  798. result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)))
  799. self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool modify output: %s' % result.output)
  800. result = runCmd('devtool build %s' % testrecipe)
  801. self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool build output: %s' % result.output)
  802. result = runCmd('devtool reset %s' % testrecipe)
  803. self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool reset output: %s' % result.output)
  804. self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
  805. self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
  806. def test_devtool_modify_localfiles_only(self):
  807. # Check preconditions
  808. testrecipe = 'base-files'
  809. src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
  810. foundlocalonly = False
  811. correct_symlink = False
  812. for item in src_uri:
  813. if item.startswith('file://'):
  814. if '.patch' not in item:
  815. foundlocalonly = True
  816. else:
  817. foundlocalonly = False
  818. break
  819. self.assertTrue(foundlocalonly, 'This test expects the %s recipe to fetch local files only and it seems that it no longer does' % testrecipe)
  820. # Clean up anything in the workdir/sysroot/sstate cache
  821. bitbake('%s -c cleansstate' % testrecipe)
  822. # Try modifying a recipe
  823. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  824. self.track_for_cleanup(tempdir)
  825. self.track_for_cleanup(self.workspacedir)
  826. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  827. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  828. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  829. srcfile = os.path.join(tempdir, 'share/dot.bashrc')
  830. self.assertExists(srcfile, 'Extracted source could not be found')
  831. matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
  832. self.assertTrue(matches, 'bbappend not created')
  833. # Test devtool status
  834. result = runCmd('devtool status')
  835. self.assertIn(testrecipe, result.output)
  836. self.assertIn(tempdir, result.output)
  837. # Try building
  838. bitbake(testrecipe)
  839. def test_devtool_modify_git(self):
  840. # Check preconditions
  841. testrecipe = 'psplash'
  842. src_uri = get_bb_var('SRC_URI', testrecipe)
  843. self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
  844. # Clean up anything in the workdir/sysroot/sstate cache
  845. bitbake('%s -c cleansstate' % testrecipe)
  846. # Try modifying a recipe
  847. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  848. self.track_for_cleanup(tempdir)
  849. self.track_for_cleanup(self.workspacedir)
  850. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  851. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  852. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  853. self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
  854. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
  855. matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend'))
  856. self.assertTrue(matches, 'bbappend not created')
  857. # Test devtool status
  858. result = runCmd('devtool status')
  859. self.assertIn(testrecipe, result.output)
  860. self.assertIn(tempdir, result.output)
  861. # Check git repo
  862. self._check_src_repo(tempdir)
  863. # Try building
  864. bitbake(testrecipe)
  865. def test_devtool_modify_git_no_extract(self):
  866. # Check preconditions
  867. testrecipe = 'psplash'
  868. src_uri = get_bb_var('SRC_URI', testrecipe)
  869. self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
  870. # Clean up anything in the workdir/sysroot/sstate cache
  871. bitbake('%s -c cleansstate' % testrecipe)
  872. # Try modifying a recipe
  873. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  874. self.track_for_cleanup(tempdir)
  875. self.track_for_cleanup(self.workspacedir)
  876. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  877. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  878. result = runCmd('git clone https://git.yoctoproject.org/psplash %s && devtool modify -n %s %s' % (tempdir, testrecipe, tempdir))
  879. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
  880. matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend'))
  881. self.assertTrue(matches, 'bbappend not created')
  882. # Test devtool status
  883. result = runCmd('devtool status')
  884. self.assertIn(testrecipe, result.output)
  885. self.assertIn(tempdir, result.output)
  886. def test_devtool_modify_git_crates_subpath(self):
  887. # This tests two things in devtool context:
  888. # - that we support local git dependencies for cargo based recipe
  889. # - that we support patches in SRC_URI when git url contains subpath parameter
  890. # Check preconditions:
  891. # recipe inherits cargo
  892. # git:// uri with a subpath as the main package
  893. # some crate:// in SRC_URI
  894. # others git:// in SRC_URI
  895. # cointains a patch
  896. testrecipe = 'hello-rs'
  897. bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'UNPACKDIR', 'CARGO_HOME'], testrecipe)
  898. recipefile = bb_vars['FILE']
  899. unpackdir = bb_vars['UNPACKDIR']
  900. cargo_home = bb_vars['CARGO_HOME']
  901. src_uri = bb_vars['SRC_URI'].split()
  902. self.assertTrue(src_uri[0].startswith('git://'),
  903. 'This test expects the %s recipe to have a git repo has its main uri' % testrecipe)
  904. self.assertIn(';subpath=', src_uri[0],
  905. 'This test expects the %s recipe to have a git uri with subpath' % testrecipe)
  906. self.assertTrue(any([uri.startswith('crate://') for uri in src_uri]),
  907. 'This test expects the %s recipe to have some crates in its src uris' % testrecipe)
  908. self.assertGreaterEqual(sum(map(lambda x:x.startswith('git://'), src_uri)), 2,
  909. 'This test expects the %s recipe to have several git:// uris' % testrecipe)
  910. self.assertTrue(any([uri.startswith('file://') and '.patch' in uri for uri in src_uri]),
  911. 'This test expects the %s recipe to have a patch in its src uris' % testrecipe)
  912. self._test_recipe_contents(recipefile, {}, ['ptest-cargo'])
  913. # Clean up anything in the workdir/sysroot/sstate cache
  914. bitbake('%s -c cleansstate' % testrecipe)
  915. # Try modifying a recipe
  916. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  917. self.track_for_cleanup(tempdir)
  918. self.track_for_cleanup(self.workspacedir)
  919. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  920. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  921. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  922. self.assertExists(os.path.join(tempdir, 'Cargo.toml'), 'Extracted source could not be found')
  923. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
  924. matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
  925. self.assertTrue(matches, 'bbappend not created')
  926. # Test devtool status
  927. result = runCmd('devtool status')
  928. self.assertIn(testrecipe, result.output)
  929. self.assertIn(tempdir, result.output)
  930. # Check git repo
  931. self._check_src_repo(tempdir)
  932. # Check that the patch is correctly applied.
  933. # The last commit message in the tree must contain the following note:
  934. # Notes (devtool):
  935. # original patch: <patchname>
  936. # ..
  937. patchname = None
  938. for uri in src_uri:
  939. if uri.startswith('file://') and '.patch' in uri:
  940. patchname = uri.replace("file://", "").partition('.patch')[0] + '.patch'
  941. self.assertIsNotNone(patchname)
  942. result = runCmd('git -C %s log -1' % tempdir)
  943. self.assertIn("Notes (devtool):\n original patch: %s" % patchname, result.output)
  944. # Configure the recipe to check that the git dependencies are correctly patched in cargo config
  945. bitbake('-c configure %s' % testrecipe)
  946. cargo_config_path = os.path.join(cargo_home, 'config.toml')
  947. with open(cargo_config_path, "r") as f:
  948. cargo_config_contents = [line.strip('\n') for line in f.readlines()]
  949. # Get back git dependencies of the recipe (ignoring the main one)
  950. # and check that they are all correctly patched to be fetched locally
  951. git_deps = [uri for uri in src_uri if uri.startswith("git://")][1:]
  952. for git_dep in git_deps:
  953. raw_url, _, raw_parms = git_dep.partition(";")
  954. parms = {}
  955. for parm in raw_parms.split(";"):
  956. name_parm, _, value_parm = parm.partition('=')
  957. parms[name_parm]=value_parm
  958. self.assertIn('protocol', parms, 'git dependencies uri should contain the "protocol" parameter')
  959. self.assertIn('name', parms, 'git dependencies uri should contain the "name" parameter')
  960. self.assertIn('destsuffix', parms, 'git dependencies uri should contain the "destsuffix" parameter')
  961. self.assertIn('type', parms, 'git dependencies uri should contain the "type" parameter')
  962. self.assertEqual(parms['type'], 'git-dependency', 'git dependencies uri should have "type=git-dependency"')
  963. raw_url = raw_url.replace("git://", '%s://' % parms['protocol'])
  964. patch_line = '[patch."%s"]' % raw_url
  965. path_patched = os.path.join(unpackdir, parms['destsuffix'])
  966. path_override_line = '%s = { path = "%s" }' % (parms['name'], path_patched)
  967. # Would have been better to use tomllib to read this file :/
  968. self.assertIn(patch_line, cargo_config_contents)
  969. self.assertIn(path_override_line, cargo_config_contents)
  970. # Try to package the recipe
  971. bitbake('-c package_qa %s' % testrecipe)
  972. def test_devtool_modify_localfiles(self):
  973. # Check preconditions
  974. testrecipe = 'lighttpd'
  975. src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
  976. foundlocal = False
  977. for item in src_uri:
  978. if item.startswith('file://') and '.patch' not in item:
  979. foundlocal = True
  980. break
  981. self.assertTrue(foundlocal, 'This test expects the %s recipe to fetch local files and it seems that it no longer does' % testrecipe)
  982. # Clean up anything in the workdir/sysroot/sstate cache
  983. bitbake('%s -c cleansstate' % testrecipe)
  984. # Try modifying a recipe
  985. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  986. self.track_for_cleanup(tempdir)
  987. self.track_for_cleanup(self.workspacedir)
  988. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  989. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  990. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  991. self.assertExists(os.path.join(tempdir, 'configure.ac'), 'Extracted source could not be found')
  992. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  993. matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
  994. self.assertTrue(matches, 'bbappend not created')
  995. # Test devtool status
  996. result = runCmd('devtool status')
  997. self.assertIn(testrecipe, result.output)
  998. self.assertIn(tempdir, result.output)
  999. # Try building
  1000. bitbake(testrecipe)
  1001. def test_devtool_modify_virtual(self):
  1002. # Try modifying a virtual recipe
  1003. virtrecipe = 'virtual/make'
  1004. realrecipe = 'make'
  1005. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1006. self.track_for_cleanup(tempdir)
  1007. self.track_for_cleanup(self.workspacedir)
  1008. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1009. result = runCmd('devtool modify %s -x %s' % (virtrecipe, tempdir))
  1010. self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
  1011. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
  1012. matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % realrecipe))
  1013. self.assertTrue(matches, 'bbappend not created %s' % result.output)
  1014. # Test devtool status
  1015. result = runCmd('devtool status')
  1016. self.assertNotIn(virtrecipe, result.output)
  1017. self.assertIn(realrecipe, result.output)
  1018. # Check git repo
  1019. self._check_src_repo(tempdir)
  1020. # This is probably sufficient
  1021. def test_devtool_modify_overrides(self):
  1022. # Try modifying a recipe with patches in overrides
  1023. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1024. self.track_for_cleanup(tempdir)
  1025. self.track_for_cleanup(self.workspacedir)
  1026. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1027. result = runCmd('devtool modify devtool-patch-overrides -x %s' % (tempdir))
  1028. self._check_src_repo(tempdir)
  1029. source = os.path.join(tempdir, "source")
  1030. def check(branch, expected):
  1031. runCmd('git -C %s checkout %s' % (tempdir, branch))
  1032. with open(source, "rt") as f:
  1033. content = f.read()
  1034. self.assertEqual(content, expected)
  1035. if self.td["MACHINE"] == "qemux86":
  1036. check('devtool', 'This is a test for qemux86\n')
  1037. elif self.td["MACHINE"] == "qemuarm":
  1038. check('devtool', 'This is a test for qemuarm\n')
  1039. else:
  1040. check('devtool', 'This is a test for something\n')
  1041. check('devtool-no-overrides', 'This is a test for something\n')
  1042. check('devtool-override-qemuarm', 'This is a test for qemuarm\n')
  1043. check('devtool-override-qemux86', 'This is a test for qemux86\n')
  1044. def test_devtool_modify_multiple_sources(self):
  1045. # This test check that recipes fetching several sources can be used with devtool modify/build
  1046. # Check preconditions
  1047. testrecipe = 'bzip2'
  1048. src_uri = get_bb_var('SRC_URI', testrecipe)
  1049. src1 = 'https://' in src_uri
  1050. src2 = 'git://' in src_uri
  1051. self.assertTrue(src1 and src2, 'This test expects the %s recipe to fetch both a git source and a tarball and it seems that it no longer does' % testrecipe)
  1052. # Clean up anything in the workdir/sysroot/sstate cache
  1053. bitbake('%s -c cleansstate' % testrecipe)
  1054. # Try modifying a recipe
  1055. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1056. self.track_for_cleanup(tempdir)
  1057. self.track_for_cleanup(self.workspacedir)
  1058. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  1059. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1060. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  1061. self.assertEqual(result.status, 0, "Could not modify recipe %s. Output: %s" % (testrecipe, result.output))
  1062. # Test devtool status
  1063. result = runCmd('devtool status')
  1064. self.assertIn(testrecipe, result.output)
  1065. self.assertIn(tempdir, result.output)
  1066. # Try building
  1067. result = bitbake(testrecipe)
  1068. self.assertEqual(result.status, 0, "Bitbake failed, exit code %s, output %s" % (result.status, result.output))
  1069. class DevtoolUpdateTests(DevtoolBase):
  1070. def test_devtool_update_recipe(self):
  1071. # Check preconditions
  1072. testrecipe = 'minicom'
  1073. bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
  1074. recipefile = bb_vars['FILE']
  1075. src_uri = bb_vars['SRC_URI']
  1076. self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
  1077. self._check_repo_status(os.path.dirname(recipefile), [])
  1078. # First, modify a recipe
  1079. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1080. self.track_for_cleanup(tempdir)
  1081. self.track_for_cleanup(self.workspacedir)
  1082. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1083. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1084. # We don't use -x here so that we test the behaviour of devtool modify without it
  1085. result = runCmd('devtool modify %s %s' % (testrecipe, tempdir))
  1086. # Check git repo
  1087. self._check_src_repo(tempdir)
  1088. # Add a couple of commits
  1089. # FIXME: this only tests adding, need to also test update and remove
  1090. result = runCmd('echo "Additional line" >> README', cwd=tempdir)
  1091. result = runCmd('git commit -a -m "Change the README"', cwd=tempdir)
  1092. result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
  1093. result = runCmd('git add devtool-new-file', cwd=tempdir)
  1094. result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
  1095. cleanup_cmd = 'cd %s; rm %s/*.patch; git add %s; git checkout %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))
  1096. self.add_command_to_tearDown(cleanup_cmd)
  1097. result = runCmd('devtool update-recipe %s' % testrecipe)
  1098. result = runCmd('git add minicom', cwd=os.path.dirname(recipefile))
  1099. expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
  1100. ('A ', '.*/0001-Change-the-README.patch$'),
  1101. ('A ', '.*/0002-Add-a-new-file.patch$')]
  1102. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1103. result = runCmd(cleanup_cmd)
  1104. self._check_repo_status(os.path.dirname(recipefile), [])
  1105. def test_devtool_update_recipe_git(self):
  1106. # Check preconditions
  1107. testrecipe = 'mtd-utils-selftest'
  1108. bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
  1109. recipefile = bb_vars['FILE']
  1110. src_uri = bb_vars['SRC_URI']
  1111. self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
  1112. patches = []
  1113. for entry in src_uri.split():
  1114. if entry.startswith('file://') and entry.endswith('.patch'):
  1115. patches.append(entry[7:].split(';')[0])
  1116. self.assertGreater(len(patches), 0, 'The %s recipe does not appear to contain any patches, so this test will not be effective' % testrecipe)
  1117. self._check_repo_status(os.path.dirname(recipefile), [])
  1118. # First, modify a recipe
  1119. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1120. self.track_for_cleanup(tempdir)
  1121. self.track_for_cleanup(self.workspacedir)
  1122. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1123. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1124. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  1125. # Check git repo
  1126. self._check_src_repo(tempdir)
  1127. # Add a couple of commits
  1128. # FIXME: this only tests adding, need to also test update and remove
  1129. result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir)
  1130. result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
  1131. result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
  1132. result = runCmd('git add devtool-new-file', cwd=tempdir)
  1133. result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
  1134. self.add_command_to_tearDown('cd %s; rm -rf %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
  1135. result = runCmd('devtool update-recipe -m srcrev %s' % testrecipe)
  1136. expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile))] + \
  1137. [(' D', '.*/%s$' % patch) for patch in patches]
  1138. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1139. result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
  1140. addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git;branch=master"']
  1141. srcurilines = src_uri.split()
  1142. srcurilines[0] = 'SRC_URI = "' + srcurilines[0]
  1143. srcurilines.append('"')
  1144. removelines = ['SRCREV = ".*"'] + srcurilines
  1145. self._check_diff(result.output, addlines, removelines)
  1146. # Now try with auto mode
  1147. runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
  1148. result = runCmd('devtool update-recipe %s' % testrecipe)
  1149. result = runCmd('git rev-parse --show-toplevel', cwd=os.path.dirname(recipefile))
  1150. topleveldir = result.output.strip()
  1151. relpatchpath = os.path.join(os.path.relpath(os.path.dirname(recipefile), topleveldir), testrecipe)
  1152. expected_status = [(' M', os.path.relpath(recipefile, topleveldir)),
  1153. ('??', '%s/0001-Change-the-Makefile.patch' % relpatchpath),
  1154. ('??', '%s/0002-Add-a-new-file.patch' % relpatchpath)]
  1155. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1156. def test_devtool_update_recipe_append(self):
  1157. # Check preconditions
  1158. testrecipe = 'minicom'
  1159. bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
  1160. recipefile = bb_vars['FILE']
  1161. src_uri = bb_vars['SRC_URI']
  1162. self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
  1163. self._check_repo_status(os.path.dirname(recipefile), [])
  1164. # First, modify a recipe
  1165. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1166. tempsrcdir = os.path.join(tempdir, 'source')
  1167. templayerdir = os.path.join(tempdir, 'layer')
  1168. self.track_for_cleanup(tempdir)
  1169. self.track_for_cleanup(self.workspacedir)
  1170. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1171. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1172. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
  1173. # Check git repo
  1174. self._check_src_repo(tempsrcdir)
  1175. # Add a commit
  1176. result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir)
  1177. result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
  1178. self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
  1179. # Create a temporary layer and add it to bblayers.conf
  1180. self._create_temp_layer(templayerdir, True, 'selftestupdaterecipe')
  1181. # Create the bbappend
  1182. result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
  1183. self.assertNotIn('WARNING:', result.output)
  1184. # Check recipe is still clean
  1185. self._check_repo_status(os.path.dirname(recipefile), [])
  1186. # Check bbappend was created
  1187. splitpath = os.path.dirname(recipefile).split(os.sep)
  1188. appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
  1189. bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
  1190. patchfile = os.path.join(appenddir, testrecipe, '0001-Add-our-custom-version.patch')
  1191. self.assertExists(patchfile, 'Patch file not created')
  1192. # Check bbappend contents
  1193. expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
  1194. '\n',
  1195. 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n',
  1196. '\n']
  1197. with open(bbappendfile, 'r') as f:
  1198. self.assertEqual(expectedlines, f.readlines())
  1199. # Check we can run it again and bbappend isn't modified
  1200. result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
  1201. with open(bbappendfile, 'r') as f:
  1202. self.assertEqual(expectedlines, f.readlines())
  1203. # Drop new commit and check patch gets deleted
  1204. result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir)
  1205. result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
  1206. self.assertNotExists(patchfile, 'Patch file not deleted')
  1207. expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
  1208. '\n']
  1209. with open(bbappendfile, 'r') as f:
  1210. self.assertEqual(expectedlines2, f.readlines())
  1211. # Put commit back and check we can run it if layer isn't in bblayers.conf
  1212. os.remove(bbappendfile)
  1213. result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir)
  1214. result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
  1215. result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
  1216. result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
  1217. self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
  1218. self.assertExists(patchfile, 'Patch file not created (with disabled layer)')
  1219. with open(bbappendfile, 'r') as f:
  1220. self.assertEqual(expectedlines, f.readlines())
  1221. # Deleting isn't expected to work under these circumstances
  1222. def test_devtool_update_recipe_append_git(self):
  1223. # Check preconditions
  1224. testrecipe = 'mtd-utils-selftest'
  1225. bb_vars = get_bb_vars(['FILE', 'SRC_URI', 'LAYERSERIES_CORENAMES'], testrecipe)
  1226. recipefile = bb_vars['FILE']
  1227. src_uri = bb_vars['SRC_URI']
  1228. corenames = bb_vars['LAYERSERIES_CORENAMES']
  1229. self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
  1230. for entry in src_uri.split():
  1231. if entry.startswith('git://'):
  1232. git_uri = entry
  1233. break
  1234. self._check_repo_status(os.path.dirname(recipefile), [])
  1235. # First, modify a recipe
  1236. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1237. tempsrcdir = os.path.join(tempdir, 'source')
  1238. templayerdir = os.path.join(tempdir, 'layer')
  1239. self.track_for_cleanup(tempdir)
  1240. self.track_for_cleanup(self.workspacedir)
  1241. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1242. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1243. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
  1244. # Check git repo
  1245. self._check_src_repo(tempsrcdir)
  1246. # Add a commit
  1247. result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
  1248. result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
  1249. self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
  1250. # Create a temporary layer
  1251. os.makedirs(os.path.join(templayerdir, 'conf'))
  1252. with open(os.path.join(templayerdir, 'conf', 'layer.conf'), 'w') as f:
  1253. f.write('BBPATH .= ":${LAYERDIR}"\n')
  1254. f.write('BBFILES += "${LAYERDIR}/recipes-*/*/*.bbappend"\n')
  1255. f.write('BBFILE_COLLECTIONS += "oeselftesttemplayer"\n')
  1256. f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n')
  1257. f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n')
  1258. f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n')
  1259. f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "%s"\n' % corenames)
  1260. self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
  1261. result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
  1262. # Create the bbappend
  1263. result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
  1264. self.assertNotIn('WARNING:', result.output)
  1265. # Check recipe is still clean
  1266. self._check_repo_status(os.path.dirname(recipefile), [])
  1267. # Check bbappend was created
  1268. splitpath = os.path.dirname(recipefile).split(os.sep)
  1269. appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
  1270. bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
  1271. self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
  1272. # Check bbappend contents
  1273. result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
  1274. expectedlines = set(['SRCREV = "%s"\n' % result.output,
  1275. '\n',
  1276. 'SRC_URI = "%s"\n' % git_uri,
  1277. '\n'])
  1278. with open(bbappendfile, 'r') as f:
  1279. self.assertEqual(expectedlines, set(f.readlines()))
  1280. # Check we can run it again and bbappend isn't modified
  1281. result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
  1282. with open(bbappendfile, 'r') as f:
  1283. self.assertEqual(expectedlines, set(f.readlines()))
  1284. # Drop new commit and check SRCREV changes
  1285. result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir)
  1286. result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
  1287. self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
  1288. result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
  1289. expectedlines = set(['SRCREV = "%s"\n' % result.output,
  1290. '\n',
  1291. 'SRC_URI = "%s"\n' % git_uri,
  1292. '\n'])
  1293. with open(bbappendfile, 'r') as f:
  1294. self.assertEqual(expectedlines, set(f.readlines()))
  1295. # Put commit back and check we can run it if layer isn't in bblayers.conf
  1296. os.remove(bbappendfile)
  1297. result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
  1298. result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
  1299. result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
  1300. result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
  1301. self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
  1302. self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
  1303. result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
  1304. expectedlines = set(['SRCREV = "%s"\n' % result.output,
  1305. '\n',
  1306. 'SRC_URI = "%s"\n' % git_uri,
  1307. '\n'])
  1308. with open(bbappendfile, 'r') as f:
  1309. self.assertEqual(expectedlines, set(f.readlines()))
  1310. # Deleting isn't expected to work under these circumstances
  1311. def test_devtool_update_recipe_local_files(self):
  1312. """Check that local source files are copied over instead of patched"""
  1313. testrecipe = 'makedevs'
  1314. recipefile = get_bb_var('FILE', testrecipe)
  1315. # Setup srctree for modifying the recipe
  1316. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1317. self.track_for_cleanup(tempdir)
  1318. self.track_for_cleanup(self.workspacedir)
  1319. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1320. # (don't bother with cleaning the recipe on teardown, we won't be
  1321. # building it)
  1322. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  1323. # Check git repo
  1324. self._check_src_repo(tempdir)
  1325. # Try building just to ensure we haven't broken that
  1326. bitbake("%s" % testrecipe)
  1327. # Edit / commit local source
  1328. runCmd('echo "/* Foobar */" >> makedevs.c', cwd=tempdir)
  1329. runCmd('echo "Foo" > new-local', cwd=tempdir)
  1330. runCmd('echo "Bar" > new-file', cwd=tempdir)
  1331. runCmd('git add new-file', cwd=tempdir)
  1332. runCmd('git commit -m "Add new file"', cwd=tempdir)
  1333. runCmd('git add new-local', cwd=tempdir)
  1334. runCmd('devtool update-recipe %s' % testrecipe)
  1335. expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
  1336. (' M', '.*/makedevs/makedevs.c$'),
  1337. ('??', '.*/makedevs/new-local$'),
  1338. ('??', '.*/makedevs/0001-Add-new-file.patch$')]
  1339. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1340. # Now try to update recipe in another layer, so first, clean it
  1341. runCmd('cd %s; git clean -fd .; git checkout .' % os.path.dirname(recipefile))
  1342. # Create a temporary layer and add it to bblayers.conf
  1343. self._create_temp_layer(templayerdir, True, 'templayer')
  1344. # Update recipe in templayer
  1345. result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
  1346. self.assertNotIn('WARNING:', result.output)
  1347. # Check recipe is still clean
  1348. self._check_repo_status(os.path.dirname(recipefile), [])
  1349. splitpath = os.path.dirname(recipefile).split(os.sep)
  1350. appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
  1351. bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
  1352. patchfile = os.path.join(appenddir, testrecipe, '0001-Add-new-file.patch')
  1353. new_local_file = os.path.join(appenddir, testrecipe, 'new_local')
  1354. local_file = os.path.join(appenddir, testrecipe, 'makedevs.c')
  1355. self.assertExists(patchfile, 'Patch file 0001-Add-new-file.patch not created')
  1356. self.assertExists(local_file, 'File makedevs.c not created')
  1357. self.assertExists(patchfile, 'File new_local not created')
  1358. def _test_devtool_update_recipe_local_files_2(self):
  1359. """Check local source files support when editing local files in Git"""
  1360. testrecipe = 'devtool-test-local'
  1361. recipefile = get_bb_var('FILE', testrecipe)
  1362. recipedir = os.path.dirname(recipefile)
  1363. result = runCmd('git status --porcelain .', cwd=recipedir)
  1364. if result.output.strip():
  1365. self.fail('Recipe directory for %s contains uncommitted changes' % testrecipe)
  1366. # Setup srctree for modifying the recipe
  1367. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1368. self.track_for_cleanup(tempdir)
  1369. self.track_for_cleanup(self.workspacedir)
  1370. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1371. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  1372. # Check git repo
  1373. self._check_src_repo(tempdir)
  1374. # Edit / commit local sources
  1375. runCmd('echo "# Foobar" >> file1', cwd=tempdir)
  1376. runCmd('git commit -am "Edit existing file"', cwd=tempdir)
  1377. runCmd('git rm file2', cwd=tempdir)
  1378. runCmd('git commit -m"Remove file"', cwd=tempdir)
  1379. runCmd('echo "Foo" > new-local', cwd=tempdir)
  1380. runCmd('git add new-local', cwd=tempdir)
  1381. runCmd('git commit -m "Add new local file"', cwd=tempdir)
  1382. runCmd('echo "Gar" > new-file', cwd=tempdir)
  1383. runCmd('git add new-file', cwd=tempdir)
  1384. runCmd('git commit -m "Add new file"', cwd=tempdir)
  1385. self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
  1386. os.path.dirname(recipefile))
  1387. # Checkout unmodified file to working copy -> devtool should still pick
  1388. # the modified version from HEAD
  1389. runCmd('git checkout HEAD^ -- file1', cwd=tempdir)
  1390. runCmd('devtool update-recipe %s' % testrecipe)
  1391. expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
  1392. (' M', '.*/file1$'),
  1393. (' D', '.*/file2$'),
  1394. ('??', '.*/new-local$'),
  1395. ('??', '.*/0001-Add-new-file.patch$')]
  1396. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1397. def test_devtool_update_recipe_with_gitignore(self):
  1398. # First, modify the recipe
  1399. testrecipe = 'devtool-test-ignored'
  1400. bb_vars = get_bb_vars(['FILE'], testrecipe)
  1401. recipefile = bb_vars['FILE']
  1402. patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch')
  1403. newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch.expected')
  1404. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1405. self.track_for_cleanup(tempdir)
  1406. self.track_for_cleanup(self.workspacedir)
  1407. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1408. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1409. result = runCmd('devtool modify %s' % testrecipe)
  1410. self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
  1411. result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe)
  1412. # Check recipe got changed as expected
  1413. with open(newpatchfile, 'r') as f:
  1414. desiredlines = f.readlines()
  1415. with open(patchfile, 'r') as f:
  1416. newlines = f.readlines()
  1417. # Ignore the initial lines, because oe-selftest creates own meta-selftest repo
  1418. # which changes the metadata subject which is added into the patch, but keep
  1419. # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh
  1420. # devtool-test-ignored manually, then it should generate exactly the same .patch file
  1421. self.assertEqual(desiredlines[5:], newlines[5:])
  1422. def test_devtool_update_recipe_long_filename(self):
  1423. # First, modify the recipe
  1424. testrecipe = 'devtool-test-long-filename'
  1425. bb_vars = get_bb_vars(['FILE'], testrecipe)
  1426. recipefile = bb_vars['FILE']
  1427. patchfilename = '0001-I-ll-patch-you-only-if-devtool-lets-me-to-do-it-corr.patch'
  1428. patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename)
  1429. newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename + '.expected')
  1430. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1431. self.track_for_cleanup(tempdir)
  1432. self.track_for_cleanup(self.workspacedir)
  1433. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1434. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1435. result = runCmd('devtool modify %s' % testrecipe)
  1436. self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
  1437. result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe)
  1438. # Check recipe got changed as expected
  1439. with open(newpatchfile, 'r') as f:
  1440. desiredlines = f.readlines()
  1441. with open(patchfile, 'r') as f:
  1442. newlines = f.readlines()
  1443. # Ignore the initial lines, because oe-selftest creates own meta-selftest repo
  1444. # which changes the metadata subject which is added into the patch, but keep
  1445. # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh
  1446. # devtool-test-ignored manually, then it should generate exactly the same .patch file
  1447. self.assertEqual(desiredlines[5:], newlines[5:])
  1448. def test_devtool_update_recipe_local_files_3(self):
  1449. # First, modify the recipe
  1450. testrecipe = 'devtool-test-localonly'
  1451. bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
  1452. recipefile = bb_vars['FILE']
  1453. src_uri = bb_vars['SRC_URI']
  1454. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1455. self.track_for_cleanup(tempdir)
  1456. self.track_for_cleanup(self.workspacedir)
  1457. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1458. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1459. result = runCmd('devtool modify %s' % testrecipe)
  1460. # Modify one file
  1461. runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe))
  1462. self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
  1463. result = runCmd('devtool update-recipe %s' % testrecipe)
  1464. expected_status = [(' M', '.*/%s/file2$' % testrecipe)]
  1465. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1466. def test_devtool_update_recipe_local_patch_gz(self):
  1467. # First, modify the recipe
  1468. testrecipe = 'devtool-test-patch-gz'
  1469. if get_bb_var('DISTRO') == 'poky-tiny':
  1470. self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe)
  1471. bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
  1472. recipefile = bb_vars['FILE']
  1473. src_uri = bb_vars['SRC_URI']
  1474. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1475. self.track_for_cleanup(tempdir)
  1476. self.track_for_cleanup(self.workspacedir)
  1477. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1478. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1479. result = runCmd('devtool modify %s' % testrecipe)
  1480. # Modify one file
  1481. srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
  1482. runCmd('echo "Another line" >> README', cwd=srctree)
  1483. runCmd('git commit -a --amend --no-edit --no-verify', cwd=srctree)
  1484. self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
  1485. result = runCmd('devtool update-recipe %s' % testrecipe)
  1486. expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
  1487. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1488. patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz')
  1489. result = runCmd('file %s' % patch_gz)
  1490. if 'gzip compressed data' not in result.output:
  1491. self.fail('New patch file is not gzipped - file reports:\n%s' % result.output)
  1492. def test_devtool_update_recipe_local_files_subdir(self):
  1493. # Try devtool update-recipe on a recipe that has a file with subdir= set in
  1494. # SRC_URI such that it overwrites a file that was in an archive that
  1495. # was also in SRC_URI
  1496. # First, modify the recipe
  1497. testrecipe = 'devtool-test-subdir'
  1498. bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
  1499. recipefile = bb_vars['FILE']
  1500. src_uri = bb_vars['SRC_URI']
  1501. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1502. self.track_for_cleanup(tempdir)
  1503. self.track_for_cleanup(self.workspacedir)
  1504. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1505. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  1506. result = runCmd('devtool modify %s' % testrecipe)
  1507. testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile')
  1508. self.assertExists(testfile, 'Extracted source could not be found')
  1509. with open(testfile, 'r') as f:
  1510. contents = f.read().rstrip()
  1511. self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been')
  1512. # Test devtool update-recipe without modifying any files
  1513. self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
  1514. result = runCmd('devtool update-recipe %s' % testrecipe)
  1515. expected_status = []
  1516. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1517. def test_devtool_finish_modify_git_subdir(self):
  1518. # Check preconditions
  1519. testrecipe = 'dos2unix'
  1520. self.append_config('ERROR_QA:remove:pn-dos2unix = "patch-status"\n')
  1521. bb_vars = get_bb_vars(['SRC_URI', 'S', 'UNPACKDIR', 'FILE', 'BB_GIT_DEFAULT_DESTSUFFIX'], testrecipe)
  1522. self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe)
  1523. unpackdir_git = '%s/%s/' % (bb_vars['UNPACKDIR'], bb_vars['BB_GIT_DEFAULT_DESTSUFFIX'])
  1524. if not bb_vars['S'].startswith(unpackdir_git):
  1525. self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe)
  1526. subdir = bb_vars['S'].split(unpackdir_git, 1)[1]
  1527. # Clean up anything in the workdir/sysroot/sstate cache
  1528. bitbake('%s -c cleansstate' % testrecipe)
  1529. # Try modifying a recipe
  1530. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1531. self.track_for_cleanup(tempdir)
  1532. self.track_for_cleanup(self.workspacedir)
  1533. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  1534. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1535. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  1536. testsrcfile = os.path.join(tempdir, subdir, 'dos2unix.c')
  1537. self.assertExists(testsrcfile, 'Extracted source could not be found')
  1538. self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
  1539. self.assertNotExists(os.path.join(tempdir, subdir, '.git'), 'Subdirectory has been initialised as a git repo')
  1540. # Check git repo
  1541. self._check_src_repo(tempdir)
  1542. # Modify file
  1543. runCmd("sed -i '1s:^:/* Add a comment */\\n:' %s" % testsrcfile)
  1544. result = runCmd('git commit -a -m "Add a comment"', cwd=tempdir)
  1545. # Now try updating original recipe
  1546. recipefile = bb_vars['FILE']
  1547. recipedir = os.path.dirname(recipefile)
  1548. self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
  1549. result = runCmd('devtool update-recipe %s' % testrecipe)
  1550. expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
  1551. ('??', '.*/%s/%s/$' % (testrecipe, testrecipe))]
  1552. self._check_repo_status(os.path.dirname(recipefile), expected_status)
  1553. result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
  1554. removelines = ['SRC_URI = "git://.*"']
  1555. addlines = [
  1556. 'SRC_URI = "git://.* \\\\',
  1557. 'file://0001-Add-a-comment.patch;patchdir=.. \\\\',
  1558. '"'
  1559. ]
  1560. self._check_diff(result.output, addlines, removelines)
  1561. # Put things back so we can run devtool finish on a different layer
  1562. runCmd('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
  1563. # Run devtool finish
  1564. res = re.search('recipes-.*', recipedir)
  1565. self.assertTrue(res, 'Unable to find recipe subdirectory')
  1566. recipesubdir = res[0]
  1567. self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, recipesubdir))
  1568. result = runCmd('devtool finish %s meta-selftest' % testrecipe)
  1569. # Check bbappend file contents
  1570. appendfn = os.path.join(self.testlayer_path, recipesubdir, '%s_%%.bbappend' % testrecipe)
  1571. with open(appendfn, 'r') as f:
  1572. appendlines = f.readlines()
  1573. expected_appendlines = [
  1574. 'FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
  1575. '\n',
  1576. 'SRC_URI += "file://0001-Add-a-comment.patch;patchdir=.."\n',
  1577. '\n'
  1578. ]
  1579. self.assertEqual(appendlines, expected_appendlines)
  1580. self.assertExists(os.path.join(os.path.dirname(appendfn), testrecipe, '0001-Add-a-comment.patch'))
  1581. # Try building
  1582. bitbake('%s -c patch' % testrecipe)
  1583. def test_devtool_git_submodules(self):
  1584. # This tests if we can add a patch in a git submodule and extract it properly using devtool finish
  1585. # Check preconditions
  1586. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1587. self.track_for_cleanup(self.workspacedir)
  1588. recipe = 'vulkan-samples'
  1589. src_uri = get_bb_var('SRC_URI', recipe)
  1590. self.assertIn('gitsm://', src_uri, 'This test expects the %s recipe to be a git recipe with submodules' % recipe)
  1591. oldrecipefile = get_bb_var('FILE', recipe)
  1592. recipedir = os.path.dirname(oldrecipefile)
  1593. result = runCmd('git status --porcelain .', cwd=recipedir)
  1594. if result.output.strip():
  1595. self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
  1596. self.assertIn('/meta/', recipedir)
  1597. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1598. self.track_for_cleanup(tempdir)
  1599. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1600. result = runCmd('devtool modify %s %s' % (recipe, tempdir))
  1601. self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
  1602. # Test devtool status
  1603. result = runCmd('devtool status')
  1604. self.assertIn(recipe, result.output)
  1605. self.assertIn(tempdir, result.output)
  1606. # Modify a source file in a submodule, (grab the first one)
  1607. result = runCmd('git submodule --quiet foreach \'echo $sm_path\'', cwd=tempdir)
  1608. submodule = result.output.splitlines()[0]
  1609. submodule_path = os.path.join(tempdir, submodule)
  1610. runCmd('echo "#This is a first comment" >> testfile', cwd=submodule_path)
  1611. result = runCmd('git status --porcelain . ', cwd=submodule_path)
  1612. self.assertIn("testfile", result.output)
  1613. runCmd('git add testfile; git commit -m "Adding a new file"', cwd=submodule_path)
  1614. # Try finish to the original layer
  1615. self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
  1616. runCmd('devtool finish -f %s meta' % recipe)
  1617. result = runCmd('devtool status')
  1618. self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
  1619. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
  1620. expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
  1621. ('??', '.*/.*-Adding-a-new-file.patch$')]
  1622. self._check_repo_status(recipedir, expected_status)
  1623. # Make sure the patch is added to the recipe with the correct "patchdir" option
  1624. result = runCmd('git diff .', cwd=recipedir)
  1625. addlines = [
  1626. 'file://0001-Adding-a-new-file.patch;patchdir=%s \\\\' % submodule
  1627. ]
  1628. self._check_diff(result.output, addlines, [])
  1629. class DevtoolExtractTests(DevtoolBase):
  1630. def test_devtool_extract(self):
  1631. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1632. # Try devtool extract
  1633. self.track_for_cleanup(tempdir)
  1634. self.track_for_cleanup(self.workspacedir)
  1635. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1636. result = runCmd('devtool extract matchbox-terminal %s' % tempdir)
  1637. self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
  1638. self._check_src_repo(tempdir)
  1639. def test_devtool_extract_virtual(self):
  1640. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1641. # Try devtool extract
  1642. self.track_for_cleanup(tempdir)
  1643. self.track_for_cleanup(self.workspacedir)
  1644. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1645. result = runCmd('devtool extract virtual/make %s' % tempdir)
  1646. self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
  1647. self._check_src_repo(tempdir)
  1648. class DevtoolResetTests(DevtoolBase):
  1649. def test_devtool_reset_all(self):
  1650. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1651. self.track_for_cleanup(tempdir)
  1652. self.track_for_cleanup(self.workspacedir)
  1653. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1654. testrecipe1 = 'mdadm'
  1655. testrecipe2 = 'cronie'
  1656. result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1)))
  1657. result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2)))
  1658. result = runCmd('devtool build %s' % testrecipe1)
  1659. result = runCmd('devtool build %s' % testrecipe2)
  1660. stampprefix1 = get_bb_var('STAMP', testrecipe1)
  1661. self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1)
  1662. stampprefix2 = get_bb_var('STAMP', testrecipe2)
  1663. self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2)
  1664. result = runCmd('devtool reset -a')
  1665. self.assertIn(testrecipe1, result.output)
  1666. self.assertIn(testrecipe2, result.output)
  1667. result = runCmd('devtool status')
  1668. self.assertNotIn(testrecipe1, result.output)
  1669. self.assertNotIn(testrecipe2, result.output)
  1670. matches1 = glob.glob(stampprefix1 + '*')
  1671. self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1)
  1672. matches2 = glob.glob(stampprefix2 + '*')
  1673. self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
  1674. def test_devtool_reset_re_plus_plus(self):
  1675. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1676. self.track_for_cleanup(tempdir)
  1677. self.track_for_cleanup(self.workspacedir)
  1678. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1679. testrecipe = 'devtool-test-reset-re++'
  1680. result = runCmd('devtool modify %s' % testrecipe)
  1681. result = runCmd('devtool reset -n %s' % testrecipe)
  1682. self.assertIn(testrecipe, result.output)
  1683. result = runCmd('devtool status')
  1684. self.assertNotIn(testrecipe, result.output)
  1685. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', testrecipe), 'Recipe directory should not exist after resetting')
  1686. class DevtoolDeployTargetTests(DevtoolBase):
  1687. @OETestTag("runqemu")
  1688. def test_devtool_deploy_target(self):
  1689. self._check_runqemu_prerequisites()
  1690. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1691. # Definitions
  1692. testrecipe = 'mdadm'
  1693. testfile = '/sbin/mdadm'
  1694. if "usrmerge" in get_bb_var('DISTRO_FEATURES'):
  1695. testfile = '/usr/sbin/mdadm'
  1696. testimage = 'oe-selftest-image'
  1697. testcommand = '/sbin/mdadm --help'
  1698. # Build an image to run
  1699. bitbake("%s qemu-native qemu-helper-native" % testimage)
  1700. deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
  1701. self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
  1702. self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage))
  1703. # Clean recipe so the first deploy will fail
  1704. bitbake("%s -c clean" % testrecipe)
  1705. # Try devtool modify
  1706. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1707. self.track_for_cleanup(tempdir)
  1708. self.track_for_cleanup(self.workspacedir)
  1709. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  1710. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1711. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  1712. # Test that deploy-target at this point fails (properly)
  1713. result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe, ignore_status=True)
  1714. self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output)
  1715. self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output)
  1716. result = runCmd('devtool build %s' % testrecipe)
  1717. # First try a dry-run of deploy-target
  1718. result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe)
  1719. self.assertIn(' %s' % testfile, result.output)
  1720. # Boot the image
  1721. with runqemu(testimage) as qemu:
  1722. # Now really test deploy-target
  1723. for extra_opt in ['', '--strip']:
  1724. deploy_cmd= 'devtool deploy-target -c %s root@%s %s' % (testrecipe, qemu.ip, extra_opt)
  1725. self.logger.debug(deploy_cmd)
  1726. result = runCmd(deploy_cmd)
  1727. # Run a test command to see if it was installed properly
  1728. sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
  1729. result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
  1730. # Check if it deployed all of the files with the right ownership/perms
  1731. # First look on the host - need to do this under pseudo to get the correct ownership/perms
  1732. bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe)
  1733. installdir = bb_vars['D']
  1734. fakerootenv = bb_vars['FAKEROOTENV']
  1735. fakerootcmd = bb_vars['FAKEROOTCMD']
  1736. result = runCmd('%s %s find . -type f -exec ls -l {} \\;' % (fakerootenv, fakerootcmd), cwd=installdir)
  1737. filelist1 = self._process_ls_output(result.output)
  1738. # Now look on the target
  1739. tempdir2 = tempfile.mkdtemp(prefix='devtoolqa')
  1740. self.track_for_cleanup(tempdir2)
  1741. tmpfilelist = os.path.join(tempdir2, 'files.txt')
  1742. with open(tmpfilelist, 'w') as f:
  1743. for line in filelist1:
  1744. splitline = line.split()
  1745. f.write(splitline[-1] + '\n')
  1746. result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip))
  1747. filelist2 = self._process_ls_output(result.output)
  1748. filelist1.sort(key=lambda item: item.split()[-1])
  1749. filelist2.sort(key=lambda item: item.split()[-1])
  1750. self.assertEqual(filelist1, filelist2)
  1751. # Test undeploy-target
  1752. result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip))
  1753. result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
  1754. self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
  1755. class DevtoolBuildImageTests(DevtoolBase):
  1756. def test_devtool_build_image(self):
  1757. """Test devtool build-image plugin"""
  1758. # Check preconditions
  1759. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1760. image = 'core-image-minimal'
  1761. self.track_for_cleanup(self.workspacedir)
  1762. self.add_command_to_tearDown('bitbake -c clean %s' % image)
  1763. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1764. bitbake('%s -c clean' % image)
  1765. # Add target and native recipes to workspace
  1766. recipes = ['mdadm', 'parted-native']
  1767. for recipe in recipes:
  1768. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1769. self.track_for_cleanup(tempdir)
  1770. self.add_command_to_tearDown('bitbake -c clean %s' % recipe)
  1771. runCmd('devtool modify %s -x %s' % (recipe, tempdir))
  1772. # Try to build image
  1773. result = runCmd('devtool build-image %s' % image)
  1774. self.assertNotEqual(result, 0, 'devtool build-image failed')
  1775. # Check if image contains expected packages
  1776. deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
  1777. image_link_name = get_bb_var('IMAGE_LINK_NAME', image)
  1778. reqpkgs = [item for item in recipes if not item.endswith('-native')]
  1779. with open(os.path.join(deploy_dir_image, image_link_name + '.manifest'), 'r') as f:
  1780. for line in f:
  1781. splitval = line.split()
  1782. if splitval:
  1783. pkg = splitval[0]
  1784. if pkg in reqpkgs:
  1785. reqpkgs.remove(pkg)
  1786. if reqpkgs:
  1787. self.fail('The following packages were not present in the image as expected: %s' % ', '.join(reqpkgs))
  1788. class DevtoolUpgradeTests(DevtoolBase):
  1789. def setUp(self):
  1790. super().setUp()
  1791. try:
  1792. runCmd("git config --global user.name")
  1793. runCmd("git config --global user.email")
  1794. except:
  1795. self.skip("Git user.name and user.email must be set")
  1796. def test_devtool_upgrade(self):
  1797. # Check preconditions
  1798. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1799. self.track_for_cleanup(self.workspacedir)
  1800. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1801. # Check parameters
  1802. result = runCmd('devtool upgrade -h')
  1803. for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split():
  1804. self.assertIn(param, result.output)
  1805. # For the moment, we are using a real recipe.
  1806. recipe = 'devtool-upgrade-test1'
  1807. version = '1.6.0'
  1808. oldrecipefile = get_bb_var('FILE', recipe)
  1809. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1810. self.track_for_cleanup(tempdir)
  1811. # Check that recipe is not already under devtool control
  1812. result = runCmd('devtool status')
  1813. self.assertNotIn(recipe, result.output)
  1814. # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
  1815. # we are downgrading instead of upgrading.
  1816. result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
  1817. # Check if srctree at least is populated
  1818. self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, version))
  1819. # Check new recipe subdirectory is present
  1820. self.assertExists(os.path.join(self.workspacedir, 'recipes', recipe, '%s-%s' % (recipe, version)), 'Recipe folder should exist')
  1821. # Check new recipe file is present
  1822. newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
  1823. self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
  1824. # Check devtool status and make sure recipe is present
  1825. result = runCmd('devtool status')
  1826. self.assertIn(recipe, result.output)
  1827. self.assertIn(tempdir, result.output)
  1828. # Check recipe got changed as expected
  1829. with open(oldrecipefile + '.upgraded', 'r') as f:
  1830. desiredlines = f.readlines()
  1831. with open(newrecipefile, 'r') as f:
  1832. newlines = f.readlines()
  1833. self.assertEqual(desiredlines, newlines)
  1834. # Check devtool reset recipe
  1835. result = runCmd('devtool reset %s -n' % recipe)
  1836. result = runCmd('devtool status')
  1837. self.assertNotIn(recipe, result.output)
  1838. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
  1839. def _test_devtool_upgrade_git_by_recipe(self, recipe, commit):
  1840. # Check preconditions
  1841. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1842. self.track_for_cleanup(self.workspacedir)
  1843. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1844. oldrecipefile = get_bb_var('FILE', recipe)
  1845. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1846. self.track_for_cleanup(tempdir)
  1847. # Check that recipe is not already under devtool control
  1848. result = runCmd('devtool status')
  1849. self.assertNotIn(recipe, result.output)
  1850. # Check upgrade
  1851. result = runCmd('devtool upgrade %s %s -S %s' % (recipe, tempdir, commit))
  1852. # Check if srctree at least is populated
  1853. self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit))
  1854. # Check new recipe file is present
  1855. newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile))
  1856. self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
  1857. # Check devtool status and make sure recipe is present
  1858. result = runCmd('devtool status')
  1859. self.assertIn(recipe, result.output)
  1860. self.assertIn(tempdir, result.output)
  1861. # Check recipe got changed as expected
  1862. with open(oldrecipefile + '.upgraded', 'r') as f:
  1863. desiredlines = f.readlines()
  1864. with open(newrecipefile, 'r') as f:
  1865. newlines = f.readlines()
  1866. self.assertEqual(desiredlines, newlines)
  1867. # Check devtool reset recipe
  1868. result = runCmd('devtool reset %s -n' % recipe)
  1869. result = runCmd('devtool status')
  1870. self.assertNotIn(recipe, result.output)
  1871. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
  1872. def test_devtool_upgrade_git(self):
  1873. self._test_devtool_upgrade_git_by_recipe('devtool-upgrade-test2', '6cc6077a36fe2648a5f993fe7c16c9632f946517')
  1874. def test_devtool_upgrade_gitsm(self):
  1875. self._test_devtool_upgrade_git_by_recipe('devtool-upgrade-test5', 'a2885dd7d25380d23627e7544b7bbb55014b16ee')
  1876. def test_devtool_upgrade_drop_md5sum(self):
  1877. # Check preconditions
  1878. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1879. self.track_for_cleanup(self.workspacedir)
  1880. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1881. # For the moment, we are using a real recipe.
  1882. recipe = 'devtool-upgrade-test3'
  1883. version = '1.6.0'
  1884. oldrecipefile = get_bb_var('FILE', recipe)
  1885. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1886. self.track_for_cleanup(tempdir)
  1887. # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
  1888. # we are downgrading instead of upgrading.
  1889. result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
  1890. # Check new recipe file is present
  1891. newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
  1892. self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
  1893. # Check recipe got changed as expected
  1894. with open(oldrecipefile + '.upgraded', 'r') as f:
  1895. desiredlines = f.readlines()
  1896. with open(newrecipefile, 'r') as f:
  1897. newlines = f.readlines()
  1898. self.assertEqual(desiredlines, newlines)
  1899. def test_devtool_upgrade_all_checksums(self):
  1900. # Check preconditions
  1901. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1902. self.track_for_cleanup(self.workspacedir)
  1903. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1904. # For the moment, we are using a real recipe.
  1905. recipe = 'devtool-upgrade-test4'
  1906. version = '1.6.0'
  1907. oldrecipefile = get_bb_var('FILE', recipe)
  1908. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1909. self.track_for_cleanup(tempdir)
  1910. # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
  1911. # we are downgrading instead of upgrading.
  1912. result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
  1913. # Check new recipe file is present
  1914. newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
  1915. self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
  1916. # Check recipe got changed as expected
  1917. with open(oldrecipefile + '.upgraded', 'r') as f:
  1918. desiredlines = f.readlines()
  1919. with open(newrecipefile, 'r') as f:
  1920. newlines = f.readlines()
  1921. self.assertEqual(desiredlines, newlines)
  1922. def test_devtool_upgrade_recipe_upgrade_extra_tasks(self):
  1923. # Check preconditions
  1924. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  1925. self.track_for_cleanup(self.workspacedir)
  1926. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1927. recipe = 'python3-guessing-game'
  1928. version = '0.2.0'
  1929. commit = '40cf004c2772ffa20ea803fa3be1528a75be3e98'
  1930. oldrecipefile = get_bb_var('FILE', recipe)
  1931. oldcratesincfile = os.path.join(os.path.dirname(oldrecipefile), os.path.basename(oldrecipefile).strip('_git.bb') + '-crates.inc')
  1932. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  1933. self.track_for_cleanup(tempdir)
  1934. # Check that recipe is not already under devtool control
  1935. result = runCmd('devtool status')
  1936. self.assertNotIn(recipe, result.output)
  1937. # Check upgrade
  1938. result = runCmd('devtool upgrade %s %s --version %s --srcrev %s' % (recipe, tempdir, version, commit))
  1939. # Check if srctree at least is populated
  1940. self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit))
  1941. # Check new recipe file and new -crates.inc files are present
  1942. newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile))
  1943. newcratesincfile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldcratesincfile))
  1944. self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
  1945. self.assertExists(newcratesincfile, 'Recipe crates.inc file should exist after upgrade')
  1946. # Check devtool status and make sure recipe is present
  1947. result = runCmd('devtool status')
  1948. self.assertIn(recipe, result.output)
  1949. self.assertIn(tempdir, result.output)
  1950. # Check recipe got changed as expected
  1951. with open(oldrecipefile + '.upgraded', 'r') as f:
  1952. desiredlines = f.readlines()
  1953. with open(newrecipefile, 'r') as f:
  1954. newlines = f.readlines()
  1955. self.assertEqual(desiredlines, newlines)
  1956. # Check crates.inc got changed as expected
  1957. with open(oldcratesincfile + '.upgraded', 'r') as f:
  1958. desiredlines = f.readlines()
  1959. with open(newcratesincfile, 'r') as f:
  1960. newlines = f.readlines()
  1961. self.assertEqual(desiredlines, newlines)
  1962. # Check devtool reset recipe
  1963. result = runCmd('devtool reset %s -n' % recipe)
  1964. result = runCmd('devtool status')
  1965. self.assertNotIn(recipe, result.output)
  1966. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
  1967. def test_devtool_layer_plugins(self):
  1968. """Test that devtool can use plugins from other layers.
  1969. This test executes the selftest-reverse command from meta-selftest."""
  1970. self.track_for_cleanup(self.workspacedir)
  1971. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  1972. s = "Microsoft Made No Profit From Anyone's Zunes Yo"
  1973. result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s)
  1974. self.assertEqual(result.output, s[::-1])
  1975. def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
  1976. dstdir = basedstdir
  1977. self.assertExists(dstdir)
  1978. for p in paths:
  1979. dstdir = os.path.join(dstdir, p)
  1980. if not os.path.exists(dstdir):
  1981. try:
  1982. os.makedirs(dstdir)
  1983. except PermissionError:
  1984. return False
  1985. except OSError as e:
  1986. if e.errno == errno.EROFS:
  1987. return False
  1988. else:
  1989. raise e
  1990. if p == "lib":
  1991. # Can race with other tests
  1992. self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir)
  1993. else:
  1994. self.track_for_cleanup(dstdir)
  1995. dstfile = os.path.join(dstdir, os.path.basename(srcfile))
  1996. if srcfile != dstfile:
  1997. try:
  1998. shutil.copy(srcfile, dstfile)
  1999. except PermissionError:
  2000. return False
  2001. self.track_for_cleanup(dstfile)
  2002. return True
  2003. def test_devtool_load_plugin(self):
  2004. """Test that devtool loads only the first found plugin in BBPATH."""
  2005. self.track_for_cleanup(self.workspacedir)
  2006. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  2007. devtool = runCmd("which devtool")
  2008. fromname = runCmd("devtool --quiet pluginfile")
  2009. srcfile = fromname.output
  2010. bbpath = get_bb_var('BBPATH')
  2011. searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)]
  2012. plugincontent = []
  2013. with open(srcfile) as fh:
  2014. plugincontent = fh.readlines()
  2015. try:
  2016. self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
  2017. searchpath = [
  2018. path for path in searchpath
  2019. if self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
  2020. ]
  2021. result = runCmd("devtool --quiet count")
  2022. self.assertEqual(result.output, '1')
  2023. result = runCmd("devtool --quiet multiloaded")
  2024. self.assertEqual(result.output, "no")
  2025. for path in searchpath:
  2026. result = runCmd("devtool --quiet bbdir")
  2027. self.assertEqual(os.path.realpath(result.output), os.path.realpath(path))
  2028. os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
  2029. finally:
  2030. with open(srcfile, 'w') as fh:
  2031. fh.writelines(plugincontent)
  2032. def _setup_test_devtool_finish_upgrade(self):
  2033. # Check preconditions
  2034. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  2035. self.track_for_cleanup(self.workspacedir)
  2036. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  2037. # Use a "real" recipe from meta-selftest
  2038. recipe = 'devtool-upgrade-test1'
  2039. oldversion = '1.5.3'
  2040. newversion = '1.6.0'
  2041. oldrecipefile = get_bb_var('FILE', recipe)
  2042. recipedir = os.path.dirname(oldrecipefile)
  2043. result = runCmd('git status --porcelain .', cwd=recipedir)
  2044. if result.output.strip():
  2045. self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
  2046. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  2047. self.track_for_cleanup(tempdir)
  2048. # Check that recipe is not already under devtool control
  2049. result = runCmd('devtool status')
  2050. self.assertNotIn(recipe, result.output)
  2051. # Do the upgrade
  2052. result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion))
  2053. # Check devtool status and make sure recipe is present
  2054. result = runCmd('devtool status')
  2055. self.assertIn(recipe, result.output)
  2056. self.assertIn(tempdir, result.output)
  2057. # Make a change to the source
  2058. result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir)
  2059. result = runCmd('git status --porcelain', cwd=tempdir)
  2060. self.assertIn('M src/pv/number.c', result.output)
  2061. result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir)
  2062. # Check if patch is there
  2063. recipedir = os.path.dirname(oldrecipefile)
  2064. olddir = os.path.join(recipedir, recipe + '-' + oldversion)
  2065. patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch'
  2066. backportedpatchfn = 'backported.patch'
  2067. self.assertExists(os.path.join(olddir, patchfn), 'Original patch file does not exist')
  2068. self.assertExists(os.path.join(olddir, backportedpatchfn), 'Backported patch file does not exist')
  2069. return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn
  2070. def test_devtool_finish_upgrade_origlayer(self):
  2071. recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade()
  2072. # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
  2073. self.assertIn('/meta-selftest/', recipedir)
  2074. # Try finish to the original layer
  2075. self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
  2076. result = runCmd('devtool finish %s meta-selftest' % recipe)
  2077. result = runCmd('devtool status')
  2078. self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
  2079. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
  2080. self.assertNotExists(oldrecipefile, 'Old recipe file should have been deleted but wasn\'t')
  2081. self.assertNotExists(os.path.join(olddir, patchfn), 'Old patch file should have been deleted but wasn\'t')
  2082. self.assertNotExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should have been deleted but wasn\'t')
  2083. newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion))
  2084. newdir = os.path.join(recipedir, recipe + '-' + newversion)
  2085. self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t')
  2086. self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t')
  2087. self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was')
  2088. self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t')
  2089. with open(newrecipefile, 'r') as f:
  2090. newcontent = f.read()
  2091. self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't")
  2092. self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was")
  2093. self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't")
  2094. self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI")
  2095. def test_devtool_finish_upgrade_otherlayer(self):
  2096. recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade()
  2097. # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
  2098. self.assertIn('/meta-selftest/', recipedir)
  2099. # Try finish to a different layer - should create a bbappend
  2100. # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here
  2101. self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
  2102. oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta')
  2103. newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool')
  2104. newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion))
  2105. self.track_for_cleanup(newrecipedir)
  2106. result = runCmd('devtool finish %s oe-core' % recipe)
  2107. result = runCmd('devtool status')
  2108. self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
  2109. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
  2110. self.assertExists(oldrecipefile, 'Old recipe file should not have been deleted')
  2111. self.assertExists(os.path.join(olddir, patchfn), 'Old patch file should not have been deleted')
  2112. self.assertExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should not have been deleted')
  2113. newdir = os.path.join(newrecipedir, recipe + '-' + newversion)
  2114. self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t')
  2115. self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t')
  2116. self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was')
  2117. self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t')
  2118. with open(newrecipefile, 'r') as f:
  2119. newcontent = f.read()
  2120. self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't")
  2121. self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was")
  2122. self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't")
  2123. self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI")
  2124. def _setup_test_devtool_finish_modify(self):
  2125. # Check preconditions
  2126. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  2127. # Try modifying a recipe
  2128. self.track_for_cleanup(self.workspacedir)
  2129. recipe = 'mdadm'
  2130. oldrecipefile = get_bb_var('FILE', recipe)
  2131. recipedir = os.path.dirname(oldrecipefile)
  2132. result = runCmd('git status --porcelain .', cwd=recipedir)
  2133. if result.output.strip():
  2134. self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
  2135. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  2136. self.track_for_cleanup(tempdir)
  2137. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  2138. result = runCmd('devtool modify %s %s' % (recipe, tempdir))
  2139. self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
  2140. # Test devtool status
  2141. result = runCmd('devtool status')
  2142. self.assertIn(recipe, result.output)
  2143. self.assertIn(tempdir, result.output)
  2144. # Make a change to the source
  2145. result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir)
  2146. result = runCmd('git status --porcelain', cwd=tempdir)
  2147. self.assertIn('M maps.c', result.output)
  2148. result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir)
  2149. for entry in os.listdir(recipedir):
  2150. filesdir = os.path.join(recipedir, entry)
  2151. if os.path.isdir(filesdir):
  2152. break
  2153. else:
  2154. self.fail('Unable to find recipe files directory for %s' % recipe)
  2155. return recipe, oldrecipefile, recipedir, filesdir
  2156. def test_devtool_finish_modify_origlayer(self):
  2157. recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
  2158. # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
  2159. self.assertIn('/meta/', recipedir)
  2160. # Try finish to the original layer
  2161. self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
  2162. result = runCmd('devtool finish %s meta' % recipe)
  2163. result = runCmd('devtool status')
  2164. self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
  2165. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
  2166. expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
  2167. ('??', '.*/.*-Add-a-comment-to-the-code.patch$')]
  2168. self._check_repo_status(recipedir, expected_status)
  2169. def test_devtool_finish_modify_otherlayer(self):
  2170. recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
  2171. # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
  2172. self.assertIn('/meta/', recipedir)
  2173. relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta'))
  2174. appenddir = os.path.join(get_test_layer(), relpth)
  2175. self.track_for_cleanup(appenddir)
  2176. # Try finish to the original layer
  2177. self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
  2178. result = runCmd('devtool finish %s meta-selftest' % recipe)
  2179. result = runCmd('devtool status')
  2180. self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
  2181. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
  2182. result = runCmd('git status --porcelain .', cwd=recipedir)
  2183. if result.output.strip():
  2184. self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip()))
  2185. recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0]
  2186. recipefn = recipefn.split('_')[0] + '_%'
  2187. appendfile = os.path.join(appenddir, recipefn + '.bbappend')
  2188. self.assertExists(appendfile, 'bbappend %s should have been created but wasn\'t' % appendfile)
  2189. newdir = os.path.join(appenddir, recipe)
  2190. files = os.listdir(newdir)
  2191. foundpatch = None
  2192. for fn in files:
  2193. if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'):
  2194. foundpatch = fn
  2195. if not foundpatch:
  2196. self.fail('No patch file created next to bbappend')
  2197. files.remove(foundpatch)
  2198. if files:
  2199. self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
  2200. def test_devtool_finish_update_patch(self):
  2201. # This test uses a modified version of the sysdig recipe from meta-oe.
  2202. # - The patches have been renamed.
  2203. # - The dependencies are commented out since the recipe is not being
  2204. # built.
  2205. #
  2206. # The sysdig recipe is interesting in that it fetches two different Git
  2207. # repositories, and there are patches for both. This leads to that
  2208. # devtool will create ignore commits as it uses Git submodules to keep
  2209. # track of the second repository.
  2210. #
  2211. # This test will verify that the ignored commits actually are ignored
  2212. # when a commit in between is modified. It will also verify that the
  2213. # updated patch keeps its original name.
  2214. # Check preconditions
  2215. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  2216. # Try modifying a recipe
  2217. self.track_for_cleanup(self.workspacedir)
  2218. recipe = 'sysdig-selftest'
  2219. recipefile = get_bb_var('FILE', recipe)
  2220. recipedir = os.path.dirname(recipefile)
  2221. result = runCmd('git status --porcelain .', cwd=recipedir)
  2222. if result.output.strip():
  2223. self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
  2224. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  2225. self.track_for_cleanup(tempdir)
  2226. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  2227. result = runCmd('devtool modify %s %s' % (recipe, tempdir))
  2228. self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (recipedir, recipe, recipe, os.path.basename(recipefile)))
  2229. self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
  2230. # Make a change to one of the existing commits
  2231. result = runCmd('echo "# A comment " >> CMakeLists.txt', cwd=tempdir)
  2232. result = runCmd('git status --porcelain', cwd=tempdir)
  2233. self.assertIn('M CMakeLists.txt', result.output)
  2234. result = runCmd('git commit --fixup HEAD^ CMakeLists.txt', cwd=tempdir)
  2235. result = runCmd('git show -s --format=%s', cwd=tempdir)
  2236. self.assertIn('fixup! cmake: Pass PROBE_NAME via CFLAGS', result.output)
  2237. result = runCmd('GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash devtool-base', cwd=tempdir)
  2238. result = runCmd('devtool finish %s meta-selftest' % recipe)
  2239. result = runCmd('devtool status')
  2240. self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
  2241. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
  2242. expected_status = [(' M', '.*/0099-cmake-Pass-PROBE_NAME-via-CFLAGS.patch$')]
  2243. self._check_repo_status(recipedir, expected_status)
  2244. def test_devtool_rename(self):
  2245. # Check preconditions
  2246. self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  2247. self.track_for_cleanup(self.workspacedir)
  2248. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  2249. # First run devtool add
  2250. # We already have this recipe in OE-Core, but that doesn't matter
  2251. recipename = 'i2c-tools'
  2252. recipever = '3.1.2'
  2253. recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever))
  2254. url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever
  2255. def add_recipe():
  2256. result = runCmd('devtool add %s' % url)
  2257. self.assertExists(recipefile, 'Expected recipe file not created')
  2258. self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory not created')
  2259. checkvars = {}
  2260. checkvars['S'] = None
  2261. checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
  2262. self._test_recipe_contents(recipefile, checkvars, [])
  2263. add_recipe()
  2264. # Now rename it - change both name and version
  2265. newrecipename = 'mynewrecipe'
  2266. newrecipever = '456'
  2267. newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever))
  2268. result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever))
  2269. self.assertExists(newrecipefile, 'Recipe file not renamed')
  2270. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists')
  2271. newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename)
  2272. self.assertExists(newsrctree, 'Source directory not renamed')
  2273. checkvars = {}
  2274. checkvars['S'] = '${UNPACKDIR}/%s-%s' % (recipename, recipever)
  2275. checkvars['SRC_URI'] = url
  2276. self._test_recipe_contents(newrecipefile, checkvars, [])
  2277. # Try again - change just name this time
  2278. result = runCmd('devtool reset -n %s' % newrecipename)
  2279. add_recipe()
  2280. newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
  2281. result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
  2282. self.assertExists(newrecipefile, 'Recipe file not renamed')
  2283. self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists')
  2284. self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed')
  2285. checkvars = {}
  2286. checkvars['S'] = '${UNPACKDIR}/%s-${PV}' % recipename
  2287. checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
  2288. self._test_recipe_contents(newrecipefile, checkvars, [])
  2289. # Try again - change just version this time
  2290. result = runCmd('devtool reset -n %s' % newrecipename)
  2291. add_recipe()
  2292. newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
  2293. result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
  2294. self.assertExists(newrecipefile, 'Recipe file not renamed')
  2295. self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists')
  2296. checkvars = {}
  2297. checkvars['S'] = '${UNPACKDIR}/${BPN}-%s' % recipever
  2298. checkvars['SRC_URI'] = url
  2299. self._test_recipe_contents(newrecipefile, checkvars, [])
  2300. def test_devtool_virtual_kernel_modify(self):
  2301. """
  2302. Summary: The purpose of this test case is to verify that
  2303. devtool modify works correctly when building
  2304. the kernel.
  2305. Dependencies: NA
  2306. Steps: 1. Build kernel with bitbake.
  2307. 2. Save the config file generated.
  2308. 3. Clean the environment.
  2309. 4. Use `devtool modify virtual/kernel` to validate following:
  2310. 4.1 The source is checked out correctly.
  2311. 4.2 The resulting configuration is the same as
  2312. what was get on step 2.
  2313. 4.3 The Kernel can be build correctly.
  2314. 4.4 Changes made on the source are reflected on the
  2315. subsequent builds.
  2316. 4.5 Changes on the configuration are reflected on the
  2317. subsequent builds
  2318. Expected: devtool modify is able to checkout the source of the kernel
  2319. and modification to the source and configurations are reflected
  2320. when building the kernel.
  2321. """
  2322. kernel_provider = self.td['PREFERRED_PROVIDER_virtual/kernel']
  2323. # Clean up the environment
  2324. bitbake('%s -c clean' % kernel_provider)
  2325. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  2326. tempdir_cfg = tempfile.mkdtemp(prefix='config_qa')
  2327. self.track_for_cleanup(tempdir)
  2328. self.track_for_cleanup(tempdir_cfg)
  2329. self.track_for_cleanup(self.workspacedir)
  2330. self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider)
  2331. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  2332. #Step 1
  2333. #Here is just generated the config file instead of all the kernel to optimize the
  2334. #time of executing this test case.
  2335. bitbake('%s -c configure' % kernel_provider)
  2336. bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config')
  2337. #Step 2
  2338. runCmd('cp %s %s' % (bbconfig, tempdir_cfg))
  2339. self.assertExists(os.path.join(tempdir_cfg, '.config'), 'Could not copy .config file from kernel')
  2340. tmpconfig = os.path.join(tempdir_cfg, '.config')
  2341. #Step 3
  2342. bitbake('%s -c clean' % kernel_provider)
  2343. #Step 4.1
  2344. runCmd('devtool modify virtual/kernel -x %s' % tempdir)
  2345. self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
  2346. #Step 4.2
  2347. configfile = os.path.join(tempdir,'.config')
  2348. runCmd('diff %s %s' % (tmpconfig, configfile))
  2349. #Step 4.3
  2350. #NOTE: virtual/kernel is mapped to kernel_provider
  2351. runCmd('devtool build %s' % kernel_provider)
  2352. kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
  2353. self.assertExists(kernelfile, 'Kernel was not build correctly')
  2354. #Modify the kernel source
  2355. modfile = os.path.join(tempdir, 'init/version.c')
  2356. # Moved to uts.h in 6.1 onwards
  2357. modfile2 = os.path.join(tempdir, 'include/linux/uts.h')
  2358. runCmd("sed -i 's/Linux/LiNuX/g' %s %s" % (modfile, modfile2))
  2359. #Modify the configuration
  2360. codeconfigfile = os.path.join(tempdir, '.config.new')
  2361. modconfopt = "CONFIG_SG_POOL=n"
  2362. runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
  2363. #Build again kernel with devtool
  2364. runCmd('devtool build %s' % kernel_provider)
  2365. #Step 4.4
  2366. runCmd("grep '%s' %s" % ('LiNuX', kernelfile))
  2367. #Step 4.5
  2368. runCmd("grep %s %s" % (modconfopt, codeconfigfile))
  2369. class DevtoolIdeSdkTests(DevtoolBase):
  2370. def _write_bb_config(self, recipe_names):
  2371. """Helper to write the bitbake local.conf file"""
  2372. conf_lines = [
  2373. 'IMAGE_CLASSES += "image-combined-dbg"',
  2374. 'IMAGE_GEN_DEBUGFS = "1"',
  2375. 'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join(
  2376. [r + '-ptest' for r in recipe_names]),
  2377. 'DISTRO_FEATURES:append = " ptest"'
  2378. ]
  2379. self.write_config("\n".join(conf_lines))
  2380. def _check_workspace(self):
  2381. """Check if a workspace directory is available and setup the cleanup"""
  2382. self.assertTrue(not os.path.exists(self.workspacedir),
  2383. 'This test cannot be run with a workspace directory under the build directory')
  2384. self.track_for_cleanup(self.workspacedir)
  2385. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  2386. def _workspace_scripts_dir(self, recipe_name):
  2387. return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts'))
  2388. def _sources_scripts_dir(self, src_dir):
  2389. return os.path.realpath(os.path.join(src_dir, 'oe-scripts'))
  2390. def _workspace_gdbinit_dir(self, recipe_name):
  2391. return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts', 'gdbinit'))
  2392. def _sources_gdbinit_dir(self, src_dir):
  2393. return os.path.realpath(os.path.join(src_dir, 'oe-gdbinit'))
  2394. def _devtool_ide_sdk_recipe(self, recipe_name, build_file, testimage):
  2395. """Setup a recipe for working with devtool ide-sdk
  2396. Basically devtool modify -x followed by some tests
  2397. """
  2398. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  2399. self.track_for_cleanup(tempdir)
  2400. self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name)
  2401. result = runCmd('devtool modify %s -x %s --debug-build' % (recipe_name, tempdir))
  2402. self.assertExists(os.path.join(tempdir, build_file),
  2403. 'Extracted source could not be found')
  2404. self.assertExists(os.path.join(self.workspacedir, 'conf',
  2405. 'layer.conf'), 'Workspace directory not created')
  2406. matches = glob.glob(os.path.join(self.workspacedir,
  2407. 'appends', recipe_name + '.bbappend'))
  2408. self.assertTrue(matches, 'bbappend not created %s' % result.output)
  2409. # Test devtool status
  2410. result = runCmd('devtool status')
  2411. self.assertIn(recipe_name, result.output)
  2412. self.assertIn(tempdir, result.output)
  2413. self._check_src_repo(tempdir)
  2414. # Usually devtool ide-sdk would initiate the build of the SDK.
  2415. # But there is a circular dependency with starting Qemu and passing the IP of runqemu to devtool ide-sdk.
  2416. if testimage:
  2417. bitbake("%s qemu-native qemu-helper-native" % testimage)
  2418. deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
  2419. self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
  2420. self.add_command_to_tearDown(
  2421. 'rm -f %s/%s*' % (deploy_dir_image, testimage))
  2422. return tempdir
  2423. def _get_recipe_ids(self, recipe_name):
  2424. """IDs needed to write recipe specific config entries into IDE config files"""
  2425. package_arch = get_bb_var('PACKAGE_ARCH', recipe_name)
  2426. recipe_id = recipe_name + "-" + package_arch
  2427. recipe_id_pretty = recipe_name + ": " + package_arch
  2428. return (recipe_id, recipe_id_pretty)
  2429. def _verify_install_script_code(self, tempdir, recipe_name):
  2430. """Verify the scripts referred by the tasks.json file are fine.
  2431. This function does not depend on Qemu. Therefore it verifies the scripts
  2432. exists and the delete step works as expected. But it does not try to
  2433. deploy to Qemu.
  2434. """
  2435. recipe_id, recipe_id_pretty = self._get_recipe_ids(recipe_name)
  2436. with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j:
  2437. tasks_d = json.load(tasks_j)
  2438. tasks = tasks_d["tasks"]
  2439. task_install = next(
  2440. (task for task in tasks if task["label"] == "install && deploy-target %s" % recipe_id_pretty), None)
  2441. self.assertIsNot(task_install, None)
  2442. # execute only the bb_run_do_install script since the deploy would require e.g. Qemu running.
  2443. i_and_d_script = "install_and_deploy_" + recipe_id
  2444. i_and_d_script_path = os.path.join(
  2445. self._workspace_scripts_dir(recipe_name), i_and_d_script)
  2446. self.assertExists(i_and_d_script_path)
  2447. def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe):
  2448. """Verify deployment and execution in Qemu system work for one recipe.
  2449. This function checks the entire SDK workflow: changing the code, recompiling
  2450. it and deploying it back to Qemu, and checking that the changes have been
  2451. incorporated into the provided binaries. It also runs the tests of the recipe.
  2452. """
  2453. recipe_id, _ = self._get_recipe_ids(recipe_name)
  2454. i_and_d_script = "install_and_deploy_" + recipe_id
  2455. install_deploy_cmd = os.path.join(
  2456. self._workspace_scripts_dir(recipe_name), i_and_d_script)
  2457. self.assertExists(install_deploy_cmd,
  2458. '%s script not found' % install_deploy_cmd)
  2459. runCmd(install_deploy_cmd)
  2460. MAGIC_STRING_ORIG = "Magic: 123456789"
  2461. MAGIC_STRING_NEW = "Magic: 987654321"
  2462. ptest_cmd = "ptest-runner " + recipe_name
  2463. # validate that SSH is working
  2464. status, _ = qemu.run("uname")
  2465. self.assertEqual(
  2466. status, 0, msg="Failed to connect to the SSH server on Qemu")
  2467. # Verify the unmodified example prints the magic string
  2468. status, output = qemu.run(example_exe)
  2469. self.assertEqual(status, 0, msg="%s failed: %s" %
  2470. (example_exe, output))
  2471. self.assertIn(MAGIC_STRING_ORIG, output)
  2472. # Verify the unmodified ptests work
  2473. status, output = qemu.run(ptest_cmd)
  2474. self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
  2475. self.assertIn("PASS: cpp-example-lib", output)
  2476. # Verify remote debugging works
  2477. self._gdb_cross_debugging(
  2478. qemu, recipe_name, example_exe, MAGIC_STRING_ORIG)
  2479. # Replace the Magic String in the code, compile and deploy to Qemu
  2480. cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp')
  2481. with open(cpp_example_lib_hpp, 'r') as file:
  2482. cpp_code = file.read()
  2483. cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW)
  2484. with open(cpp_example_lib_hpp, 'w') as file:
  2485. file.write(cpp_code)
  2486. runCmd(install_deploy_cmd, cwd=tempdir)
  2487. # Verify the modified example prints the modified magic string
  2488. status, output = qemu.run(example_exe)
  2489. self.assertEqual(status, 0, msg="%s failed: %s" %
  2490. (example_exe, output))
  2491. self.assertNotIn(MAGIC_STRING_ORIG, output)
  2492. self.assertIn(MAGIC_STRING_NEW, output)
  2493. # Verify the modified example ptests work
  2494. status, output = qemu.run(ptest_cmd)
  2495. self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
  2496. self.assertIn("PASS: cpp-example-lib", output)
  2497. # Verify remote debugging works wit the modified magic string
  2498. self._gdb_cross_debugging(
  2499. qemu, recipe_name, example_exe, MAGIC_STRING_NEW)
  2500. def _gdb_cross(self):
  2501. """Verify gdb-cross is provided by devtool ide-sdk"""
  2502. target_arch = self.td["TARGET_ARCH"]
  2503. target_sys = self.td["TARGET_SYS"]
  2504. gdb_recipe = "gdb-cross-" + target_arch
  2505. gdb_binary = target_sys + "-gdb"
  2506. native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe)
  2507. r = runCmd("%s --version" % gdb_binary,
  2508. native_sysroot=native_sysroot, target_sys=target_sys)
  2509. self.assertEqual(r.status, 0)
  2510. self.assertIn("GNU gdb", r.output)
  2511. def _gdb_cross_debugging(self, qemu, recipe_name, example_exe, magic_string):
  2512. """Verify gdb-cross is working
  2513. Test remote debugging:
  2514. break main
  2515. run
  2516. continue
  2517. break CppExample::print_json()
  2518. continue
  2519. print CppExample::test_string.compare("cpp-example-lib Magic: 123456789")
  2520. $1 = 0
  2521. print CppExample::test_string.compare("cpp-example-lib Magic: 123456789aaa")
  2522. $2 = -3
  2523. list cpp-example-lib.hpp:13,13
  2524. 13 inline static const std::string test_string = "cpp-example-lib Magic: 123456789";
  2525. continue
  2526. """
  2527. sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
  2528. gdbserver_script = os.path.join(self._workspace_scripts_dir(
  2529. recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_m')
  2530. gdb_script = os.path.join(self._workspace_scripts_dir(
  2531. recipe_name), 'gdb_1234_usr-bin-' + example_exe)
  2532. # Start a gdbserver
  2533. r = runCmd(gdbserver_script)
  2534. self.assertEqual(r.status, 0)
  2535. # Check there is a gdbserver running
  2536. r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
  2537. self.assertEqual(r.status, 0)
  2538. self.assertIn("gdbserver ", r.output)
  2539. # Check the pid file is correct
  2540. test_cmd = "cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \
  2541. example_exe + "/pid)/cmdline"
  2542. r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd))
  2543. self.assertEqual(r.status, 0)
  2544. self.assertIn("gdbserver", r.output)
  2545. # Test remote debugging works
  2546. gdb_batch_cmd = " --batch -ex 'break main' -ex 'run'"
  2547. gdb_batch_cmd += " -ex 'break CppExample::print_json()' -ex 'continue'"
  2548. gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %s\")'" % magic_string
  2549. gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %saaa\")'" % magic_string
  2550. gdb_batch_cmd += " -ex 'list cpp-example-lib.hpp:13,13'"
  2551. gdb_batch_cmd += " -ex 'continue'"
  2552. r = runCmd(gdb_script + gdb_batch_cmd)
  2553. self.logger.debug("%s %s returned: %s", gdb_script,
  2554. gdb_batch_cmd, r.output)
  2555. self.assertEqual(r.status, 0)
  2556. self.assertIn("Breakpoint 1, main", r.output)
  2557. self.assertIn("$1 = 0", r.output) # test.string.compare equal
  2558. self.assertIn("$2 = -3", r.output) # test.string.compare longer
  2559. self.assertIn(
  2560. 'inline static const std::string test_string = "cpp-example-lib %s";' % magic_string, r.output)
  2561. self.assertIn("exited normally", r.output)
  2562. # Stop the gdbserver
  2563. r = runCmd(gdbserver_script + ' stop')
  2564. self.assertEqual(r.status, 0)
  2565. # Check there is no gdbserver running
  2566. r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
  2567. self.assertEqual(r.status, 0)
  2568. self.assertNotIn("gdbserver ", r.output)
  2569. def _verify_cmake_preset(self, tempdir):
  2570. """Verify the generated cmake preset works as expected
  2571. Check if compiling works
  2572. Check if unit tests can be executed in qemu (not qemu-system)
  2573. """
  2574. with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j:
  2575. cmake_preset_d = json.load(cmake_preset_j)
  2576. config_presets = cmake_preset_d["configurePresets"]
  2577. self.assertEqual(len(config_presets), 1)
  2578. cmake_exe = config_presets[0]["cmakeExecutable"]
  2579. preset_name = config_presets[0]["name"]
  2580. # Verify the wrapper for cmake native is available
  2581. self.assertExists(cmake_exe)
  2582. # Verify the cmake preset generated by devtool ide-sdk is available
  2583. result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir)
  2584. self.assertIn(preset_name, result.output)
  2585. # Verify cmake re-uses the o files compiled by bitbake
  2586. result = runCmd('%s --build --preset %s' %
  2587. (cmake_exe, preset_name), cwd=tempdir)
  2588. self.assertIn("ninja: no work to do.", result.output)
  2589. # Verify the unit tests work (in Qemu user mode)
  2590. result = runCmd('%s --build --preset %s --target test' %
  2591. (cmake_exe, preset_name), cwd=tempdir)
  2592. self.assertIn("100% tests passed", result.output)
  2593. # Verify re-building and testing works again
  2594. result = runCmd('%s --build --preset %s --target clean' %
  2595. (cmake_exe, preset_name), cwd=tempdir)
  2596. self.assertIn("Cleaning", result.output)
  2597. result = runCmd('%s --build --preset %s' %
  2598. (cmake_exe, preset_name), cwd=tempdir)
  2599. self.assertIn("Building", result.output)
  2600. self.assertIn("Linking", result.output)
  2601. result = runCmd('%s --build --preset %s --target test' %
  2602. (cmake_exe, preset_name), cwd=tempdir)
  2603. self.assertIn("Running tests...", result.output)
  2604. self.assertIn("100% tests passed", result.output)
  2605. @OETestTag("runqemu")
  2606. def test_devtool_ide_sdk_none_qemu(self):
  2607. """Start qemu-system and run tests for multiple recipes. ide=none is used."""
  2608. recipe_names = ["cmake-example", "meson-example"]
  2609. testimage = "oe-selftest-image"
  2610. self._check_workspace()
  2611. self._write_bb_config(recipe_names)
  2612. self._check_runqemu_prerequisites()
  2613. # Verify deployment to Qemu (system mode) works
  2614. bitbake(testimage)
  2615. with runqemu(testimage, runqemuparams="nographic") as qemu:
  2616. # cmake-example recipe
  2617. recipe_name = "cmake-example"
  2618. example_exe = "cmake-example"
  2619. build_file = "CMakeLists.txt"
  2620. tempdir = self._devtool_ide_sdk_recipe(
  2621. recipe_name, build_file, testimage)
  2622. bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
  2623. recipe_name, testimage, qemu.ip)
  2624. runCmd(bitbake_sdk_cmd)
  2625. self._gdb_cross()
  2626. self._verify_cmake_preset(tempdir)
  2627. self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
  2628. # Verify the oe-scripts sym-link is valid
  2629. self.assertEqual(self._workspace_scripts_dir(
  2630. recipe_name), self._sources_scripts_dir(tempdir))
  2631. # meson-example recipe
  2632. recipe_name = "meson-example"
  2633. example_exe = "mesonex"
  2634. build_file = "meson.build"
  2635. tempdir = self._devtool_ide_sdk_recipe(
  2636. recipe_name, build_file, testimage)
  2637. bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
  2638. recipe_name, testimage, qemu.ip)
  2639. runCmd(bitbake_sdk_cmd)
  2640. self._gdb_cross()
  2641. self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
  2642. # Verify the oe-scripts sym-link is valid
  2643. self.assertEqual(self._workspace_scripts_dir(
  2644. recipe_name), self._sources_scripts_dir(tempdir))
  2645. def test_devtool_ide_sdk_code_cmake(self):
  2646. """Verify a cmake recipe works with ide=code mode"""
  2647. recipe_name = "cmake-example"
  2648. build_file = "CMakeLists.txt"
  2649. testimage = "oe-selftest-image"
  2650. self._check_workspace()
  2651. self._write_bb_config([recipe_name])
  2652. tempdir = self._devtool_ide_sdk_recipe(
  2653. recipe_name, build_file, testimage)
  2654. bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
  2655. recipe_name, testimage)
  2656. runCmd(bitbake_sdk_cmd)
  2657. self._verify_cmake_preset(tempdir)
  2658. self._verify_install_script_code(tempdir, recipe_name)
  2659. self._gdb_cross()
  2660. def test_devtool_ide_sdk_code_meson(self):
  2661. """Verify a meson recipe works with ide=code mode"""
  2662. recipe_name = "meson-example"
  2663. build_file = "meson.build"
  2664. testimage = "oe-selftest-image"
  2665. self._check_workspace()
  2666. self._write_bb_config([recipe_name])
  2667. tempdir = self._devtool_ide_sdk_recipe(
  2668. recipe_name, build_file, testimage)
  2669. bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
  2670. recipe_name, testimage)
  2671. runCmd(bitbake_sdk_cmd)
  2672. with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j:
  2673. settings_d = json.load(settings_j)
  2674. meson_exe = settings_d["mesonbuild.mesonPath"]
  2675. meson_build_folder = settings_d["mesonbuild.buildFolder"]
  2676. # Verify the wrapper for meson native is available
  2677. self.assertExists(meson_exe)
  2678. # Verify meson re-uses the o files compiled by bitbake
  2679. result = runCmd('%s compile -C %s' %
  2680. (meson_exe, meson_build_folder), cwd=tempdir)
  2681. self.assertIn("ninja: no work to do.", result.output)
  2682. # Verify the unit tests work (in Qemu)
  2683. runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
  2684. # Verify re-building and testing works again
  2685. result = runCmd('%s compile -C %s --clean' %
  2686. (meson_exe, meson_build_folder), cwd=tempdir)
  2687. self.assertIn("Cleaning...", result.output)
  2688. result = runCmd('%s compile -C %s' %
  2689. (meson_exe, meson_build_folder), cwd=tempdir)
  2690. self.assertIn("Linking target", result.output)
  2691. runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
  2692. self._verify_install_script_code(tempdir, recipe_name)
  2693. self._gdb_cross()
  2694. def test_devtool_ide_sdk_shared_sysroots(self):
  2695. """Verify the shared sysroot SDK"""
  2696. # Handle the workspace (which is not needed by this test case)
  2697. self._check_workspace()
  2698. result_init = runCmd(
  2699. 'devtool ide-sdk -m shared oe-selftest-image cmake-example meson-example --ide=code')
  2700. bb_vars = get_bb_vars(
  2701. ['REAL_MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE'], "meta-ide-support")
  2702. environment_script = 'environment-setup-%s' % bb_vars['REAL_MULTIMACH_TARGET_SYS']
  2703. deploydir = bb_vars['DEPLOY_DIR_IMAGE']
  2704. environment_script_path = os.path.join(deploydir, environment_script)
  2705. cpp_example_src = os.path.join(
  2706. bb_vars['COREBASE'], 'meta-selftest', 'recipes-test', 'cpp', 'files')
  2707. # Verify the cross environment script is available
  2708. self.assertExists(environment_script_path)
  2709. def runCmdEnv(cmd, cwd):
  2710. cmd = '/bin/sh -c ". %s > /dev/null && %s"' % (
  2711. environment_script_path, cmd)
  2712. return runCmd(cmd, cwd)
  2713. # Verify building the C++ example works with CMake
  2714. tempdir_cmake = tempfile.mkdtemp(prefix='devtoolqa')
  2715. self.track_for_cleanup(tempdir_cmake)
  2716. result_cmake = runCmdEnv("which cmake", cwd=tempdir_cmake)
  2717. cmake_native = os.path.normpath(result_cmake.output.strip())
  2718. self.assertExists(cmake_native)
  2719. runCmdEnv('cmake %s' % cpp_example_src, cwd=tempdir_cmake)
  2720. runCmdEnv('cmake --build %s' % tempdir_cmake, cwd=tempdir_cmake)
  2721. # Verify the printed note really referres to a cmake executable
  2722. cmake_native_code = ""
  2723. for line in result_init.output.splitlines():
  2724. m = re.search(r'"cmake.cmakePath": "(.*)"', line)
  2725. if m:
  2726. cmake_native_code = m.group(1)
  2727. break
  2728. self.assertExists(cmake_native_code)
  2729. self.assertEqual(cmake_native, cmake_native_code)
  2730. # Verify building the C++ example works with Meson
  2731. tempdir_meson = tempfile.mkdtemp(prefix='devtoolqa')
  2732. self.track_for_cleanup(tempdir_meson)
  2733. result_cmake = runCmdEnv("which meson", cwd=tempdir_meson)
  2734. meson_native = os.path.normpath(result_cmake.output.strip())
  2735. self.assertExists(meson_native)
  2736. runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src)
  2737. runCmdEnv('meson compile', cwd=tempdir_meson)
  2738. def test_devtool_ide_sdk_plugins(self):
  2739. """Test that devtool ide-sdk can use plugins from other layers."""
  2740. # We need a workspace layer and a modified recipe (but no image)
  2741. modified_recipe_name = "meson-example"
  2742. modified_build_file = "meson.build"
  2743. testimage = "oe-selftest-image"
  2744. shared_recipe_name = "cmake-example"
  2745. self._check_workspace()
  2746. self._write_bb_config([modified_recipe_name])
  2747. tempdir = self._devtool_ide_sdk_recipe(
  2748. modified_recipe_name, modified_build_file, None)
  2749. IDE_RE = re.compile(r'.*--ide \{(.*)\}.*')
  2750. def get_ides_from_help(help_str):
  2751. m = IDE_RE.search(help_str)
  2752. return m.group(1).split(',')
  2753. # verify the default plugins are available but the foo plugin is not
  2754. result = runCmd('devtool ide-sdk -h')
  2755. found_ides = get_ides_from_help(result.output)
  2756. self.assertIn('code', found_ides)
  2757. self.assertIn('none', found_ides)
  2758. self.assertNotIn('foo', found_ides)
  2759. shared_config_file = os.path.join(tempdir, 'shared-config.txt')
  2760. shared_config_str = 'Dummy shared IDE config'
  2761. modified_config_file = os.path.join(tempdir, 'modified-config.txt')
  2762. modified_config_str = 'Dummy modified IDE config'
  2763. # Generate a foo plugin in the workspace layer
  2764. plugin_dir = os.path.join(
  2765. self.workspacedir, 'lib', 'devtool', 'ide_plugins')
  2766. os.makedirs(plugin_dir)
  2767. plugin_code = 'from devtool.ide_plugins import IdeBase\n\n'
  2768. plugin_code += 'class IdeFoo(IdeBase):\n'
  2769. plugin_code += ' def setup_shared_sysroots(self, shared_env):\n'
  2770. plugin_code += ' with open("%s", "w") as config_file:\n' % shared_config_file
  2771. plugin_code += ' config_file.write("%s")\n\n' % shared_config_str
  2772. plugin_code += ' def setup_modified_recipe(self, args, image_recipe, modified_recipe):\n'
  2773. plugin_code += ' with open("%s", "w") as config_file:\n' % modified_config_file
  2774. plugin_code += ' config_file.write("%s")\n\n' % modified_config_str
  2775. plugin_code += 'def register_ide_plugin(ide_plugins):\n'
  2776. plugin_code += ' ide_plugins["foo"] = IdeFoo\n'
  2777. plugin_py = os.path.join(plugin_dir, 'ide_foo.py')
  2778. with open(plugin_py, 'w') as plugin_file:
  2779. plugin_file.write(plugin_code)
  2780. # Verify the foo plugin is available as well
  2781. result = runCmd('devtool ide-sdk -h')
  2782. found_ides = get_ides_from_help(result.output)
  2783. self.assertIn('code', found_ides)
  2784. self.assertIn('none', found_ides)
  2785. self.assertIn('foo', found_ides)
  2786. # Verify the foo plugin generates a shared config
  2787. result = runCmd(
  2788. 'devtool ide-sdk -m shared --skip-bitbake --ide foo %s' % shared_recipe_name)
  2789. with open(shared_config_file) as shared_config:
  2790. shared_config_new = shared_config.read()
  2791. self.assertEqual(shared_config_str, shared_config_new)
  2792. # Verify the foo plugin generates a modified config
  2793. result = runCmd('devtool ide-sdk --skip-bitbake --ide foo %s %s' %
  2794. (modified_recipe_name, testimage))
  2795. with open(modified_config_file) as modified_config:
  2796. modified_config_new = modified_config.read()
  2797. self.assertEqual(modified_config_str, modified_config_new)