summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/selftest/cases/devtool.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/selftest/cases/devtool.py')
-rw-r--r--meta/lib/oeqa/selftest/cases/devtool.py1412
1 files changed, 1262 insertions, 150 deletions
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 3385546e8e..05f228f03e 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -1,18 +1,23 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: MIT 4# SPDX-License-Identifier: MIT
3# 5#
4 6
7import errno
5import os 8import os
6import re 9import re
7import shutil 10import shutil
8import tempfile 11import tempfile
9import glob 12import glob
10import fnmatch 13import fnmatch
14import unittest
15import json
11 16
12import oeqa.utils.ftools as ftools
13from oeqa.selftest.case import OESelftestTestCase 17from oeqa.selftest.case import OESelftestTestCase
14from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer 18from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
15from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer 19from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer
20from oeqa.core.decorator import OETestTag
16 21
17oldmetapath = None 22oldmetapath = None
18 23
@@ -24,6 +29,9 @@ def setUpModule():
24 corecopydir = os.path.join(templayerdir, 'core-copy') 29 corecopydir = os.path.join(templayerdir, 'core-copy')
25 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf') 30 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
26 edited_layers = [] 31 edited_layers = []
32 # make sure user doesn't have a local workspace
33 result = runCmd('bitbake-layers show-layers')
34 assert "workspacelayer" not in result.output, "Devtool test suite cannot be run with a local workspace directory"
27 35
28 # We need to take a copy of the meta layer so we can modify it and not 36 # We need to take a copy of the meta layer so we can modify it and not
29 # have any races against other tests that might be running in parallel 37 # have any races against other tests that might be running in parallel
@@ -38,10 +46,17 @@ def setUpModule():
38 canonical_layerpath = os.path.realpath(canonical_layerpath) + '/' 46 canonical_layerpath = os.path.realpath(canonical_layerpath) + '/'
39 edited_layers.append(layerpath) 47 edited_layers.append(layerpath)
40 oldmetapath = os.path.realpath(layerpath) 48 oldmetapath = os.path.realpath(layerpath)
49
50 # when downloading poky from tar.gz some tests will be skipped (BUG 12389)
51 try:
52 runCmd('git rev-parse --is-inside-work-tree', cwd=canonical_layerpath)
53 except:
54 raise unittest.SkipTest("devtool tests require folder to be a git repo")
55
41 result = runCmd('git rev-parse --show-toplevel', cwd=canonical_layerpath) 56 result = runCmd('git rev-parse --show-toplevel', cwd=canonical_layerpath)
42 oldreporoot = result.output.rstrip() 57 oldreporoot = result.output.rstrip()
43 newmetapath = os.path.join(corecopydir, os.path.relpath(oldmetapath, oldreporoot)) 58 newmetapath = os.path.join(corecopydir, os.path.relpath(oldmetapath, oldreporoot))
44 runCmd('git clone %s %s' % (oldreporoot, corecopydir), cwd=templayerdir) 59 runCmd('git clone file://%s %s' % (oldreporoot, corecopydir), cwd=templayerdir)
45 # Now we need to copy any modified files 60 # Now we need to copy any modified files
46 # You might ask "why not just copy the entire tree instead of 61 # You might ask "why not just copy the entire tree instead of
47 # cloning and doing this?" - well, the problem with that is 62 # cloning and doing this?" - well, the problem with that is
@@ -49,11 +64,15 @@ def setUpModule():
49 # under COREBASE and we don't want to copy that, so we have 64 # under COREBASE and we don't want to copy that, so we have
50 # to be selective. 65 # to be selective.
51 result = runCmd('git status --porcelain', cwd=oldreporoot) 66 result = runCmd('git status --porcelain', cwd=oldreporoot)
67
68 # Also copy modifications to the 'scripts/' directory
69 canonical_layerpath_scripts = os.path.normpath(canonical_layerpath + "../scripts")
70
52 for line in result.output.splitlines(): 71 for line in result.output.splitlines():
53 if line.startswith(' M ') or line.startswith('?? '): 72 if line.startswith(' M ') or line.startswith('?? '):
54 relpth = line.split()[1] 73 relpth = line.split()[1]
55 pth = os.path.join(oldreporoot, relpth) 74 pth = os.path.join(oldreporoot, relpth)
56 if pth.startswith(canonical_layerpath): 75 if pth.startswith(canonical_layerpath) or pth.startswith(canonical_layerpath_scripts):
57 if relpth.endswith('/'): 76 if relpth.endswith('/'):
58 destdir = os.path.join(corecopydir, relpth) 77 destdir = os.path.join(corecopydir, relpth)
59 # avoid race condition by not copying .pyc files YPBZ#13421,13803 78 # avoid race condition by not copying .pyc files YPBZ#13421,13803
@@ -80,32 +99,15 @@ def tearDownModule():
80 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb) 99 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
81 shutil.rmtree(templayerdir) 100 shutil.rmtree(templayerdir)
82 101
83class DevtoolBase(OESelftestTestCase): 102class DevtoolTestCase(OESelftestTestCase):
84
85 @classmethod
86 def setUpClass(cls):
87 super(DevtoolBase, cls).setUpClass()
88 bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
89 cls.original_sstate = bb_vars['SSTATE_DIR']
90 cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
91 cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
92 cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
93 % cls.original_sstate)
94
95 @classmethod
96 def tearDownClass(cls):
97 cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
98 runCmd('rm -rf %s' % cls.devtool_sstate)
99 super(DevtoolBase, cls).tearDownClass()
100 103
101 def setUp(self): 104 def setUp(self):
102 """Test case setup function""" 105 """Test case setup function"""
103 super(DevtoolBase, self).setUp() 106 super(DevtoolTestCase, self).setUp()
104 self.workspacedir = os.path.join(self.builddir, 'workspace') 107 self.workspacedir = os.path.join(self.builddir, 'workspace')
105 self.assertTrue(not os.path.exists(self.workspacedir), 108 self.assertTrue(not os.path.exists(self.workspacedir),
106 'This test cannot be run with a workspace directory ' 109 'This test cannot be run with a workspace directory '
107 'under the build directory') 110 'under the build directory')
108 self.append_config(self.sstate_conf)
109 111
110 def _check_src_repo(self, repo_dir): 112 def _check_src_repo(self, repo_dir):
111 """Check srctree git repository""" 113 """Check srctree git repository"""
@@ -152,7 +154,7 @@ class DevtoolBase(OESelftestTestCase):
152 value = invalue 154 value = invalue
153 invar = None 155 invar = None
154 elif '=' in line: 156 elif '=' in line:
155 splitline = line.split('=', 1) 157 splitline = re.split(r"[?+:]*=[+]?", line, 1)
156 var = splitline[0].rstrip() 158 var = splitline[0].rstrip()
157 value = splitline[1].strip().strip('"') 159 value = splitline[1].strip().strip('"')
158 if value.endswith('\\'): 160 if value.endswith('\\'):
@@ -235,6 +237,103 @@ class DevtoolBase(OESelftestTestCase):
235 filelist.append(' '.join(splitline)) 237 filelist.append(' '.join(splitline))
236 return filelist 238 return filelist
237 239
240 def _check_diff(self, diffoutput, addlines, removelines):
241 """Check output from 'git diff' matches expectation"""
242 remaining_addlines = addlines[:]
243 remaining_removelines = removelines[:]
244 for line in diffoutput.splitlines():
245 if line.startswith('+++') or line.startswith('---'):
246 continue
247 elif line.startswith('+'):
248 matched = False
249 for item in addlines:
250 if re.match(item, line[1:].strip()):
251 matched = True
252 remaining_addlines.remove(item)
253 break
254 self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
255 elif line.startswith('-'):
256 matched = False
257 for item in removelines:
258 if re.match(item, line[1:].strip()):
259 matched = True
260 remaining_removelines.remove(item)
261 break
262 self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
263 if remaining_addlines:
264 self.fail('Expected added lines not found: %s' % remaining_addlines)
265 if remaining_removelines:
266 self.fail('Expected removed lines not found: %s' % remaining_removelines)
267
268 def _check_runqemu_prerequisites(self):
269 """Check runqemu is available
270
271 Whilst some tests would seemingly be better placed as a runtime test,
272 unfortunately the runtime tests run under bitbake and you can't run
273 devtool within bitbake (since devtool needs to run bitbake itself).
274 Additionally we are testing build-time functionality as well, so
275 really this has to be done as an oe-selftest test.
276 """
277 machine = get_bb_var('MACHINE')
278 if not machine.startswith('qemu'):
279 self.skipTest('This test only works with qemu machines')
280 if not os.path.exists('/etc/runqemu-nosudo'):
281 self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
282 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True)
283 if result.status != 0:
284 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True)
285 if result.status != 0:
286 self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output)
287 for line in result.output.splitlines():
288 if line.startswith('tap'):
289 break
290 else:
291 self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
292
293 def _test_devtool_add_git_url(self, git_url, version, pn, resulting_src_uri, srcrev=None):
294 self.track_for_cleanup(self.workspacedir)
295 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
296 command = 'devtool add --version %s %s %s' % (version, pn, git_url)
297 if srcrev :
298 command += ' --srcrev %s' %srcrev
299 result = runCmd(command)
300 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
301 # Check the recipe name is correct
302 recipefile = get_bb_var('FILE', pn)
303 self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
304 self.assertIn(recipefile, result.output)
305 # Test devtool status
306 result = runCmd('devtool status')
307 self.assertIn(pn, result.output)
308 self.assertIn(recipefile, result.output)
309 checkvars = {}
310 checkvars['SRC_URI'] = resulting_src_uri
311 self._test_recipe_contents(recipefile, checkvars, [])
312
313class DevtoolBase(DevtoolTestCase):
314
315 @classmethod
316 def setUpClass(cls):
317 super(DevtoolBase, cls).setUpClass()
318 bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
319 cls.original_sstate = bb_vars['SSTATE_DIR']
320 cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
321 cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
322 cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
323 % cls.original_sstate)
324 cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yoctoproject.org:8686"\n')
325
326 @classmethod
327 def tearDownClass(cls):
328 cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
329 runCmd('rm -rf %s' % cls.devtool_sstate)
330 super(DevtoolBase, cls).tearDownClass()
331
332 def setUp(self):
333 """Test case setup function"""
334 super(DevtoolBase, self).setUp()
335 self.append_config(self.sstate_conf)
336
238 337
239class DevtoolTests(DevtoolBase): 338class DevtoolTests(DevtoolBase):
240 339
@@ -304,6 +403,38 @@ class DevtoolAddTests(DevtoolBase):
304 bindir = bindir[1:] 403 bindir = bindir[1:]
305 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D') 404 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
306 405
406 def test_devtool_add_binary(self):
407 # Create a binary package containing a known test file
408 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
409 self.track_for_cleanup(tempdir)
410 pn = 'tst-bin'
411 pv = '1.0'
412 test_file_dir = "var/lib/%s/" % pn
413 test_file_name = "test_file"
414 test_file_content = "TEST CONTENT"
415 test_file_package_root = os.path.join(tempdir, pn)
416 test_file_dir_full = os.path.join(test_file_package_root, test_file_dir)
417 bb.utils.mkdirhier(test_file_dir_full)
418 with open(os.path.join(test_file_dir_full, test_file_name), "w") as f:
419 f.write(test_file_content)
420 bin_package_path = os.path.join(tempdir, "%s.tar.gz" % pn)
421 runCmd("tar czf %s -C %s ." % (bin_package_path, test_file_package_root))
422
423 # Test devtool add -b on the binary package
424 self.track_for_cleanup(self.workspacedir)
425 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
426 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
427 result = runCmd('devtool add -b %s %s' % (pn, bin_package_path))
428 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
429
430 # Build the resulting recipe
431 result = runCmd('devtool build %s' % pn)
432 installdir = get_bb_var('D', pn)
433 self.assertTrue(installdir, 'Could not query installdir variable')
434
435 # Check that a known file from the binary package has indeed been installed
436 self.assertTrue(os.path.isfile(os.path.join(installdir, test_file_dir, test_file_name)), '%s not found in D' % test_file_name)
437
307 def test_devtool_add_git_local(self): 438 def test_devtool_add_git_local(self):
308 # We need dbus built so that DEPENDS recognition works 439 # We need dbus built so that DEPENDS recognition works
309 bitbake('dbus') 440 bitbake('dbus')
@@ -336,15 +467,32 @@ class DevtoolAddTests(DevtoolBase):
336 self.assertIn(srcdir, result.output) 467 self.assertIn(srcdir, result.output)
337 self.assertIn(recipefile, result.output) 468 self.assertIn(recipefile, result.output)
338 checkvars = {} 469 checkvars = {}
339 checkvars['LICENSE'] = 'GPLv2' 470 checkvars['LICENSE'] = 'GPL-2.0-only'
340 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' 471 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
341 checkvars['S'] = '${WORKDIR}/git' 472 checkvars['S'] = None
342 checkvars['PV'] = '0.1+git${SRCPV}' 473 checkvars['PV'] = '0.1+git'
343 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https' 474 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https;branch=master'
344 checkvars['SRCREV'] = srcrev 475 checkvars['SRCREV'] = srcrev
345 checkvars['DEPENDS'] = set(['dbus']) 476 checkvars['DEPENDS'] = set(['dbus'])
346 self._test_recipe_contents(recipefile, checkvars, []) 477 self._test_recipe_contents(recipefile, checkvars, [])
347 478
479 def test_devtool_add_git_style1(self):
480 version = 'v3.1.0'
481 pn = 'mbedtls'
482 # this will trigger reformat_git_uri with branch parameter in url
483 git_url = "'git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https'"
484 resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https"
485 self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri)
486
487 def test_devtool_add_git_style2(self):
488 version = 'v3.1.0'
489 srcrev = 'v3.1.0'
490 pn = 'mbedtls'
491 # this will trigger reformat_git_uri with branch parameter in url
492 git_url = "'git://git@github.com/ARMmbed/mbedtls.git;protocol=https'"
493 resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;protocol=https;branch=master"
494 self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri, srcrev)
495
348 def test_devtool_add_library(self): 496 def test_devtool_add_library(self):
349 # Fetch source 497 # Fetch source
350 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 498 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
@@ -373,7 +521,7 @@ class DevtoolAddTests(DevtoolBase):
373 recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version) 521 recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version)
374 result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile) 522 result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile)
375 with open(recipefile, 'a') as f: 523 with open(recipefile, 'a') as f:
376 f.write('\nFILES_${PN}-dev += "${datadir}/cmake/Modules"\n') 524 f.write('\nFILES:${PN}-dev += "${datadir}/cmake/Modules"\n')
377 # We don't have the ability to pick up this dependency automatically yet... 525 # We don't have the ability to pick up this dependency automatically yet...
378 f.write('\nDEPENDS += "libusb1"\n') 526 f.write('\nDEPENDS += "libusb1"\n')
379 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n') 527 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
@@ -405,7 +553,7 @@ class DevtoolAddTests(DevtoolBase):
405 self.track_for_cleanup(self.workspacedir) 553 self.track_for_cleanup(self.workspacedir)
406 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe) 554 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
407 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 555 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
408 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url)) 556 result = runCmd('devtool add --no-pypi %s %s -f %s' % (testrecipe, srcdir, url))
409 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output) 557 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output)
410 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory') 558 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
411 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created') 559 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
@@ -417,14 +565,14 @@ class DevtoolAddTests(DevtoolBase):
417 recipefile = get_bb_var('FILE', testrecipe) 565 recipefile = get_bb_var('FILE', testrecipe)
418 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named') 566 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
419 checkvars = {} 567 checkvars = {}
420 checkvars['S'] = '${WORKDIR}/MarkupSafe-${PV}' 568 checkvars['S'] = '${UNPACKDIR}/MarkupSafe-${PV}'
421 checkvars['SRC_URI'] = url.replace(testver, '${PV}') 569 checkvars['SRC_URI'] = url.replace(testver, '${PV}')
422 self._test_recipe_contents(recipefile, checkvars, []) 570 self._test_recipe_contents(recipefile, checkvars, [])
423 # Try with version specified 571 # Try with version specified
424 result = runCmd('devtool reset -n %s' % testrecipe) 572 result = runCmd('devtool reset -n %s' % testrecipe)
425 shutil.rmtree(srcdir) 573 shutil.rmtree(srcdir)
426 fakever = '1.9' 574 fakever = '1.9'
427 result = runCmd('devtool add %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever)) 575 result = runCmd('devtool add --no-pypi %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever))
428 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory') 576 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
429 # Test devtool status 577 # Test devtool status
430 result = runCmd('devtool status') 578 result = runCmd('devtool status')
@@ -434,7 +582,7 @@ class DevtoolAddTests(DevtoolBase):
434 recipefile = get_bb_var('FILE', testrecipe) 582 recipefile = get_bb_var('FILE', testrecipe)
435 self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named') 583 self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named')
436 checkvars = {} 584 checkvars = {}
437 checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver 585 checkvars['S'] = '${UNPACKDIR}/MarkupSafe-%s' % testver
438 checkvars['SRC_URI'] = url 586 checkvars['SRC_URI'] = url
439 self._test_recipe_contents(recipefile, checkvars, []) 587 self._test_recipe_contents(recipefile, checkvars, [])
440 588
@@ -442,6 +590,7 @@ class DevtoolAddTests(DevtoolBase):
442 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 590 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
443 self.track_for_cleanup(tempdir) 591 self.track_for_cleanup(tempdir)
444 url = 'gitsm://git.yoctoproject.org/mraa' 592 url = 'gitsm://git.yoctoproject.org/mraa'
593 url_branch = '%s;branch=master' % url
445 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d' 594 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
446 testrecipe = 'mraa' 595 testrecipe = 'mraa'
447 srcdir = os.path.join(tempdir, testrecipe) 596 srcdir = os.path.join(tempdir, testrecipe)
@@ -460,9 +609,9 @@ class DevtoolAddTests(DevtoolBase):
460 recipefile = get_bb_var('FILE', testrecipe) 609 recipefile = get_bb_var('FILE', testrecipe)
461 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') 610 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
462 checkvars = {} 611 checkvars = {}
463 checkvars['S'] = '${WORKDIR}/git' 612 checkvars['S'] = None
464 checkvars['PV'] = '1.0+git${SRCPV}' 613 checkvars['PV'] = '1.0+git'
465 checkvars['SRC_URI'] = url 614 checkvars['SRC_URI'] = url_branch
466 checkvars['SRCREV'] = '${AUTOREV}' 615 checkvars['SRCREV'] = '${AUTOREV}'
467 self._test_recipe_contents(recipefile, checkvars, []) 616 self._test_recipe_contents(recipefile, checkvars, [])
468 # Try with revision and version specified 617 # Try with revision and version specified
@@ -479,9 +628,9 @@ class DevtoolAddTests(DevtoolBase):
479 recipefile = get_bb_var('FILE', testrecipe) 628 recipefile = get_bb_var('FILE', testrecipe)
480 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') 629 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
481 checkvars = {} 630 checkvars = {}
482 checkvars['S'] = '${WORKDIR}/git' 631 checkvars['S'] = None
483 checkvars['PV'] = '1.5+git${SRCPV}' 632 checkvars['PV'] = '1.5+git'
484 checkvars['SRC_URI'] = url 633 checkvars['SRC_URI'] = url_branch
485 checkvars['SRCREV'] = checkrev 634 checkvars['SRCREV'] = checkrev
486 self._test_recipe_contents(recipefile, checkvars, []) 635 self._test_recipe_contents(recipefile, checkvars, [])
487 636
@@ -504,7 +653,7 @@ class DevtoolAddTests(DevtoolBase):
504 result = runCmd('devtool status') 653 result = runCmd('devtool status')
505 self.assertIn(testrecipe, result.output) 654 self.assertIn(testrecipe, result.output)
506 self.assertIn(srcdir, result.output) 655 self.assertIn(srcdir, result.output)
507 # Check recipe 656 # Check recipedevtool add
508 recipefile = get_bb_var('FILE', testrecipe) 657 recipefile = get_bb_var('FILE', testrecipe)
509 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named') 658 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
510 checkvars = {} 659 checkvars = {}
@@ -536,6 +685,19 @@ class DevtoolAddTests(DevtoolBase):
536 # Test devtool build 685 # Test devtool build
537 result = runCmd('devtool build %s' % pn) 686 result = runCmd('devtool build %s' % pn)
538 687
688 def test_devtool_add_python_egg_requires(self):
689 # Fetch source
690 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
691 self.track_for_cleanup(tempdir)
692 testver = '0.14.0'
693 url = 'https://files.pythonhosted.org/packages/e9/9e/25d59f5043cf763833b2581c8027fa92342c4cf8ee523b498ecdf460c16d/uvicorn-%s.tar.gz' % testver
694 testrecipe = 'python3-uvicorn'
695 srcdir = os.path.join(tempdir, testrecipe)
696 # Test devtool add
697 self.track_for_cleanup(self.workspacedir)
698 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
699 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
700
539class DevtoolModifyTests(DevtoolBase): 701class DevtoolModifyTests(DevtoolBase):
540 702
541 def test_devtool_modify(self): 703 def test_devtool_modify(self):
@@ -595,6 +757,25 @@ class DevtoolModifyTests(DevtoolBase):
595 result = runCmd('devtool status') 757 result = runCmd('devtool status')
596 self.assertNotIn('mdadm', result.output) 758 self.assertNotIn('mdadm', result.output)
597 759
760 def test_devtool_modify_go(self):
761 import oe.path
762 from tempfile import TemporaryDirectory
763 with TemporaryDirectory(prefix='devtoolqa') as tempdir:
764 self.track_for_cleanup(self.workspacedir)
765 self.add_command_to_tearDown('bitbake -c clean go-helloworld')
766 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
767 result = runCmd('devtool modify go-helloworld -x %s' % tempdir)
768 self.assertExists(
769 oe.path.join(tempdir, 'src', 'golang.org', 'x', 'example', 'go.mod'),
770 'Extracted source could not be found'
771 )
772 self.assertExists(
773 oe.path.join(self.workspacedir, 'conf', 'layer.conf'),
774 'Workspace directory not created'
775 )
776 matches = glob.glob(oe.path.join(self.workspacedir, 'appends', 'go-helloworld_*.bbappend'))
777 self.assertTrue(matches, 'bbappend not created %s' % result.output)
778
598 def test_devtool_buildclean(self): 779 def test_devtool_buildclean(self):
599 def assertFile(path, *paths): 780 def assertFile(path, *paths):
600 f = os.path.join(path, *paths) 781 f = os.path.join(path, *paths)
@@ -649,7 +830,7 @@ class DevtoolModifyTests(DevtoolBase):
649 self.track_for_cleanup(self.workspacedir) 830 self.track_for_cleanup(self.workspacedir)
650 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 831 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
651 832
652 testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split() 833 testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk'.split()
653 # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose 834 # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose
654 result = runCmd('bitbake-layers show-recipes gcc-source*') 835 result = runCmd('bitbake-layers show-recipes gcc-source*')
655 for line in result.output.splitlines(): 836 for line in result.output.splitlines():
@@ -697,6 +878,7 @@ class DevtoolModifyTests(DevtoolBase):
697 878
698 self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes)) 879 self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
699 self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes)) 880 self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
881
700 def test_devtool_modify_localfiles_only(self): 882 def test_devtool_modify_localfiles_only(self):
701 # Check preconditions 883 # Check preconditions
702 testrecipe = 'base-files' 884 testrecipe = 'base-files'
@@ -720,13 +902,8 @@ class DevtoolModifyTests(DevtoolBase):
720 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 902 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
721 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 903 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
722 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 904 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
723 srcfile = os.path.join(tempdir, 'oe-local-files/share/dot.bashrc') 905 srcfile = os.path.join(tempdir, 'share/dot.bashrc')
724 srclink = os.path.join(tempdir, 'share/dot.bashrc')
725 self.assertExists(srcfile, 'Extracted source could not be found') 906 self.assertExists(srcfile, 'Extracted source could not be found')
726 if os.path.islink(srclink) and os.path.exists(srclink) and os.path.samefile(srcfile, srclink):
727 correct_symlink = True
728 self.assertTrue(correct_symlink, 'Source symlink to oe-local-files is broken')
729
730 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe)) 907 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
731 self.assertTrue(matches, 'bbappend not created') 908 self.assertTrue(matches, 'bbappend not created')
732 # Test devtool status 909 # Test devtool status
@@ -763,6 +940,122 @@ class DevtoolModifyTests(DevtoolBase):
763 # Try building 940 # Try building
764 bitbake(testrecipe) 941 bitbake(testrecipe)
765 942
943 def test_devtool_modify_git_no_extract(self):
944 # Check preconditions
945 testrecipe = 'psplash'
946 src_uri = get_bb_var('SRC_URI', testrecipe)
947 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
948 # Clean up anything in the workdir/sysroot/sstate cache
949 bitbake('%s -c cleansstate' % testrecipe)
950 # Try modifying a recipe
951 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
952 self.track_for_cleanup(tempdir)
953 self.track_for_cleanup(self.workspacedir)
954 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
955 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
956 result = runCmd('git clone https://git.yoctoproject.org/psplash %s && devtool modify -n %s %s' % (tempdir, testrecipe, tempdir))
957 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
958 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend'))
959 self.assertTrue(matches, 'bbappend not created')
960 # Test devtool status
961 result = runCmd('devtool status')
962 self.assertIn(testrecipe, result.output)
963 self.assertIn(tempdir, result.output)
964
965 def test_devtool_modify_git_crates_subpath(self):
966 # This tests two things in devtool context:
967 # - that we support local git dependencies for cargo based recipe
968 # - that we support patches in SRC_URI when git url contains subpath parameter
969
970 # Check preconditions:
971 # recipe inherits cargo
972 # git:// uri with a subpath as the main package
973 # some crate:// in SRC_URI
974 # others git:// in SRC_URI
975 # cointains a patch
976 testrecipe = 'hello-rs'
977 bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'UNPACKDIR', 'CARGO_HOME'], testrecipe)
978 recipefile = bb_vars['FILE']
979 unpackdir = bb_vars['UNPACKDIR']
980 cargo_home = bb_vars['CARGO_HOME']
981 src_uri = bb_vars['SRC_URI'].split()
982 self.assertTrue(src_uri[0].startswith('git://'),
983 'This test expects the %s recipe to have a git repo has its main uri' % testrecipe)
984 self.assertIn(';subpath=', src_uri[0],
985 'This test expects the %s recipe to have a git uri with subpath' % testrecipe)
986 self.assertTrue(any([uri.startswith('crate://') for uri in src_uri]),
987 'This test expects the %s recipe to have some crates in its src uris' % testrecipe)
988 self.assertGreaterEqual(sum(map(lambda x:x.startswith('git://'), src_uri)), 2,
989 'This test expects the %s recipe to have several git:// uris' % testrecipe)
990 self.assertTrue(any([uri.startswith('file://') and '.patch' in uri for uri in src_uri]),
991 'This test expects the %s recipe to have a patch in its src uris' % testrecipe)
992
993 self._test_recipe_contents(recipefile, {}, ['ptest-cargo'])
994
995 # Clean up anything in the workdir/sysroot/sstate cache
996 bitbake('%s -c cleansstate' % testrecipe)
997 # Try modifying a recipe
998 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
999 self.track_for_cleanup(tempdir)
1000 self.track_for_cleanup(self.workspacedir)
1001 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1002 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1003 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1004 self.assertExists(os.path.join(tempdir, 'Cargo.toml'), 'Extracted source could not be found')
1005 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
1006 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
1007 self.assertTrue(matches, 'bbappend not created')
1008 # Test devtool status
1009 result = runCmd('devtool status')
1010 self.assertIn(testrecipe, result.output)
1011 self.assertIn(tempdir, result.output)
1012 # Check git repo
1013 self._check_src_repo(tempdir)
1014 # Check that the patch is correctly applied.
1015 # The last commit message in the tree must contain the following note:
1016 # Notes (devtool):
1017 # original patch: <patchname>
1018 # ..
1019 patchname = None
1020 for uri in src_uri:
1021 if uri.startswith('file://') and '.patch' in uri:
1022 patchname = uri.replace("file://", "").partition('.patch')[0] + '.patch'
1023 self.assertIsNotNone(patchname)
1024 result = runCmd('git -C %s log -1' % tempdir)
1025 self.assertIn("Notes (devtool):\n original patch: %s" % patchname, result.output)
1026
1027 # Configure the recipe to check that the git dependencies are correctly patched in cargo config
1028 bitbake('-c configure %s' % testrecipe)
1029
1030 cargo_config_path = os.path.join(cargo_home, 'config.toml')
1031 with open(cargo_config_path, "r") as f:
1032 cargo_config_contents = [line.strip('\n') for line in f.readlines()]
1033
1034 # Get back git dependencies of the recipe (ignoring the main one)
1035 # and check that they are all correctly patched to be fetched locally
1036 git_deps = [uri for uri in src_uri if uri.startswith("git://")][1:]
1037 for git_dep in git_deps:
1038 raw_url, _, raw_parms = git_dep.partition(";")
1039 parms = {}
1040 for parm in raw_parms.split(";"):
1041 name_parm, _, value_parm = parm.partition('=')
1042 parms[name_parm]=value_parm
1043 self.assertIn('protocol', parms, 'git dependencies uri should contain the "protocol" parameter')
1044 self.assertIn('name', parms, 'git dependencies uri should contain the "name" parameter')
1045 self.assertIn('destsuffix', parms, 'git dependencies uri should contain the "destsuffix" parameter')
1046 self.assertIn('type', parms, 'git dependencies uri should contain the "type" parameter')
1047 self.assertEqual(parms['type'], 'git-dependency', 'git dependencies uri should have "type=git-dependency"')
1048 raw_url = raw_url.replace("git://", '%s://' % parms['protocol'])
1049 patch_line = '[patch."%s"]' % raw_url
1050 path_patched = os.path.join(unpackdir, parms['destsuffix'])
1051 path_override_line = '%s = { path = "%s" }' % (parms['name'], path_patched)
1052 # Would have been better to use tomllib to read this file :/
1053 self.assertIn(patch_line, cargo_config_contents)
1054 self.assertIn(path_override_line, cargo_config_contents)
1055
1056 # Try to package the recipe
1057 bitbake('-c package_qa %s' % testrecipe)
1058
766 def test_devtool_modify_localfiles(self): 1059 def test_devtool_modify_localfiles(self):
767 # Check preconditions 1060 # Check preconditions
768 testrecipe = 'lighttpd' 1061 testrecipe = 'lighttpd'
@@ -828,12 +1121,43 @@ class DevtoolModifyTests(DevtoolBase):
828 runCmd('git -C %s checkout %s' % (tempdir, branch)) 1121 runCmd('git -C %s checkout %s' % (tempdir, branch))
829 with open(source, "rt") as f: 1122 with open(source, "rt") as f:
830 content = f.read() 1123 content = f.read()
831 self.assertEquals(content, expected) 1124 self.assertEqual(content, expected)
832 check('devtool', 'This is a test for something\n') 1125 if self.td["MACHINE"] == "qemux86":
1126 check('devtool', 'This is a test for qemux86\n')
1127 elif self.td["MACHINE"] == "qemuarm":
1128 check('devtool', 'This is a test for qemuarm\n')
1129 else:
1130 check('devtool', 'This is a test for something\n')
833 check('devtool-no-overrides', 'This is a test for something\n') 1131 check('devtool-no-overrides', 'This is a test for something\n')
834 check('devtool-override-qemuarm', 'This is a test for qemuarm\n') 1132 check('devtool-override-qemuarm', 'This is a test for qemuarm\n')
835 check('devtool-override-qemux86', 'This is a test for qemux86\n') 1133 check('devtool-override-qemux86', 'This is a test for qemux86\n')
836 1134
1135 def test_devtool_modify_multiple_sources(self):
1136 # This test check that recipes fetching several sources can be used with devtool modify/build
1137 # Check preconditions
1138 testrecipe = 'bzip2'
1139 src_uri = get_bb_var('SRC_URI', testrecipe)
1140 src1 = 'https://' in src_uri
1141 src2 = 'git://' in src_uri
1142 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)
1143 # Clean up anything in the workdir/sysroot/sstate cache
1144 bitbake('%s -c cleansstate' % testrecipe)
1145 # Try modifying a recipe
1146 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1147 self.track_for_cleanup(tempdir)
1148 self.track_for_cleanup(self.workspacedir)
1149 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1150 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1151 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1152 self.assertEqual(result.status, 0, "Could not modify recipe %s. Output: %s" % (testrecipe, result.output))
1153 # Test devtool status
1154 result = runCmd('devtool status')
1155 self.assertIn(testrecipe, result.output)
1156 self.assertIn(tempdir, result.output)
1157 # Try building
1158 result = bitbake(testrecipe)
1159 self.assertEqual(result.status, 0, "Bitbake failed, exit code %s, output %s" % (result.status, result.output))
1160
837class DevtoolUpdateTests(DevtoolBase): 1161class DevtoolUpdateTests(DevtoolBase):
838 1162
839 def test_devtool_update_recipe(self): 1163 def test_devtool_update_recipe(self):
@@ -861,16 +1185,20 @@ class DevtoolUpdateTests(DevtoolBase):
861 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir) 1185 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
862 result = runCmd('git add devtool-new-file', cwd=tempdir) 1186 result = runCmd('git add devtool-new-file', cwd=tempdir)
863 result = runCmd('git commit -m "Add a new file"', cwd=tempdir) 1187 result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
864 self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1188 cleanup_cmd = 'cd %s; rm %s/*.patch; git add %s; git checkout %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))
1189 self.add_command_to_tearDown(cleanup_cmd)
865 result = runCmd('devtool update-recipe %s' % testrecipe) 1190 result = runCmd('devtool update-recipe %s' % testrecipe)
1191 result = runCmd('git add minicom', cwd=os.path.dirname(recipefile))
866 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1192 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
867 ('??', '.*/0001-Change-the-README.patch$'), 1193 ('A ', '.*/0001-Change-the-README.patch$'),
868 ('??', '.*/0002-Add-a-new-file.patch$')] 1194 ('A ', '.*/0002-Add-a-new-file.patch$')]
869 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1195 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1196 result = runCmd(cleanup_cmd)
1197 self._check_repo_status(os.path.dirname(recipefile), [])
870 1198
871 def test_devtool_update_recipe_git(self): 1199 def test_devtool_update_recipe_git(self):
872 # Check preconditions 1200 # Check preconditions
873 testrecipe = 'mtd-utils' 1201 testrecipe = 'mtd-utils-selftest'
874 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1202 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
875 recipefile = bb_vars['FILE'] 1203 recipefile = bb_vars['FILE']
876 src_uri = bb_vars['SRC_URI'] 1204 src_uri = bb_vars['SRC_URI']
@@ -904,28 +1232,12 @@ class DevtoolUpdateTests(DevtoolBase):
904 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1232 self._check_repo_status(os.path.dirname(recipefile), expected_status)
905 1233
906 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile)) 1234 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
907 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git"'] 1235 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git;branch=master"']
908 srcurilines = src_uri.split() 1236 srcurilines = src_uri.split()
909 srcurilines[0] = 'SRC_URI = "' + srcurilines[0] 1237 srcurilines[0] = 'SRC_URI = "' + srcurilines[0]
910 srcurilines.append('"') 1238 srcurilines.append('"')
911 removelines = ['SRCREV = ".*"'] + srcurilines 1239 removelines = ['SRCREV = ".*"'] + srcurilines
912 for line in result.output.splitlines(): 1240 self._check_diff(result.output, addlines, removelines)
913 if line.startswith('+++') or line.startswith('---'):
914 continue
915 elif line.startswith('+'):
916 matched = False
917 for item in addlines:
918 if re.match(item, line[1:].strip()):
919 matched = True
920 break
921 self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
922 elif line.startswith('-'):
923 matched = False
924 for item in removelines:
925 if re.match(item, line[1:].strip()):
926 matched = True
927 break
928 self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
929 # Now try with auto mode 1241 # Now try with auto mode
930 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile))) 1242 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
931 result = runCmd('devtool update-recipe %s' % testrecipe) 1243 result = runCmd('devtool update-recipe %s' % testrecipe)
@@ -939,7 +1251,7 @@ class DevtoolUpdateTests(DevtoolBase):
939 1251
940 def test_devtool_update_recipe_append(self): 1252 def test_devtool_update_recipe_append(self):
941 # Check preconditions 1253 # Check preconditions
942 testrecipe = 'mdadm' 1254 testrecipe = 'minicom'
943 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1255 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
944 recipefile = bb_vars['FILE'] 1256 recipefile = bb_vars['FILE']
945 src_uri = bb_vars['SRC_URI'] 1257 src_uri = bb_vars['SRC_URI']
@@ -957,7 +1269,7 @@ class DevtoolUpdateTests(DevtoolBase):
957 # Check git repo 1269 # Check git repo
958 self._check_src_repo(tempsrcdir) 1270 self._check_src_repo(tempsrcdir)
959 # Add a commit 1271 # Add a commit
960 result = runCmd("sed 's!\\(#define VERSION\\W*\"[^\"]*\\)\"!\\1-custom\"!' -i ReadMe.c", cwd=tempsrcdir) 1272 result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir)
961 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) 1273 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
962 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe)) 1274 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
963 # Create a temporary layer and add it to bblayers.conf 1275 # Create a temporary layer and add it to bblayers.conf
@@ -975,7 +1287,7 @@ class DevtoolUpdateTests(DevtoolBase):
975 self.assertExists(patchfile, 'Patch file not created') 1287 self.assertExists(patchfile, 'Patch file not created')
976 1288
977 # Check bbappend contents 1289 # Check bbappend contents
978 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n', 1290 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
979 '\n', 1291 '\n',
980 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n', 1292 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n',
981 '\n'] 1293 '\n']
@@ -987,15 +1299,16 @@ class DevtoolUpdateTests(DevtoolBase):
987 with open(bbappendfile, 'r') as f: 1299 with open(bbappendfile, 'r') as f:
988 self.assertEqual(expectedlines, f.readlines()) 1300 self.assertEqual(expectedlines, f.readlines())
989 # Drop new commit and check patch gets deleted 1301 # Drop new commit and check patch gets deleted
990 result = runCmd('git reset HEAD^', cwd=tempsrcdir) 1302 result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir)
991 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1303 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
992 self.assertNotExists(patchfile, 'Patch file not deleted') 1304 self.assertNotExists(patchfile, 'Patch file not deleted')
993 expectedlines2 = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n', 1305 expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
994 '\n'] 1306 '\n']
995 with open(bbappendfile, 'r') as f: 1307 with open(bbappendfile, 'r') as f:
996 self.assertEqual(expectedlines2, f.readlines()) 1308 self.assertEqual(expectedlines2, f.readlines())
997 # Put commit back and check we can run it if layer isn't in bblayers.conf 1309 # Put commit back and check we can run it if layer isn't in bblayers.conf
998 os.remove(bbappendfile) 1310 os.remove(bbappendfile)
1311 result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir)
999 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) 1312 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
1000 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) 1313 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
1001 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1314 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
@@ -1007,10 +1320,11 @@ class DevtoolUpdateTests(DevtoolBase):
1007 1320
1008 def test_devtool_update_recipe_append_git(self): 1321 def test_devtool_update_recipe_append_git(self):
1009 # Check preconditions 1322 # Check preconditions
1010 testrecipe = 'mtd-utils' 1323 testrecipe = 'mtd-utils-selftest'
1011 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1324 bb_vars = get_bb_vars(['FILE', 'SRC_URI', 'LAYERSERIES_CORENAMES'], testrecipe)
1012 recipefile = bb_vars['FILE'] 1325 recipefile = bb_vars['FILE']
1013 src_uri = bb_vars['SRC_URI'] 1326 src_uri = bb_vars['SRC_URI']
1327 corenames = bb_vars['LAYERSERIES_CORENAMES']
1014 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe) 1328 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
1015 for entry in src_uri.split(): 1329 for entry in src_uri.split():
1016 if entry.startswith('git://'): 1330 if entry.startswith('git://'):
@@ -1041,7 +1355,7 @@ class DevtoolUpdateTests(DevtoolBase):
1041 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n') 1355 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n')
1042 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n') 1356 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n')
1043 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n') 1357 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n')
1044 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "${LAYERSERIES_COMPAT_core}"\n') 1358 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "%s"\n' % corenames)
1045 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir) 1359 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
1046 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir) 1360 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
1047 # Create the bbappend 1361 # Create the bbappend
@@ -1069,7 +1383,7 @@ class DevtoolUpdateTests(DevtoolBase):
1069 with open(bbappendfile, 'r') as f: 1383 with open(bbappendfile, 'r') as f:
1070 self.assertEqual(expectedlines, set(f.readlines())) 1384 self.assertEqual(expectedlines, set(f.readlines()))
1071 # Drop new commit and check SRCREV changes 1385 # Drop new commit and check SRCREV changes
1072 result = runCmd('git reset HEAD^', cwd=tempsrcdir) 1386 result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir)
1073 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1387 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1074 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') 1388 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
1075 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) 1389 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
@@ -1081,6 +1395,7 @@ class DevtoolUpdateTests(DevtoolBase):
1081 self.assertEqual(expectedlines, set(f.readlines())) 1395 self.assertEqual(expectedlines, set(f.readlines()))
1082 # Put commit back and check we can run it if layer isn't in bblayers.conf 1396 # Put commit back and check we can run it if layer isn't in bblayers.conf
1083 os.remove(bbappendfile) 1397 os.remove(bbappendfile)
1398 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
1084 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir) 1399 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
1085 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) 1400 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
1086 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1401 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
@@ -1112,22 +1427,39 @@ class DevtoolUpdateTests(DevtoolBase):
1112 # Try building just to ensure we haven't broken that 1427 # Try building just to ensure we haven't broken that
1113 bitbake("%s" % testrecipe) 1428 bitbake("%s" % testrecipe)
1114 # Edit / commit local source 1429 # Edit / commit local source
1115 runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir) 1430 runCmd('echo "/* Foobar */" >> makedevs.c', cwd=tempdir)
1116 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir) 1431 runCmd('echo "Foo" > new-local', cwd=tempdir)
1117 runCmd('echo "Bar" > new-file', cwd=tempdir) 1432 runCmd('echo "Bar" > new-file', cwd=tempdir)
1118 runCmd('git add new-file', cwd=tempdir) 1433 runCmd('git add new-file', cwd=tempdir)
1119 runCmd('git commit -m "Add new file"', cwd=tempdir) 1434 runCmd('git commit -m "Add new file"', cwd=tempdir)
1120 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' % 1435 runCmd('git add new-local', cwd=tempdir)
1121 os.path.dirname(recipefile))
1122 runCmd('devtool update-recipe %s' % testrecipe) 1436 runCmd('devtool update-recipe %s' % testrecipe)
1123 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1437 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1124 (' M', '.*/makedevs/makedevs.c$'), 1438 (' M', '.*/makedevs/makedevs.c$'),
1125 ('??', '.*/makedevs/new-local$'), 1439 ('??', '.*/makedevs/new-local$'),
1126 ('??', '.*/makedevs/0001-Add-new-file.patch$')] 1440 ('??', '.*/makedevs/0001-Add-new-file.patch$')]
1127 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1441 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1128 1442 # Now try to update recipe in another layer, so first, clean it
1129 def test_devtool_update_recipe_local_files_2(self): 1443 runCmd('cd %s; git clean -fd .; git checkout .' % os.path.dirname(recipefile))
1130 """Check local source files support when oe-local-files is in Git""" 1444 # Create a temporary layer and add it to bblayers.conf
1445 self._create_temp_layer(templayerdir, True, 'templayer')
1446 # Update recipe in templayer
1447 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
1448 self.assertNotIn('WARNING:', result.output)
1449 # Check recipe is still clean
1450 self._check_repo_status(os.path.dirname(recipefile), [])
1451 splitpath = os.path.dirname(recipefile).split(os.sep)
1452 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
1453 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
1454 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-new-file.patch')
1455 new_local_file = os.path.join(appenddir, testrecipe, 'new_local')
1456 local_file = os.path.join(appenddir, testrecipe, 'makedevs.c')
1457 self.assertExists(patchfile, 'Patch file 0001-Add-new-file.patch not created')
1458 self.assertExists(local_file, 'File makedevs.c not created')
1459 self.assertExists(patchfile, 'File new_local not created')
1460
1461 def _test_devtool_update_recipe_local_files_2(self):
1462 """Check local source files support when editing local files in Git"""
1131 testrecipe = 'devtool-test-local' 1463 testrecipe = 'devtool-test-local'
1132 recipefile = get_bb_var('FILE', testrecipe) 1464 recipefile = get_bb_var('FILE', testrecipe)
1133 recipedir = os.path.dirname(recipefile) 1465 recipedir = os.path.dirname(recipefile)
@@ -1142,17 +1474,13 @@ class DevtoolUpdateTests(DevtoolBase):
1142 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1474 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1143 # Check git repo 1475 # Check git repo
1144 self._check_src_repo(tempdir) 1476 self._check_src_repo(tempdir)
1145 # Add oe-local-files to Git
1146 runCmd('rm oe-local-files/.gitignore', cwd=tempdir)
1147 runCmd('git add oe-local-files', cwd=tempdir)
1148 runCmd('git commit -m "Add local sources"', cwd=tempdir)
1149 # Edit / commit local sources 1477 # Edit / commit local sources
1150 runCmd('echo "# Foobar" >> oe-local-files/file1', cwd=tempdir) 1478 runCmd('echo "# Foobar" >> file1', cwd=tempdir)
1151 runCmd('git commit -am "Edit existing file"', cwd=tempdir) 1479 runCmd('git commit -am "Edit existing file"', cwd=tempdir)
1152 runCmd('git rm oe-local-files/file2', cwd=tempdir) 1480 runCmd('git rm file2', cwd=tempdir)
1153 runCmd('git commit -m"Remove file"', cwd=tempdir) 1481 runCmd('git commit -m"Remove file"', cwd=tempdir)
1154 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir) 1482 runCmd('echo "Foo" > new-local', cwd=tempdir)
1155 runCmd('git add oe-local-files/new-local', cwd=tempdir) 1483 runCmd('git add new-local', cwd=tempdir)
1156 runCmd('git commit -m "Add new local file"', cwd=tempdir) 1484 runCmd('git commit -m "Add new local file"', cwd=tempdir)
1157 runCmd('echo "Gar" > new-file', cwd=tempdir) 1485 runCmd('echo "Gar" > new-file', cwd=tempdir)
1158 runCmd('git add new-file', cwd=tempdir) 1486 runCmd('git add new-file', cwd=tempdir)
@@ -1161,7 +1489,7 @@ class DevtoolUpdateTests(DevtoolBase):
1161 os.path.dirname(recipefile)) 1489 os.path.dirname(recipefile))
1162 # Checkout unmodified file to working copy -> devtool should still pick 1490 # Checkout unmodified file to working copy -> devtool should still pick
1163 # the modified version from HEAD 1491 # the modified version from HEAD
1164 runCmd('git checkout HEAD^ -- oe-local-files/file1', cwd=tempdir) 1492 runCmd('git checkout HEAD^ -- file1', cwd=tempdir)
1165 runCmd('devtool update-recipe %s' % testrecipe) 1493 runCmd('devtool update-recipe %s' % testrecipe)
1166 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1494 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1167 (' M', '.*/file1$'), 1495 (' M', '.*/file1$'),
@@ -1236,7 +1564,7 @@ class DevtoolUpdateTests(DevtoolBase):
1236 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1564 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1237 result = runCmd('devtool modify %s' % testrecipe) 1565 result = runCmd('devtool modify %s' % testrecipe)
1238 # Modify one file 1566 # Modify one file
1239 runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files')) 1567 runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe))
1240 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1568 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1241 result = runCmd('devtool update-recipe %s' % testrecipe) 1569 result = runCmd('devtool update-recipe %s' % testrecipe)
1242 expected_status = [(' M', '.*/%s/file2$' % testrecipe)] 1570 expected_status = [(' M', '.*/%s/file2$' % testrecipe)]
@@ -1259,7 +1587,7 @@ class DevtoolUpdateTests(DevtoolBase):
1259 # Modify one file 1587 # Modify one file
1260 srctree = os.path.join(self.workspacedir, 'sources', testrecipe) 1588 srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
1261 runCmd('echo "Another line" >> README', cwd=srctree) 1589 runCmd('echo "Another line" >> README', cwd=srctree)
1262 runCmd('git commit -a --amend --no-edit', cwd=srctree) 1590 runCmd('git commit -a --amend --no-edit --no-verify', cwd=srctree)
1263 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1591 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1264 result = runCmd('devtool update-recipe %s' % testrecipe) 1592 result = runCmd('devtool update-recipe %s' % testrecipe)
1265 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)] 1593 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
@@ -1295,6 +1623,121 @@ class DevtoolUpdateTests(DevtoolBase):
1295 expected_status = [] 1623 expected_status = []
1296 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1624 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1297 1625
1626 def test_devtool_finish_modify_git_subdir(self):
1627 # Check preconditions
1628 testrecipe = 'dos2unix'
1629 self.append_config('ERROR_QA:remove:pn-dos2unix = "patch-status"\n')
1630 bb_vars = get_bb_vars(['SRC_URI', 'S', 'UNPACKDIR', 'FILE', 'BB_GIT_DEFAULT_DESTSUFFIX'], testrecipe)
1631 self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe)
1632 unpackdir_git = '%s/%s/' % (bb_vars['UNPACKDIR'], bb_vars['BB_GIT_DEFAULT_DESTSUFFIX'])
1633 if not bb_vars['S'].startswith(unpackdir_git):
1634 self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe)
1635 subdir = bb_vars['S'].split(unpackdir_git, 1)[1]
1636 # Clean up anything in the workdir/sysroot/sstate cache
1637 bitbake('%s -c cleansstate' % testrecipe)
1638 # Try modifying a recipe
1639 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1640 self.track_for_cleanup(tempdir)
1641 self.track_for_cleanup(self.workspacedir)
1642 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1643 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1644 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1645 testsrcfile = os.path.join(tempdir, subdir, 'dos2unix.c')
1646 self.assertExists(testsrcfile, 'Extracted source could not be found')
1647 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
1648 self.assertNotExists(os.path.join(tempdir, subdir, '.git'), 'Subdirectory has been initialised as a git repo')
1649 # Check git repo
1650 self._check_src_repo(tempdir)
1651 # Modify file
1652 runCmd("sed -i '1s:^:/* Add a comment */\\n:' %s" % testsrcfile)
1653 result = runCmd('git commit -a -m "Add a comment"', cwd=tempdir)
1654 # Now try updating original recipe
1655 recipefile = bb_vars['FILE']
1656 recipedir = os.path.dirname(recipefile)
1657 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
1658 result = runCmd('devtool update-recipe %s' % testrecipe)
1659 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1660 ('??', '.*/%s/%s/$' % (testrecipe, testrecipe))]
1661 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1662 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
1663 removelines = ['SRC_URI = "git://.*"']
1664 addlines = [
1665 'SRC_URI = "git://.* \\\\',
1666 'file://0001-Add-a-comment.patch;patchdir=.. \\\\',
1667 '"'
1668 ]
1669 self._check_diff(result.output, addlines, removelines)
1670 # Put things back so we can run devtool finish on a different layer
1671 runCmd('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
1672 # Run devtool finish
1673 res = re.search('recipes-.*', recipedir)
1674 self.assertTrue(res, 'Unable to find recipe subdirectory')
1675 recipesubdir = res[0]
1676 self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, recipesubdir))
1677 result = runCmd('devtool finish %s meta-selftest' % testrecipe)
1678 # Check bbappend file contents
1679 appendfn = os.path.join(self.testlayer_path, recipesubdir, '%s_%%.bbappend' % testrecipe)
1680 with open(appendfn, 'r') as f:
1681 appendlines = f.readlines()
1682 expected_appendlines = [
1683 'FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
1684 '\n',
1685 'SRC_URI += "file://0001-Add-a-comment.patch;patchdir=.."\n',
1686 '\n'
1687 ]
1688 self.assertEqual(appendlines, expected_appendlines)
1689 self.assertExists(os.path.join(os.path.dirname(appendfn), testrecipe, '0001-Add-a-comment.patch'))
1690 # Try building
1691 bitbake('%s -c patch' % testrecipe)
1692
1693 def test_devtool_git_submodules(self):
1694 # This tests if we can add a patch in a git submodule and extract it properly using devtool finish
1695 # Check preconditions
1696 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1697 self.track_for_cleanup(self.workspacedir)
1698 recipe = 'vulkan-samples'
1699 src_uri = get_bb_var('SRC_URI', recipe)
1700 self.assertIn('gitsm://', src_uri, 'This test expects the %s recipe to be a git recipe with submodules' % recipe)
1701 oldrecipefile = get_bb_var('FILE', recipe)
1702 recipedir = os.path.dirname(oldrecipefile)
1703 result = runCmd('git status --porcelain .', cwd=recipedir)
1704 if result.output.strip():
1705 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
1706 self.assertIn('/meta/', recipedir)
1707 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1708 self.track_for_cleanup(tempdir)
1709 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1710 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
1711 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
1712 # Test devtool status
1713 result = runCmd('devtool status')
1714 self.assertIn(recipe, result.output)
1715 self.assertIn(tempdir, result.output)
1716 # Modify a source file in a submodule, (grab the first one)
1717 result = runCmd('git submodule --quiet foreach \'echo $sm_path\'', cwd=tempdir)
1718 submodule = result.output.splitlines()[0]
1719 submodule_path = os.path.join(tempdir, submodule)
1720 runCmd('echo "#This is a first comment" >> testfile', cwd=submodule_path)
1721 result = runCmd('git status --porcelain . ', cwd=submodule_path)
1722 self.assertIn("testfile", result.output)
1723 runCmd('git add testfile; git commit -m "Adding a new file"', cwd=submodule_path)
1724
1725 # Try finish to the original layer
1726 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1727 runCmd('devtool finish -f %s meta' % recipe)
1728 result = runCmd('devtool status')
1729 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1730 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
1731 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
1732 ('??', '.*/.*-Adding-a-new-file.patch$')]
1733 self._check_repo_status(recipedir, expected_status)
1734 # Make sure the patch is added to the recipe with the correct "patchdir" option
1735 result = runCmd('git diff .', cwd=recipedir)
1736 addlines = [
1737 'file://0001-Adding-a-new-file.patch;patchdir=%s \\\\' % submodule
1738 ]
1739 self._check_diff(result.output, addlines, [])
1740
1298class DevtoolExtractTests(DevtoolBase): 1741class DevtoolExtractTests(DevtoolBase):
1299 1742
1300 def test_devtool_extract(self): 1743 def test_devtool_extract(self):
@@ -1317,6 +1760,8 @@ class DevtoolExtractTests(DevtoolBase):
1317 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') 1760 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
1318 self._check_src_repo(tempdir) 1761 self._check_src_repo(tempdir)
1319 1762
1763class DevtoolResetTests(DevtoolBase):
1764
1320 def test_devtool_reset_all(self): 1765 def test_devtool_reset_all(self):
1321 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1766 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1322 self.track_for_cleanup(tempdir) 1767 self.track_for_cleanup(tempdir)
@@ -1343,33 +1788,30 @@ class DevtoolExtractTests(DevtoolBase):
1343 matches2 = glob.glob(stampprefix2 + '*') 1788 matches2 = glob.glob(stampprefix2 + '*')
1344 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2) 1789 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
1345 1790
1791 def test_devtool_reset_re_plus_plus(self):
1792 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1793 self.track_for_cleanup(tempdir)
1794 self.track_for_cleanup(self.workspacedir)
1795 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1796 testrecipe = 'devtool-test-reset-re++'
1797 result = runCmd('devtool modify %s' % testrecipe)
1798 result = runCmd('devtool reset -n %s' % testrecipe)
1799 self.assertIn(testrecipe, result.output)
1800 result = runCmd('devtool status')
1801 self.assertNotIn(testrecipe, result.output)
1802 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', testrecipe), 'Recipe directory should not exist after resetting')
1803
1804class DevtoolDeployTargetTests(DevtoolBase):
1805
1806 @OETestTag("runqemu")
1346 def test_devtool_deploy_target(self): 1807 def test_devtool_deploy_target(self):
1347 # NOTE: Whilst this test would seemingly be better placed as a runtime test, 1808 self._check_runqemu_prerequisites()
1348 # unfortunately the runtime tests run under bitbake and you can't run
1349 # devtool within bitbake (since devtool needs to run bitbake itself).
1350 # Additionally we are testing build-time functionality as well, so
1351 # really this has to be done as an oe-selftest test.
1352 #
1353 # Check preconditions
1354 machine = get_bb_var('MACHINE')
1355 if not machine.startswith('qemu'):
1356 self.skipTest('This test only works with qemu machines')
1357 if not os.path.exists('/etc/runqemu-nosudo'):
1358 self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
1359 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True)
1360 if result.status != 0:
1361 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True)
1362 if result.status != 0:
1363 self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output)
1364 for line in result.output.splitlines():
1365 if line.startswith('tap'):
1366 break
1367 else:
1368 self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
1369 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1809 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1370 # Definitions 1810 # Definitions
1371 testrecipe = 'mdadm' 1811 testrecipe = 'mdadm'
1372 testfile = '/sbin/mdadm' 1812 testfile = '/sbin/mdadm'
1813 if "usrmerge" in get_bb_var('DISTRO_FEATURES'):
1814 testfile = '/usr/sbin/mdadm'
1373 testimage = 'oe-selftest-image' 1815 testimage = 'oe-selftest-image'
1374 testcommand = '/sbin/mdadm --help' 1816 testcommand = '/sbin/mdadm --help'
1375 # Build an image to run 1817 # Build an image to run
@@ -1428,6 +1870,8 @@ class DevtoolExtractTests(DevtoolBase):
1428 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True) 1870 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
1429 self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') 1871 self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
1430 1872
1873class DevtoolBuildImageTests(DevtoolBase):
1874
1431 def test_devtool_build_image(self): 1875 def test_devtool_build_image(self):
1432 """Test devtool build-image plugin""" 1876 """Test devtool build-image plugin"""
1433 # Check preconditions 1877 # Check preconditions
@@ -1463,6 +1907,14 @@ class DevtoolExtractTests(DevtoolBase):
1463 1907
1464class DevtoolUpgradeTests(DevtoolBase): 1908class DevtoolUpgradeTests(DevtoolBase):
1465 1909
1910 def setUp(self):
1911 super().setUp()
1912 try:
1913 runCmd("git config --global user.name")
1914 runCmd("git config --global user.email")
1915 except:
1916 self.skip("Git user.name and user.email must be set")
1917
1466 def test_devtool_upgrade(self): 1918 def test_devtool_upgrade(self):
1467 # Check preconditions 1919 # Check preconditions
1468 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1920 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
@@ -1543,6 +1995,100 @@ class DevtoolUpgradeTests(DevtoolBase):
1543 self.assertNotIn(recipe, result.output) 1995 self.assertNotIn(recipe, result.output)
1544 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') 1996 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
1545 1997
1998 def test_devtool_upgrade_drop_md5sum(self):
1999 # Check preconditions
2000 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2001 self.track_for_cleanup(self.workspacedir)
2002 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2003 # For the moment, we are using a real recipe.
2004 recipe = 'devtool-upgrade-test3'
2005 version = '1.6.0'
2006 oldrecipefile = get_bb_var('FILE', recipe)
2007 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2008 self.track_for_cleanup(tempdir)
2009 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
2010 # we are downgrading instead of upgrading.
2011 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
2012 # Check new recipe file is present
2013 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
2014 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
2015 # Check recipe got changed as expected
2016 with open(oldrecipefile + '.upgraded', 'r') as f:
2017 desiredlines = f.readlines()
2018 with open(newrecipefile, 'r') as f:
2019 newlines = f.readlines()
2020 self.assertEqual(desiredlines, newlines)
2021
2022 def test_devtool_upgrade_all_checksums(self):
2023 # Check preconditions
2024 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2025 self.track_for_cleanup(self.workspacedir)
2026 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2027 # For the moment, we are using a real recipe.
2028 recipe = 'devtool-upgrade-test4'
2029 version = '1.6.0'
2030 oldrecipefile = get_bb_var('FILE', recipe)
2031 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2032 self.track_for_cleanup(tempdir)
2033 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
2034 # we are downgrading instead of upgrading.
2035 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
2036 # Check new recipe file is present
2037 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
2038 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
2039 # Check recipe got changed as expected
2040 with open(oldrecipefile + '.upgraded', 'r') as f:
2041 desiredlines = f.readlines()
2042 with open(newrecipefile, 'r') as f:
2043 newlines = f.readlines()
2044 self.assertEqual(desiredlines, newlines)
2045
2046 def test_devtool_upgrade_recipe_upgrade_extra_tasks(self):
2047 # Check preconditions
2048 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2049 self.track_for_cleanup(self.workspacedir)
2050 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2051 recipe = 'python3-guessing-game'
2052 version = '0.2.0'
2053 commit = '40cf004c2772ffa20ea803fa3be1528a75be3e98'
2054 oldrecipefile = get_bb_var('FILE', recipe)
2055 oldcratesincfile = os.path.join(os.path.dirname(oldrecipefile), os.path.basename(oldrecipefile).strip('_git.bb') + '-crates.inc')
2056 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2057 self.track_for_cleanup(tempdir)
2058 # Check that recipe is not already under devtool control
2059 result = runCmd('devtool status')
2060 self.assertNotIn(recipe, result.output)
2061 # Check upgrade
2062 result = runCmd('devtool upgrade %s %s --version %s --srcrev %s' % (recipe, tempdir, version, commit))
2063 # Check if srctree at least is populated
2064 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit))
2065 # Check new recipe file and new -crates.inc files are present
2066 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile))
2067 newcratesincfile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldcratesincfile))
2068 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
2069 self.assertExists(newcratesincfile, 'Recipe crates.inc file should exist after upgrade')
2070 # Check devtool status and make sure recipe is present
2071 result = runCmd('devtool status')
2072 self.assertIn(recipe, result.output)
2073 self.assertIn(tempdir, result.output)
2074 # Check recipe got changed as expected
2075 with open(oldrecipefile + '.upgraded', 'r') as f:
2076 desiredlines = f.readlines()
2077 with open(newrecipefile, 'r') as f:
2078 newlines = f.readlines()
2079 self.assertEqual(desiredlines, newlines)
2080 # Check crates.inc got changed as expected
2081 with open(oldcratesincfile + '.upgraded', 'r') as f:
2082 desiredlines = f.readlines()
2083 with open(newcratesincfile, 'r') as f:
2084 newlines = f.readlines()
2085 self.assertEqual(desiredlines, newlines)
2086 # Check devtool reset recipe
2087 result = runCmd('devtool reset %s -n' % recipe)
2088 result = runCmd('devtool status')
2089 self.assertNotIn(recipe, result.output)
2090 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
2091
1546 def test_devtool_layer_plugins(self): 2092 def test_devtool_layer_plugins(self):
1547 """Test that devtool can use plugins from other layers. 2093 """Test that devtool can use plugins from other layers.
1548 2094
@@ -1561,7 +2107,15 @@ class DevtoolUpgradeTests(DevtoolBase):
1561 for p in paths: 2107 for p in paths:
1562 dstdir = os.path.join(dstdir, p) 2108 dstdir = os.path.join(dstdir, p)
1563 if not os.path.exists(dstdir): 2109 if not os.path.exists(dstdir):
1564 os.makedirs(dstdir) 2110 try:
2111 os.makedirs(dstdir)
2112 except PermissionError:
2113 return False
2114 except OSError as e:
2115 if e.errno == errno.EROFS:
2116 return False
2117 else:
2118 raise e
1565 if p == "lib": 2119 if p == "lib":
1566 # Can race with other tests 2120 # Can race with other tests
1567 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir) 2121 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir)
@@ -1569,8 +2123,12 @@ class DevtoolUpgradeTests(DevtoolBase):
1569 self.track_for_cleanup(dstdir) 2123 self.track_for_cleanup(dstdir)
1570 dstfile = os.path.join(dstdir, os.path.basename(srcfile)) 2124 dstfile = os.path.join(dstdir, os.path.basename(srcfile))
1571 if srcfile != dstfile: 2125 if srcfile != dstfile:
1572 shutil.copy(srcfile, dstfile) 2126 try:
2127 shutil.copy(srcfile, dstfile)
2128 except PermissionError:
2129 return False
1573 self.track_for_cleanup(dstfile) 2130 self.track_for_cleanup(dstfile)
2131 return True
1574 2132
1575 def test_devtool_load_plugin(self): 2133 def test_devtool_load_plugin(self):
1576 """Test that devtool loads only the first found plugin in BBPATH.""" 2134 """Test that devtool loads only the first found plugin in BBPATH."""
@@ -1588,15 +2146,17 @@ class DevtoolUpgradeTests(DevtoolBase):
1588 plugincontent = fh.readlines() 2146 plugincontent = fh.readlines()
1589 try: 2147 try:
1590 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found') 2148 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
1591 for path in searchpath: 2149 searchpath = [
1592 self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool') 2150 path for path in searchpath
2151 if self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
2152 ]
1593 result = runCmd("devtool --quiet count") 2153 result = runCmd("devtool --quiet count")
1594 self.assertEqual(result.output, '1') 2154 self.assertEqual(result.output, '1')
1595 result = runCmd("devtool --quiet multiloaded") 2155 result = runCmd("devtool --quiet multiloaded")
1596 self.assertEqual(result.output, "no") 2156 self.assertEqual(result.output, "no")
1597 for path in searchpath: 2157 for path in searchpath:
1598 result = runCmd("devtool --quiet bbdir") 2158 result = runCmd("devtool --quiet bbdir")
1599 self.assertEqual(result.output, path) 2159 self.assertEqual(os.path.realpath(result.output), os.path.realpath(path))
1600 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py')) 2160 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
1601 finally: 2161 finally:
1602 with open(srcfile, 'w') as fh: 2162 with open(srcfile, 'w') as fh:
@@ -1777,6 +2337,52 @@ class DevtoolUpgradeTests(DevtoolBase):
1777 if files: 2337 if files:
1778 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files)) 2338 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
1779 2339
2340 def test_devtool_finish_update_patch(self):
2341 # This test uses a modified version of the sysdig recipe from meta-oe.
2342 # - The patches have been renamed.
2343 # - The dependencies are commented out since the recipe is not being
2344 # built.
2345 #
2346 # The sysdig recipe is interesting in that it fetches two different Git
2347 # repositories, and there are patches for both. This leads to that
2348 # devtool will create ignore commits as it uses Git submodules to keep
2349 # track of the second repository.
2350 #
2351 # This test will verify that the ignored commits actually are ignored
2352 # when a commit in between is modified. It will also verify that the
2353 # updated patch keeps its original name.
2354
2355 # Check preconditions
2356 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2357 # Try modifying a recipe
2358 self.track_for_cleanup(self.workspacedir)
2359 recipe = 'sysdig-selftest'
2360 recipefile = get_bb_var('FILE', recipe)
2361 recipedir = os.path.dirname(recipefile)
2362 result = runCmd('git status --porcelain .', cwd=recipedir)
2363 if result.output.strip():
2364 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
2365 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2366 self.track_for_cleanup(tempdir)
2367 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2368 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
2369 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (recipedir, recipe, recipe, os.path.basename(recipefile)))
2370 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
2371 # Make a change to one of the existing commits
2372 result = runCmd('echo "# A comment " >> CMakeLists.txt', cwd=tempdir)
2373 result = runCmd('git status --porcelain', cwd=tempdir)
2374 self.assertIn('M CMakeLists.txt', result.output)
2375 result = runCmd('git commit --fixup HEAD^ CMakeLists.txt', cwd=tempdir)
2376 result = runCmd('git show -s --format=%s', cwd=tempdir)
2377 self.assertIn('fixup! cmake: Pass PROBE_NAME via CFLAGS', result.output)
2378 result = runCmd('GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash devtool-base', cwd=tempdir)
2379 result = runCmd('devtool finish %s meta-selftest' % recipe)
2380 result = runCmd('devtool status')
2381 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
2382 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
2383 expected_status = [(' M', '.*/0099-cmake-Pass-PROBE_NAME-via-CFLAGS.patch$')]
2384 self._check_repo_status(recipedir, expected_status)
2385
1780 def test_devtool_rename(self): 2386 def test_devtool_rename(self):
1781 # Check preconditions 2387 # Check preconditions
1782 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2388 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
@@ -1808,12 +2414,11 @@ class DevtoolUpgradeTests(DevtoolBase):
1808 newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename) 2414 newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename)
1809 self.assertExists(newsrctree, 'Source directory not renamed') 2415 self.assertExists(newsrctree, 'Source directory not renamed')
1810 checkvars = {} 2416 checkvars = {}
1811 checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever) 2417 checkvars['S'] = '${UNPACKDIR}/%s-%s' % (recipename, recipever)
1812 checkvars['SRC_URI'] = url 2418 checkvars['SRC_URI'] = url
1813 self._test_recipe_contents(newrecipefile, checkvars, []) 2419 self._test_recipe_contents(newrecipefile, checkvars, [])
1814 # Try again - change just name this time 2420 # Try again - change just name this time
1815 result = runCmd('devtool reset -n %s' % newrecipename) 2421 result = runCmd('devtool reset -n %s' % newrecipename)
1816 shutil.rmtree(newsrctree)
1817 add_recipe() 2422 add_recipe()
1818 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever)) 2423 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
1819 result = runCmd('devtool rename %s %s' % (recipename, newrecipename)) 2424 result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
@@ -1821,19 +2426,18 @@ class DevtoolUpgradeTests(DevtoolBase):
1821 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists') 2426 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists')
1822 self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed') 2427 self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed')
1823 checkvars = {} 2428 checkvars = {}
1824 checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename 2429 checkvars['S'] = '${UNPACKDIR}/%s-${PV}' % recipename
1825 checkvars['SRC_URI'] = url.replace(recipever, '${PV}') 2430 checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
1826 self._test_recipe_contents(newrecipefile, checkvars, []) 2431 self._test_recipe_contents(newrecipefile, checkvars, [])
1827 # Try again - change just version this time 2432 # Try again - change just version this time
1828 result = runCmd('devtool reset -n %s' % newrecipename) 2433 result = runCmd('devtool reset -n %s' % newrecipename)
1829 shutil.rmtree(newsrctree)
1830 add_recipe() 2434 add_recipe()
1831 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever)) 2435 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
1832 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever)) 2436 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
1833 self.assertExists(newrecipefile, 'Recipe file not renamed') 2437 self.assertExists(newrecipefile, 'Recipe file not renamed')
1834 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists') 2438 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists')
1835 checkvars = {} 2439 checkvars = {}
1836 checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever 2440 checkvars['S'] = '${UNPACKDIR}/${BPN}-%s' % recipever
1837 checkvars['SRC_URI'] = url 2441 checkvars['SRC_URI'] = url
1838 self._test_recipe_contents(newrecipefile, checkvars, []) 2442 self._test_recipe_contents(newrecipefile, checkvars, [])
1839 2443
@@ -1858,8 +2462,9 @@ class DevtoolUpgradeTests(DevtoolBase):
1858 Expected: devtool modify is able to checkout the source of the kernel 2462 Expected: devtool modify is able to checkout the source of the kernel
1859 and modification to the source and configurations are reflected 2463 and modification to the source and configurations are reflected
1860 when building the kernel. 2464 when building the kernel.
1861 """ 2465 """
1862 kernel_provider = get_bb_var('PREFERRED_PROVIDER_virtual/kernel') 2466 kernel_provider = self.td['PREFERRED_PROVIDER_virtual/kernel']
2467
1863 # Clean up the environment 2468 # Clean up the environment
1864 bitbake('%s -c clean' % kernel_provider) 2469 bitbake('%s -c clean' % kernel_provider)
1865 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2470 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
@@ -1886,33 +2491,540 @@ class DevtoolUpgradeTests(DevtoolBase):
1886 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found') 2491 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
1887 #Step 4.2 2492 #Step 4.2
1888 configfile = os.path.join(tempdir,'.config') 2493 configfile = os.path.join(tempdir,'.config')
1889 diff = runCmd('diff %s %s' % (tmpconfig, configfile)) 2494 runCmd('diff %s %s' % (tmpconfig, configfile))
1890 self.assertEqual(0,diff.status,'Kernel .config file is not the same using bitbake and devtool') 2495
1891 #Step 4.3 2496 #Step 4.3
1892 #NOTE: virtual/kernel is mapped to kernel_provider 2497 #NOTE: virtual/kernel is mapped to kernel_provider
1893 result = runCmd('devtool build %s' % kernel_provider) 2498 runCmd('devtool build %s' % kernel_provider)
1894 self.assertEqual(0,result.status,'Cannot build kernel using `devtool build`')
1895 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux') 2499 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
1896 self.assertExists(kernelfile, 'Kernel was not build correctly') 2500 self.assertExists(kernelfile, 'Kernel was not build correctly')
1897 2501
1898 #Modify the kernel source 2502 #Modify the kernel source
1899 modfile = os.path.join(tempdir,'arch/x86/boot/header.S') 2503 modfile = os.path.join(tempdir, 'init/version.c')
1900 modstring = "Use a boot loader. Devtool testing." 2504 # Moved to uts.h in 6.1 onwards
1901 modapplied = runCmd("sed -i 's/Use a boot loader./%s/' %s" % (modstring, modfile)) 2505 modfile2 = os.path.join(tempdir, 'include/linux/uts.h')
1902 self.assertEqual(0,modapplied.status,'Modification to %s on kernel source failed' % modfile) 2506 runCmd("sed -i 's/Linux/LiNuX/g' %s %s" % (modfile, modfile2))
2507
1903 #Modify the configuration 2508 #Modify the configuration
1904 codeconfigfile = os.path.join(tempdir,'.config.new') 2509 codeconfigfile = os.path.join(tempdir, '.config.new')
1905 modconfopt = "CONFIG_SG_POOL=n" 2510 modconfopt = "CONFIG_SG_POOL=n"
1906 modconf = runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile)) 2511 runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
1907 self.assertEqual(0,modconf.status,'Modification to %s failed' % codeconfigfile) 2512
1908 #Build again kernel with devtool 2513 #Build again kernel with devtool
1909 rebuild = runCmd('devtool build %s' % kernel_provider) 2514 runCmd('devtool build %s' % kernel_provider)
1910 self.assertEqual(0,rebuild.status,'Fail to build kernel after modification of source and config') 2515
1911 #Step 4.4 2516 #Step 4.4
1912 bzimagename = 'bzImage-' + get_bb_var('KERNEL_VERSION_NAME', kernel_provider) 2517 runCmd("grep '%s' %s" % ('LiNuX', kernelfile))
1913 bzimagefile = os.path.join(get_bb_var('D', kernel_provider),'boot', bzimagename) 2518
1914 checkmodcode = runCmd("grep '%s' %s" % (modstring, bzimagefile))
1915 self.assertEqual(0,checkmodcode.status,'Modification on kernel source failed')
1916 #Step 4.5 2519 #Step 4.5
1917 checkmodconfg = runCmd("grep %s %s" % (modconfopt, codeconfigfile)) 2520 runCmd("grep %s %s" % (modconfopt, codeconfigfile))
1918 self.assertEqual(0,checkmodconfg.status,'Modification to configuration file failed') 2521
2522
2523class DevtoolIdeSdkTests(DevtoolBase):
2524 def _write_bb_config(self, recipe_names):
2525 """Helper to write the bitbake local.conf file"""
2526 conf_lines = [
2527 'IMAGE_CLASSES += "image-combined-dbg"',
2528 'IMAGE_GEN_DEBUGFS = "1"',
2529 'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join(
2530 [r + '-ptest' for r in recipe_names])
2531 ]
2532 self.write_config("\n".join(conf_lines))
2533
2534 def _check_workspace(self):
2535 """Check if a workspace directory is available and setup the cleanup"""
2536 self.assertTrue(not os.path.exists(self.workspacedir),
2537 'This test cannot be run with a workspace directory under the build directory')
2538 self.track_for_cleanup(self.workspacedir)
2539 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2540
2541 def _workspace_scripts_dir(self, recipe_name):
2542 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts'))
2543
2544 def _sources_scripts_dir(self, src_dir):
2545 return os.path.realpath(os.path.join(src_dir, 'oe-scripts'))
2546
2547 def _workspace_gdbinit_dir(self, recipe_name):
2548 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts', 'gdbinit'))
2549
2550 def _sources_gdbinit_dir(self, src_dir):
2551 return os.path.realpath(os.path.join(src_dir, 'oe-gdbinit'))
2552
2553 def _devtool_ide_sdk_recipe(self, recipe_name, build_file, testimage):
2554 """Setup a recipe for working with devtool ide-sdk
2555
2556 Basically devtool modify -x followed by some tests
2557 """
2558 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2559 self.track_for_cleanup(tempdir)
2560 self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name)
2561
2562 result = runCmd('devtool modify %s -x %s --debug-build' % (recipe_name, tempdir))
2563 self.assertExists(os.path.join(tempdir, build_file),
2564 'Extracted source could not be found')
2565 self.assertExists(os.path.join(self.workspacedir, 'conf',
2566 'layer.conf'), 'Workspace directory not created')
2567 matches = glob.glob(os.path.join(self.workspacedir,
2568 'appends', recipe_name + '.bbappend'))
2569 self.assertTrue(matches, 'bbappend not created %s' % result.output)
2570
2571 # Test devtool status
2572 result = runCmd('devtool status')
2573 self.assertIn(recipe_name, result.output)
2574 self.assertIn(tempdir, result.output)
2575 self._check_src_repo(tempdir)
2576
2577 # Usually devtool ide-sdk would initiate the build of the SDK.
2578 # But there is a circular dependency with starting Qemu and passing the IP of runqemu to devtool ide-sdk.
2579 if testimage:
2580 bitbake("%s qemu-native qemu-helper-native" % testimage)
2581 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
2582 self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
2583 self.add_command_to_tearDown(
2584 'rm -f %s/%s*' % (deploy_dir_image, testimage))
2585
2586 return tempdir
2587
2588 def _get_recipe_ids(self, recipe_name):
2589 """IDs needed to write recipe specific config entries into IDE config files"""
2590 package_arch = get_bb_var('PACKAGE_ARCH', recipe_name)
2591 recipe_id = recipe_name + "-" + package_arch
2592 recipe_id_pretty = recipe_name + ": " + package_arch
2593 return (recipe_id, recipe_id_pretty)
2594
2595 def _verify_install_script_code(self, tempdir, recipe_name):
2596 """Verify the scripts referred by the tasks.json file are fine.
2597
2598 This function does not depend on Qemu. Therefore it verifies the scripts
2599 exists and the delete step works as expected. But it does not try to
2600 deploy to Qemu.
2601 """
2602 recipe_id, recipe_id_pretty = self._get_recipe_ids(recipe_name)
2603 with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j:
2604 tasks_d = json.load(tasks_j)
2605 tasks = tasks_d["tasks"]
2606 task_install = next(
2607 (task for task in tasks if task["label"] == "install && deploy-target %s" % recipe_id_pretty), None)
2608 self.assertIsNot(task_install, None)
2609 # execute only the bb_run_do_install script since the deploy would require e.g. Qemu running.
2610 i_and_d_script = "install_and_deploy_" + recipe_id
2611 i_and_d_script_path = os.path.join(
2612 self._workspace_scripts_dir(recipe_name), i_and_d_script)
2613 self.assertExists(i_and_d_script_path)
2614
2615 def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe):
2616 """Verify deployment and execution in Qemu system work for one recipe.
2617
2618 This function checks the entire SDK workflow: changing the code, recompiling
2619 it and deploying it back to Qemu, and checking that the changes have been
2620 incorporated into the provided binaries. It also runs the tests of the recipe.
2621 """
2622 recipe_id, _ = self._get_recipe_ids(recipe_name)
2623 i_and_d_script = "install_and_deploy_" + recipe_id
2624 install_deploy_cmd = os.path.join(
2625 self._workspace_scripts_dir(recipe_name), i_and_d_script)
2626 self.assertExists(install_deploy_cmd,
2627 '%s script not found' % install_deploy_cmd)
2628 runCmd(install_deploy_cmd)
2629
2630 MAGIC_STRING_ORIG = "Magic: 123456789"
2631 MAGIC_STRING_NEW = "Magic: 987654321"
2632 ptest_cmd = "ptest-runner " + recipe_name
2633
2634 # validate that SSH is working
2635 status, _ = qemu.run("uname")
2636 self.assertEqual(
2637 status, 0, msg="Failed to connect to the SSH server on Qemu")
2638
2639 # Verify the unmodified example prints the magic string
2640 status, output = qemu.run(example_exe)
2641 self.assertEqual(status, 0, msg="%s failed: %s" %
2642 (example_exe, output))
2643 self.assertIn(MAGIC_STRING_ORIG, output)
2644
2645 # Verify the unmodified ptests work
2646 status, output = qemu.run(ptest_cmd)
2647 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
2648 self.assertIn("PASS: cpp-example-lib", output)
2649
2650 # Verify remote debugging works
2651 self._gdb_cross_debugging(
2652 qemu, recipe_name, example_exe, MAGIC_STRING_ORIG)
2653
2654 # Replace the Magic String in the code, compile and deploy to Qemu
2655 cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp')
2656 with open(cpp_example_lib_hpp, 'r') as file:
2657 cpp_code = file.read()
2658 cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW)
2659 with open(cpp_example_lib_hpp, 'w') as file:
2660 file.write(cpp_code)
2661 runCmd(install_deploy_cmd, cwd=tempdir)
2662
2663 # Verify the modified example prints the modified magic string
2664 status, output = qemu.run(example_exe)
2665 self.assertEqual(status, 0, msg="%s failed: %s" %
2666 (example_exe, output))
2667 self.assertNotIn(MAGIC_STRING_ORIG, output)
2668 self.assertIn(MAGIC_STRING_NEW, output)
2669
2670 # Verify the modified example ptests work
2671 status, output = qemu.run(ptest_cmd)
2672 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
2673 self.assertIn("PASS: cpp-example-lib", output)
2674
2675 # Verify remote debugging works wit the modified magic string
2676 self._gdb_cross_debugging(
2677 qemu, recipe_name, example_exe, MAGIC_STRING_NEW)
2678
2679 def _gdb_cross(self):
2680 """Verify gdb-cross is provided by devtool ide-sdk"""
2681 target_arch = self.td["TARGET_ARCH"]
2682 target_sys = self.td["TARGET_SYS"]
2683 gdb_recipe = "gdb-cross-" + target_arch
2684 gdb_binary = target_sys + "-gdb"
2685
2686 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe)
2687 r = runCmd("%s --version" % gdb_binary,
2688 native_sysroot=native_sysroot, target_sys=target_sys)
2689 self.assertEqual(r.status, 0)
2690 self.assertIn("GNU gdb", r.output)
2691
2692 def _gdb_cross_debugging(self, qemu, recipe_name, example_exe, magic_string):
2693 """Verify gdb-cross is working
2694
2695 Test remote debugging:
2696 break main
2697 run
2698 continue
2699 break CppExample::print_json()
2700 continue
2701 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789")
2702 $1 = 0
2703 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789aaa")
2704 $2 = -3
2705 list cpp-example-lib.hpp:13,13
2706 13 inline static const std::string test_string = "cpp-example-lib Magic: 123456789";
2707 continue
2708 """
2709 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
2710 gdbserver_script = os.path.join(self._workspace_scripts_dir(
2711 recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_m')
2712 gdb_script = os.path.join(self._workspace_scripts_dir(
2713 recipe_name), 'gdb_1234_usr-bin-' + example_exe)
2714
2715 # Start a gdbserver
2716 r = runCmd(gdbserver_script)
2717 self.assertEqual(r.status, 0)
2718
2719 # Check there is a gdbserver running
2720 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
2721 self.assertEqual(r.status, 0)
2722 self.assertIn("gdbserver ", r.output)
2723
2724 # Check the pid file is correct
2725 test_cmd = "cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \
2726 example_exe + "/pid)/cmdline"
2727 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd))
2728 self.assertEqual(r.status, 0)
2729 self.assertIn("gdbserver", r.output)
2730
2731 # Test remote debugging works
2732 gdb_batch_cmd = " --batch -ex 'break main' -ex 'run'"
2733 gdb_batch_cmd += " -ex 'break CppExample::print_json()' -ex 'continue'"
2734 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %s\")'" % magic_string
2735 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %saaa\")'" % magic_string
2736 gdb_batch_cmd += " -ex 'list cpp-example-lib.hpp:13,13'"
2737 gdb_batch_cmd += " -ex 'continue'"
2738 r = runCmd(gdb_script + gdb_batch_cmd)
2739 self.logger.debug("%s %s returned: %s", gdb_script,
2740 gdb_batch_cmd, r.output)
2741 self.assertEqual(r.status, 0)
2742 self.assertIn("Breakpoint 1, main", r.output)
2743 self.assertIn("$1 = 0", r.output) # test.string.compare equal
2744 self.assertIn("$2 = -3", r.output) # test.string.compare longer
2745 self.assertIn(
2746 'inline static const std::string test_string = "cpp-example-lib %s";' % magic_string, r.output)
2747 self.assertIn("exited normally", r.output)
2748
2749 # Stop the gdbserver
2750 r = runCmd(gdbserver_script + ' stop')
2751 self.assertEqual(r.status, 0)
2752
2753 # Check there is no gdbserver running
2754 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
2755 self.assertEqual(r.status, 0)
2756 self.assertNotIn("gdbserver ", r.output)
2757
2758 def _verify_cmake_preset(self, tempdir):
2759 """Verify the generated cmake preset works as expected
2760
2761 Check if compiling works
2762 Check if unit tests can be executed in qemu (not qemu-system)
2763 """
2764 with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j:
2765 cmake_preset_d = json.load(cmake_preset_j)
2766 config_presets = cmake_preset_d["configurePresets"]
2767 self.assertEqual(len(config_presets), 1)
2768 cmake_exe = config_presets[0]["cmakeExecutable"]
2769 preset_name = config_presets[0]["name"]
2770
2771 # Verify the wrapper for cmake native is available
2772 self.assertExists(cmake_exe)
2773
2774 # Verify the cmake preset generated by devtool ide-sdk is available
2775 result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir)
2776 self.assertIn(preset_name, result.output)
2777
2778 # Verify cmake re-uses the o files compiled by bitbake
2779 result = runCmd('%s --build --preset %s' %
2780 (cmake_exe, preset_name), cwd=tempdir)
2781 self.assertIn("ninja: no work to do.", result.output)
2782
2783 # Verify the unit tests work (in Qemu user mode)
2784 result = runCmd('%s --build --preset %s --target test' %
2785 (cmake_exe, preset_name), cwd=tempdir)
2786 self.assertIn("100% tests passed", result.output)
2787
2788 # Verify re-building and testing works again
2789 result = runCmd('%s --build --preset %s --target clean' %
2790 (cmake_exe, preset_name), cwd=tempdir)
2791 self.assertIn("Cleaning", result.output)
2792 result = runCmd('%s --build --preset %s' %
2793 (cmake_exe, preset_name), cwd=tempdir)
2794 self.assertIn("Building", result.output)
2795 self.assertIn("Linking", result.output)
2796 result = runCmd('%s --build --preset %s --target test' %
2797 (cmake_exe, preset_name), cwd=tempdir)
2798 self.assertIn("Running tests...", result.output)
2799 self.assertIn("100% tests passed", result.output)
2800
2801 @OETestTag("runqemu")
2802 def test_devtool_ide_sdk_none_qemu(self):
2803 """Start qemu-system and run tests for multiple recipes. ide=none is used."""
2804 recipe_names = ["cmake-example", "meson-example"]
2805 testimage = "oe-selftest-image"
2806
2807 self._check_workspace()
2808 self._write_bb_config(recipe_names)
2809 self._check_runqemu_prerequisites()
2810
2811 # Verify deployment to Qemu (system mode) works
2812 bitbake(testimage)
2813 with runqemu(testimage, runqemuparams="nographic") as qemu:
2814 # cmake-example recipe
2815 recipe_name = "cmake-example"
2816 example_exe = "cmake-example"
2817 build_file = "CMakeLists.txt"
2818 tempdir = self._devtool_ide_sdk_recipe(
2819 recipe_name, build_file, testimage)
2820 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
2821 recipe_name, testimage, qemu.ip)
2822 runCmd(bitbake_sdk_cmd)
2823 self._gdb_cross()
2824 self._verify_cmake_preset(tempdir)
2825 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
2826 # Verify the oe-scripts sym-link is valid
2827 self.assertEqual(self._workspace_scripts_dir(
2828 recipe_name), self._sources_scripts_dir(tempdir))
2829
2830 # meson-example recipe
2831 recipe_name = "meson-example"
2832 example_exe = "mesonex"
2833 build_file = "meson.build"
2834 tempdir = self._devtool_ide_sdk_recipe(
2835 recipe_name, build_file, testimage)
2836 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
2837 recipe_name, testimage, qemu.ip)
2838 runCmd(bitbake_sdk_cmd)
2839 self._gdb_cross()
2840 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
2841 # Verify the oe-scripts sym-link is valid
2842 self.assertEqual(self._workspace_scripts_dir(
2843 recipe_name), self._sources_scripts_dir(tempdir))
2844
2845 def test_devtool_ide_sdk_code_cmake(self):
2846 """Verify a cmake recipe works with ide=code mode"""
2847 recipe_name = "cmake-example"
2848 build_file = "CMakeLists.txt"
2849 testimage = "oe-selftest-image"
2850
2851 self._check_workspace()
2852 self._write_bb_config([recipe_name])
2853 tempdir = self._devtool_ide_sdk_recipe(
2854 recipe_name, build_file, testimage)
2855 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
2856 recipe_name, testimage)
2857 runCmd(bitbake_sdk_cmd)
2858 self._verify_cmake_preset(tempdir)
2859 self._verify_install_script_code(tempdir, recipe_name)
2860 self._gdb_cross()
2861
2862 def test_devtool_ide_sdk_code_meson(self):
2863 """Verify a meson recipe works with ide=code mode"""
2864 recipe_name = "meson-example"
2865 build_file = "meson.build"
2866 testimage = "oe-selftest-image"
2867
2868 self._check_workspace()
2869 self._write_bb_config([recipe_name])
2870 tempdir = self._devtool_ide_sdk_recipe(
2871 recipe_name, build_file, testimage)
2872 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
2873 recipe_name, testimage)
2874 runCmd(bitbake_sdk_cmd)
2875
2876 with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j:
2877 settings_d = json.load(settings_j)
2878 meson_exe = settings_d["mesonbuild.mesonPath"]
2879 meson_build_folder = settings_d["mesonbuild.buildFolder"]
2880
2881 # Verify the wrapper for meson native is available
2882 self.assertExists(meson_exe)
2883
2884 # Verify meson re-uses the o files compiled by bitbake
2885 result = runCmd('%s compile -C %s' %
2886 (meson_exe, meson_build_folder), cwd=tempdir)
2887 self.assertIn("ninja: no work to do.", result.output)
2888
2889 # Verify the unit tests work (in Qemu)
2890 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
2891
2892 # Verify re-building and testing works again
2893 result = runCmd('%s compile -C %s --clean' %
2894 (meson_exe, meson_build_folder), cwd=tempdir)
2895 self.assertIn("Cleaning...", result.output)
2896 result = runCmd('%s compile -C %s' %
2897 (meson_exe, meson_build_folder), cwd=tempdir)
2898 self.assertIn("Linking target", result.output)
2899 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
2900
2901 self._verify_install_script_code(tempdir, recipe_name)
2902 self._gdb_cross()
2903
2904 def test_devtool_ide_sdk_shared_sysroots(self):
2905 """Verify the shared sysroot SDK"""
2906
2907 # Handle the workspace (which is not needed by this test case)
2908 self._check_workspace()
2909
2910 result_init = runCmd(
2911 'devtool ide-sdk -m shared oe-selftest-image cmake-example meson-example --ide=code')
2912 bb_vars = get_bb_vars(
2913 ['REAL_MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE'], "meta-ide-support")
2914 environment_script = 'environment-setup-%s' % bb_vars['REAL_MULTIMACH_TARGET_SYS']
2915 deploydir = bb_vars['DEPLOY_DIR_IMAGE']
2916 environment_script_path = os.path.join(deploydir, environment_script)
2917 cpp_example_src = os.path.join(
2918 bb_vars['COREBASE'], 'meta-selftest', 'recipes-test', 'cpp', 'files')
2919
2920 # Verify the cross environment script is available
2921 self.assertExists(environment_script_path)
2922
2923 def runCmdEnv(cmd, cwd):
2924 cmd = '/bin/sh -c ". %s > /dev/null && %s"' % (
2925 environment_script_path, cmd)
2926 return runCmd(cmd, cwd)
2927
2928 # Verify building the C++ example works with CMake
2929 tempdir_cmake = tempfile.mkdtemp(prefix='devtoolqa')
2930 self.track_for_cleanup(tempdir_cmake)
2931
2932 result_cmake = runCmdEnv("which cmake", cwd=tempdir_cmake)
2933 cmake_native = os.path.normpath(result_cmake.output.strip())
2934 self.assertExists(cmake_native)
2935
2936 runCmdEnv('cmake %s' % cpp_example_src, cwd=tempdir_cmake)
2937 runCmdEnv('cmake --build %s' % tempdir_cmake, cwd=tempdir_cmake)
2938
2939 # Verify the printed note really referres to a cmake executable
2940 cmake_native_code = ""
2941 for line in result_init.output.splitlines():
2942 m = re.search(r'"cmake.cmakePath": "(.*)"', line)
2943 if m:
2944 cmake_native_code = m.group(1)
2945 break
2946 self.assertExists(cmake_native_code)
2947 self.assertEqual(cmake_native, cmake_native_code)
2948
2949 # Verify building the C++ example works with Meson
2950 tempdir_meson = tempfile.mkdtemp(prefix='devtoolqa')
2951 self.track_for_cleanup(tempdir_meson)
2952
2953 result_cmake = runCmdEnv("which meson", cwd=tempdir_meson)
2954 meson_native = os.path.normpath(result_cmake.output.strip())
2955 self.assertExists(meson_native)
2956
2957 runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src)
2958 runCmdEnv('meson compile', cwd=tempdir_meson)
2959
2960 def test_devtool_ide_sdk_plugins(self):
2961 """Test that devtool ide-sdk can use plugins from other layers."""
2962
2963 # We need a workspace layer and a modified recipe (but no image)
2964 modified_recipe_name = "meson-example"
2965 modified_build_file = "meson.build"
2966 testimage = "oe-selftest-image"
2967 shared_recipe_name = "cmake-example"
2968
2969 self._check_workspace()
2970 self._write_bb_config([modified_recipe_name])
2971 tempdir = self._devtool_ide_sdk_recipe(
2972 modified_recipe_name, modified_build_file, None)
2973
2974 IDE_RE = re.compile(r'.*--ide \{(.*)\}.*')
2975
2976 def get_ides_from_help(help_str):
2977 m = IDE_RE.search(help_str)
2978 return m.group(1).split(',')
2979
2980 # verify the default plugins are available but the foo plugin is not
2981 result = runCmd('devtool ide-sdk -h')
2982 found_ides = get_ides_from_help(result.output)
2983 self.assertIn('code', found_ides)
2984 self.assertIn('none', found_ides)
2985 self.assertNotIn('foo', found_ides)
2986
2987 shared_config_file = os.path.join(tempdir, 'shared-config.txt')
2988 shared_config_str = 'Dummy shared IDE config'
2989 modified_config_file = os.path.join(tempdir, 'modified-config.txt')
2990 modified_config_str = 'Dummy modified IDE config'
2991
2992 # Generate a foo plugin in the workspace layer
2993 plugin_dir = os.path.join(
2994 self.workspacedir, 'lib', 'devtool', 'ide_plugins')
2995 os.makedirs(plugin_dir)
2996 plugin_code = 'from devtool.ide_plugins import IdeBase\n\n'
2997 plugin_code += 'class IdeFoo(IdeBase):\n'
2998 plugin_code += ' def setup_shared_sysroots(self, shared_env):\n'
2999 plugin_code += ' with open("%s", "w") as config_file:\n' % shared_config_file
3000 plugin_code += ' config_file.write("%s")\n\n' % shared_config_str
3001 plugin_code += ' def setup_modified_recipe(self, args, image_recipe, modified_recipe):\n'
3002 plugin_code += ' with open("%s", "w") as config_file:\n' % modified_config_file
3003 plugin_code += ' config_file.write("%s")\n\n' % modified_config_str
3004 plugin_code += 'def register_ide_plugin(ide_plugins):\n'
3005 plugin_code += ' ide_plugins["foo"] = IdeFoo\n'
3006
3007 plugin_py = os.path.join(plugin_dir, 'ide_foo.py')
3008 with open(plugin_py, 'w') as plugin_file:
3009 plugin_file.write(plugin_code)
3010
3011 # Verify the foo plugin is available as well
3012 result = runCmd('devtool ide-sdk -h')
3013 found_ides = get_ides_from_help(result.output)
3014 self.assertIn('code', found_ides)
3015 self.assertIn('none', found_ides)
3016 self.assertIn('foo', found_ides)
3017
3018 # Verify the foo plugin generates a shared config
3019 result = runCmd(
3020 'devtool ide-sdk -m shared --skip-bitbake --ide foo %s' % shared_recipe_name)
3021 with open(shared_config_file) as shared_config:
3022 shared_config_new = shared_config.read()
3023 self.assertEqual(shared_config_str, shared_config_new)
3024
3025 # Verify the foo plugin generates a modified config
3026 result = runCmd('devtool ide-sdk --skip-bitbake --ide foo %s %s' %
3027 (modified_recipe_name, testimage))
3028 with open(modified_config_file) as modified_config:
3029 modified_config_new = modified_config.read()
3030 self.assertEqual(modified_config_str, modified_config_new)