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.py1260
1 files changed, 1146 insertions, 114 deletions
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 3385546e8e..882225dde3 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
@@ -80,32 +95,15 @@ def tearDownModule():
80 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb) 95 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
81 shutil.rmtree(templayerdir) 96 shutil.rmtree(templayerdir)
82 97
83class DevtoolBase(OESelftestTestCase): 98class 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 99
101 def setUp(self): 100 def setUp(self):
102 """Test case setup function""" 101 """Test case setup function"""
103 super(DevtoolBase, self).setUp() 102 super(DevtoolTestCase, self).setUp()
104 self.workspacedir = os.path.join(self.builddir, 'workspace') 103 self.workspacedir = os.path.join(self.builddir, 'workspace')
105 self.assertTrue(not os.path.exists(self.workspacedir), 104 self.assertTrue(not os.path.exists(self.workspacedir),
106 'This test cannot be run with a workspace directory ' 105 'This test cannot be run with a workspace directory '
107 'under the build directory') 106 'under the build directory')
108 self.append_config(self.sstate_conf)
109 107
110 def _check_src_repo(self, repo_dir): 108 def _check_src_repo(self, repo_dir):
111 """Check srctree git repository""" 109 """Check srctree git repository"""
@@ -235,6 +233,103 @@ class DevtoolBase(OESelftestTestCase):
235 filelist.append(' '.join(splitline)) 233 filelist.append(' '.join(splitline))
236 return filelist 234 return filelist
237 235
236 def _check_diff(self, diffoutput, addlines, removelines):
237 """Check output from 'git diff' matches expectation"""
238 remaining_addlines = addlines[:]
239 remaining_removelines = removelines[:]
240 for line in diffoutput.splitlines():
241 if line.startswith('+++') or line.startswith('---'):
242 continue
243 elif line.startswith('+'):
244 matched = False
245 for item in addlines:
246 if re.match(item, line[1:].strip()):
247 matched = True
248 remaining_addlines.remove(item)
249 break
250 self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
251 elif line.startswith('-'):
252 matched = False
253 for item in removelines:
254 if re.match(item, line[1:].strip()):
255 matched = True
256 remaining_removelines.remove(item)
257 break
258 self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
259 if remaining_addlines:
260 self.fail('Expected added lines not found: %s' % remaining_addlines)
261 if remaining_removelines:
262 self.fail('Expected removed lines not found: %s' % remaining_removelines)
263
264 def _check_runqemu_prerequisites(self):
265 """Check runqemu is available
266
267 Whilst some tests would seemingly be better placed as a runtime test,
268 unfortunately the runtime tests run under bitbake and you can't run
269 devtool within bitbake (since devtool needs to run bitbake itself).
270 Additionally we are testing build-time functionality as well, so
271 really this has to be done as an oe-selftest test.
272 """
273 machine = get_bb_var('MACHINE')
274 if not machine.startswith('qemu'):
275 self.skipTest('This test only works with qemu machines')
276 if not os.path.exists('/etc/runqemu-nosudo'):
277 self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
278 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True)
279 if result.status != 0:
280 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True)
281 if result.status != 0:
282 self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output)
283 for line in result.output.splitlines():
284 if line.startswith('tap'):
285 break
286 else:
287 self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
288
289 def _test_devtool_add_git_url(self, git_url, version, pn, resulting_src_uri, srcrev=None):
290 self.track_for_cleanup(self.workspacedir)
291 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
292 command = 'devtool add --version %s %s %s' % (version, pn, git_url)
293 if srcrev :
294 command += ' --srcrev %s' %srcrev
295 result = runCmd(command)
296 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
297 # Check the recipe name is correct
298 recipefile = get_bb_var('FILE', pn)
299 self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
300 self.assertIn(recipefile, result.output)
301 # Test devtool status
302 result = runCmd('devtool status')
303 self.assertIn(pn, result.output)
304 self.assertIn(recipefile, result.output)
305 checkvars = {}
306 checkvars['SRC_URI'] = resulting_src_uri
307 self._test_recipe_contents(recipefile, checkvars, [])
308
309class DevtoolBase(DevtoolTestCase):
310
311 @classmethod
312 def setUpClass(cls):
313 super(DevtoolBase, cls).setUpClass()
314 bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
315 cls.original_sstate = bb_vars['SSTATE_DIR']
316 cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
317 cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
318 cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
319 % cls.original_sstate)
320 cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687"\n')
321
322 @classmethod
323 def tearDownClass(cls):
324 cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
325 runCmd('rm -rf %s' % cls.devtool_sstate)
326 super(DevtoolBase, cls).tearDownClass()
327
328 def setUp(self):
329 """Test case setup function"""
330 super(DevtoolBase, self).setUp()
331 self.append_config(self.sstate_conf)
332
238 333
239class DevtoolTests(DevtoolBase): 334class DevtoolTests(DevtoolBase):
240 335
@@ -304,6 +399,38 @@ class DevtoolAddTests(DevtoolBase):
304 bindir = bindir[1:] 399 bindir = bindir[1:]
305 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D') 400 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
306 401
402 def test_devtool_add_binary(self):
403 # Create a binary package containing a known test file
404 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
405 self.track_for_cleanup(tempdir)
406 pn = 'tst-bin'
407 pv = '1.0'
408 test_file_dir = "var/lib/%s/" % pn
409 test_file_name = "test_file"
410 test_file_content = "TEST CONTENT"
411 test_file_package_root = os.path.join(tempdir, pn)
412 test_file_dir_full = os.path.join(test_file_package_root, test_file_dir)
413 bb.utils.mkdirhier(test_file_dir_full)
414 with open(os.path.join(test_file_dir_full, test_file_name), "w") as f:
415 f.write(test_file_content)
416 bin_package_path = os.path.join(tempdir, "%s.tar.gz" % pn)
417 runCmd("tar czf %s -C %s ." % (bin_package_path, test_file_package_root))
418
419 # Test devtool add -b on the binary package
420 self.track_for_cleanup(self.workspacedir)
421 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
422 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
423 result = runCmd('devtool add -b %s %s' % (pn, bin_package_path))
424 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
425
426 # Build the resulting recipe
427 result = runCmd('devtool build %s' % pn)
428 installdir = get_bb_var('D', pn)
429 self.assertTrue(installdir, 'Could not query installdir variable')
430
431 # Check that a known file from the binary package has indeed been installed
432 self.assertTrue(os.path.isfile(os.path.join(installdir, test_file_dir, test_file_name)), '%s not found in D' % test_file_name)
433
307 def test_devtool_add_git_local(self): 434 def test_devtool_add_git_local(self):
308 # We need dbus built so that DEPENDS recognition works 435 # We need dbus built so that DEPENDS recognition works
309 bitbake('dbus') 436 bitbake('dbus')
@@ -336,15 +463,32 @@ class DevtoolAddTests(DevtoolBase):
336 self.assertIn(srcdir, result.output) 463 self.assertIn(srcdir, result.output)
337 self.assertIn(recipefile, result.output) 464 self.assertIn(recipefile, result.output)
338 checkvars = {} 465 checkvars = {}
339 checkvars['LICENSE'] = 'GPLv2' 466 checkvars['LICENSE'] = 'GPL-2.0-only'
340 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' 467 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
341 checkvars['S'] = '${WORKDIR}/git' 468 checkvars['S'] = '${WORKDIR}/git'
342 checkvars['PV'] = '0.1+git${SRCPV}' 469 checkvars['PV'] = '0.1+git'
343 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https' 470 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https;branch=master'
344 checkvars['SRCREV'] = srcrev 471 checkvars['SRCREV'] = srcrev
345 checkvars['DEPENDS'] = set(['dbus']) 472 checkvars['DEPENDS'] = set(['dbus'])
346 self._test_recipe_contents(recipefile, checkvars, []) 473 self._test_recipe_contents(recipefile, checkvars, [])
347 474
475 def test_devtool_add_git_style1(self):
476 version = 'v3.1.0'
477 pn = 'mbedtls'
478 # this will trigger reformat_git_uri with branch parameter in url
479 git_url = "'git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https'"
480 resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https"
481 self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri)
482
483 def test_devtool_add_git_style2(self):
484 version = 'v3.1.0'
485 srcrev = 'v3.1.0'
486 pn = 'mbedtls'
487 # this will trigger reformat_git_uri with branch parameter in url
488 git_url = "'git://git@github.com/ARMmbed/mbedtls.git;protocol=https'"
489 resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;protocol=https;branch=master"
490 self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri, srcrev)
491
348 def test_devtool_add_library(self): 492 def test_devtool_add_library(self):
349 # Fetch source 493 # Fetch source
350 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 494 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
@@ -373,7 +517,7 @@ class DevtoolAddTests(DevtoolBase):
373 recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version) 517 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) 518 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: 519 with open(recipefile, 'a') as f:
376 f.write('\nFILES_${PN}-dev += "${datadir}/cmake/Modules"\n') 520 f.write('\nFILES:${PN}-dev += "${datadir}/cmake/Modules"\n')
377 # We don't have the ability to pick up this dependency automatically yet... 521 # We don't have the ability to pick up this dependency automatically yet...
378 f.write('\nDEPENDS += "libusb1"\n') 522 f.write('\nDEPENDS += "libusb1"\n')
379 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n') 523 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
@@ -405,7 +549,7 @@ class DevtoolAddTests(DevtoolBase):
405 self.track_for_cleanup(self.workspacedir) 549 self.track_for_cleanup(self.workspacedir)
406 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe) 550 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
407 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 551 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
408 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url)) 552 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) 553 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') 554 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') 555 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
@@ -424,7 +568,7 @@ class DevtoolAddTests(DevtoolBase):
424 result = runCmd('devtool reset -n %s' % testrecipe) 568 result = runCmd('devtool reset -n %s' % testrecipe)
425 shutil.rmtree(srcdir) 569 shutil.rmtree(srcdir)
426 fakever = '1.9' 570 fakever = '1.9'
427 result = runCmd('devtool add %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever)) 571 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') 572 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
429 # Test devtool status 573 # Test devtool status
430 result = runCmd('devtool status') 574 result = runCmd('devtool status')
@@ -442,6 +586,7 @@ class DevtoolAddTests(DevtoolBase):
442 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 586 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
443 self.track_for_cleanup(tempdir) 587 self.track_for_cleanup(tempdir)
444 url = 'gitsm://git.yoctoproject.org/mraa' 588 url = 'gitsm://git.yoctoproject.org/mraa'
589 url_branch = '%s;branch=master' % url
445 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d' 590 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
446 testrecipe = 'mraa' 591 testrecipe = 'mraa'
447 srcdir = os.path.join(tempdir, testrecipe) 592 srcdir = os.path.join(tempdir, testrecipe)
@@ -461,8 +606,8 @@ class DevtoolAddTests(DevtoolBase):
461 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') 606 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
462 checkvars = {} 607 checkvars = {}
463 checkvars['S'] = '${WORKDIR}/git' 608 checkvars['S'] = '${WORKDIR}/git'
464 checkvars['PV'] = '1.0+git${SRCPV}' 609 checkvars['PV'] = '1.0+git'
465 checkvars['SRC_URI'] = url 610 checkvars['SRC_URI'] = url_branch
466 checkvars['SRCREV'] = '${AUTOREV}' 611 checkvars['SRCREV'] = '${AUTOREV}'
467 self._test_recipe_contents(recipefile, checkvars, []) 612 self._test_recipe_contents(recipefile, checkvars, [])
468 # Try with revision and version specified 613 # Try with revision and version specified
@@ -480,8 +625,8 @@ class DevtoolAddTests(DevtoolBase):
480 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') 625 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
481 checkvars = {} 626 checkvars = {}
482 checkvars['S'] = '${WORKDIR}/git' 627 checkvars['S'] = '${WORKDIR}/git'
483 checkvars['PV'] = '1.5+git${SRCPV}' 628 checkvars['PV'] = '1.5+git'
484 checkvars['SRC_URI'] = url 629 checkvars['SRC_URI'] = url_branch
485 checkvars['SRCREV'] = checkrev 630 checkvars['SRCREV'] = checkrev
486 self._test_recipe_contents(recipefile, checkvars, []) 631 self._test_recipe_contents(recipefile, checkvars, [])
487 632
@@ -504,7 +649,7 @@ class DevtoolAddTests(DevtoolBase):
504 result = runCmd('devtool status') 649 result = runCmd('devtool status')
505 self.assertIn(testrecipe, result.output) 650 self.assertIn(testrecipe, result.output)
506 self.assertIn(srcdir, result.output) 651 self.assertIn(srcdir, result.output)
507 # Check recipe 652 # Check recipedevtool add
508 recipefile = get_bb_var('FILE', testrecipe) 653 recipefile = get_bb_var('FILE', testrecipe)
509 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named') 654 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
510 checkvars = {} 655 checkvars = {}
@@ -536,6 +681,19 @@ class DevtoolAddTests(DevtoolBase):
536 # Test devtool build 681 # Test devtool build
537 result = runCmd('devtool build %s' % pn) 682 result = runCmd('devtool build %s' % pn)
538 683
684 def test_devtool_add_python_egg_requires(self):
685 # Fetch source
686 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
687 self.track_for_cleanup(tempdir)
688 testver = '0.14.0'
689 url = 'https://files.pythonhosted.org/packages/e9/9e/25d59f5043cf763833b2581c8027fa92342c4cf8ee523b498ecdf460c16d/uvicorn-%s.tar.gz' % testver
690 testrecipe = 'python3-uvicorn'
691 srcdir = os.path.join(tempdir, testrecipe)
692 # Test devtool add
693 self.track_for_cleanup(self.workspacedir)
694 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
695 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
696
539class DevtoolModifyTests(DevtoolBase): 697class DevtoolModifyTests(DevtoolBase):
540 698
541 def test_devtool_modify(self): 699 def test_devtool_modify(self):
@@ -649,7 +807,7 @@ class DevtoolModifyTests(DevtoolBase):
649 self.track_for_cleanup(self.workspacedir) 807 self.track_for_cleanup(self.workspacedir)
650 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 808 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
651 809
652 testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split() 810 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 811 # 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*') 812 result = runCmd('bitbake-layers show-recipes gcc-source*')
655 for line in result.output.splitlines(): 813 for line in result.output.splitlines():
@@ -697,6 +855,7 @@ class DevtoolModifyTests(DevtoolBase):
697 855
698 self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes)) 856 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)) 857 self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
858
700 def test_devtool_modify_localfiles_only(self): 859 def test_devtool_modify_localfiles_only(self):
701 # Check preconditions 860 # Check preconditions
702 testrecipe = 'base-files' 861 testrecipe = 'base-files'
@@ -763,6 +922,122 @@ class DevtoolModifyTests(DevtoolBase):
763 # Try building 922 # Try building
764 bitbake(testrecipe) 923 bitbake(testrecipe)
765 924
925 def test_devtool_modify_git_no_extract(self):
926 # Check preconditions
927 testrecipe = 'psplash'
928 src_uri = get_bb_var('SRC_URI', testrecipe)
929 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
930 # Clean up anything in the workdir/sysroot/sstate cache
931 bitbake('%s -c cleansstate' % testrecipe)
932 # Try modifying a recipe
933 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
934 self.track_for_cleanup(tempdir)
935 self.track_for_cleanup(self.workspacedir)
936 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
937 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
938 result = runCmd('git clone https://git.yoctoproject.org/psplash %s && devtool modify -n %s %s' % (tempdir, testrecipe, tempdir))
939 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
940 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend'))
941 self.assertTrue(matches, 'bbappend not created')
942 # Test devtool status
943 result = runCmd('devtool status')
944 self.assertIn(testrecipe, result.output)
945 self.assertIn(tempdir, result.output)
946
947 def test_devtool_modify_git_crates_subpath(self):
948 # This tests two things in devtool context:
949 # - that we support local git dependencies for cargo based recipe
950 # - that we support patches in SRC_URI when git url contains subpath parameter
951
952 # Check preconditions:
953 # recipe inherits cargo
954 # git:// uri with a subpath as the main package
955 # some crate:// in SRC_URI
956 # others git:// in SRC_URI
957 # cointains a patch
958 testrecipe = 'hello-rs'
959 bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'WORKDIR', 'CARGO_HOME'], testrecipe)
960 recipefile = bb_vars['FILE']
961 workdir = bb_vars['WORKDIR']
962 cargo_home = bb_vars['CARGO_HOME']
963 src_uri = bb_vars['SRC_URI'].split()
964 self.assertTrue(src_uri[0].startswith('git://'),
965 'This test expects the %s recipe to have a git repo has its main uri' % testrecipe)
966 self.assertIn(';subpath=', src_uri[0],
967 'This test expects the %s recipe to have a git uri with subpath' % testrecipe)
968 self.assertTrue(any([uri.startswith('crate://') for uri in src_uri]),
969 'This test expects the %s recipe to have some crates in its src uris' % testrecipe)
970 self.assertGreaterEqual(sum(map(lambda x:x.startswith('git://'), src_uri)), 2,
971 'This test expects the %s recipe to have several git:// uris' % testrecipe)
972 self.assertTrue(any([uri.startswith('file://') and '.patch' in uri for uri in src_uri]),
973 'This test expects the %s recipe to have a patch in its src uris' % testrecipe)
974
975 self._test_recipe_contents(recipefile, {}, ['ptest-cargo'])
976
977 # Clean up anything in the workdir/sysroot/sstate cache
978 bitbake('%s -c cleansstate' % testrecipe)
979 # Try modifying a recipe
980 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
981 self.track_for_cleanup(tempdir)
982 self.track_for_cleanup(self.workspacedir)
983 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
984 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
985 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
986 self.assertExists(os.path.join(tempdir, 'Cargo.toml'), 'Extracted source could not be found')
987 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
988 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
989 self.assertTrue(matches, 'bbappend not created')
990 # Test devtool status
991 result = runCmd('devtool status')
992 self.assertIn(testrecipe, result.output)
993 self.assertIn(tempdir, result.output)
994 # Check git repo
995 self._check_src_repo(tempdir)
996 # Check that the patch is correctly applied.
997 # The last commit message in the tree must contain the following note:
998 # Notes (devtool):
999 # original patch: <patchname>
1000 # ..
1001 patchname = None
1002 for uri in src_uri:
1003 if uri.startswith('file://') and '.patch' in uri:
1004 patchname = uri.replace("file://", "").partition('.patch')[0] + '.patch'
1005 self.assertIsNotNone(patchname)
1006 result = runCmd('git -C %s log -1' % tempdir)
1007 self.assertIn("Notes (devtool):\n original patch: %s" % patchname, result.output)
1008
1009 # Configure the recipe to check that the git dependencies are correctly patched in cargo config
1010 bitbake('-c configure %s' % testrecipe)
1011
1012 cargo_config_path = os.path.join(cargo_home, 'config')
1013 with open(cargo_config_path, "r") as f:
1014 cargo_config_contents = [line.strip('\n') for line in f.readlines()]
1015
1016 # Get back git dependencies of the recipe (ignoring the main one)
1017 # and check that they are all correctly patched to be fetched locally
1018 git_deps = [uri for uri in src_uri if uri.startswith("git://")][1:]
1019 for git_dep in git_deps:
1020 raw_url, _, raw_parms = git_dep.partition(";")
1021 parms = {}
1022 for parm in raw_parms.split(";"):
1023 name_parm, _, value_parm = parm.partition('=')
1024 parms[name_parm]=value_parm
1025 self.assertIn('protocol', parms, 'git dependencies uri should contain the "protocol" parameter')
1026 self.assertIn('name', parms, 'git dependencies uri should contain the "name" parameter')
1027 self.assertIn('destsuffix', parms, 'git dependencies uri should contain the "destsuffix" parameter')
1028 self.assertIn('type', parms, 'git dependencies uri should contain the "type" parameter')
1029 self.assertEqual(parms['type'], 'git-dependency', 'git dependencies uri should have "type=git-dependency"')
1030 raw_url = raw_url.replace("git://", '%s://' % parms['protocol'])
1031 patch_line = '[patch."%s"]' % raw_url
1032 path_patched = os.path.join(workdir, parms['destsuffix'])
1033 path_override_line = '%s = { path = "%s" }' % (parms['name'], path_patched)
1034 # Would have been better to use tomllib to read this file :/
1035 self.assertIn(patch_line, cargo_config_contents)
1036 self.assertIn(path_override_line, cargo_config_contents)
1037
1038 # Try to package the recipe
1039 bitbake('-c package_qa %s' % testrecipe)
1040
766 def test_devtool_modify_localfiles(self): 1041 def test_devtool_modify_localfiles(self):
767 # Check preconditions 1042 # Check preconditions
768 testrecipe = 'lighttpd' 1043 testrecipe = 'lighttpd'
@@ -828,12 +1103,43 @@ class DevtoolModifyTests(DevtoolBase):
828 runCmd('git -C %s checkout %s' % (tempdir, branch)) 1103 runCmd('git -C %s checkout %s' % (tempdir, branch))
829 with open(source, "rt") as f: 1104 with open(source, "rt") as f:
830 content = f.read() 1105 content = f.read()
831 self.assertEquals(content, expected) 1106 self.assertEqual(content, expected)
832 check('devtool', 'This is a test for something\n') 1107 if self.td["MACHINE"] == "qemux86":
1108 check('devtool', 'This is a test for qemux86\n')
1109 elif self.td["MACHINE"] == "qemuarm":
1110 check('devtool', 'This is a test for qemuarm\n')
1111 else:
1112 check('devtool', 'This is a test for something\n')
833 check('devtool-no-overrides', 'This is a test for something\n') 1113 check('devtool-no-overrides', 'This is a test for something\n')
834 check('devtool-override-qemuarm', 'This is a test for qemuarm\n') 1114 check('devtool-override-qemuarm', 'This is a test for qemuarm\n')
835 check('devtool-override-qemux86', 'This is a test for qemux86\n') 1115 check('devtool-override-qemux86', 'This is a test for qemux86\n')
836 1116
1117 def test_devtool_modify_multiple_sources(self):
1118 # This test check that recipes fetching several sources can be used with devtool modify/build
1119 # Check preconditions
1120 testrecipe = 'bzip2'
1121 src_uri = get_bb_var('SRC_URI', testrecipe)
1122 src1 = 'https://' in src_uri
1123 src2 = 'git://' in src_uri
1124 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)
1125 # Clean up anything in the workdir/sysroot/sstate cache
1126 bitbake('%s -c cleansstate' % testrecipe)
1127 # Try modifying a recipe
1128 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1129 self.track_for_cleanup(tempdir)
1130 self.track_for_cleanup(self.workspacedir)
1131 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1132 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1133 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1134 self.assertEqual(result.status, 0, "Could not modify recipe %s. Output: %s" % (testrecipe, result.output))
1135 # Test devtool status
1136 result = runCmd('devtool status')
1137 self.assertIn(testrecipe, result.output)
1138 self.assertIn(tempdir, result.output)
1139 # Try building
1140 result = bitbake(testrecipe)
1141 self.assertEqual(result.status, 0, "Bitbake failed, exit code %s, output %s" % (result.status, result.output))
1142
837class DevtoolUpdateTests(DevtoolBase): 1143class DevtoolUpdateTests(DevtoolBase):
838 1144
839 def test_devtool_update_recipe(self): 1145 def test_devtool_update_recipe(self):
@@ -863,14 +1169,15 @@ class DevtoolUpdateTests(DevtoolBase):
863 result = runCmd('git commit -m "Add a new file"', cwd=tempdir) 1169 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))) 1170 self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
865 result = runCmd('devtool update-recipe %s' % testrecipe) 1171 result = runCmd('devtool update-recipe %s' % testrecipe)
1172 result = runCmd('git add minicom', cwd=os.path.dirname(recipefile))
866 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1173 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
867 ('??', '.*/0001-Change-the-README.patch$'), 1174 ('A ', '.*/0001-Change-the-README.patch$'),
868 ('??', '.*/0002-Add-a-new-file.patch$')] 1175 ('A ', '.*/0002-Add-a-new-file.patch$')]
869 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1176 self._check_repo_status(os.path.dirname(recipefile), expected_status)
870 1177
871 def test_devtool_update_recipe_git(self): 1178 def test_devtool_update_recipe_git(self):
872 # Check preconditions 1179 # Check preconditions
873 testrecipe = 'mtd-utils' 1180 testrecipe = 'mtd-utils-selftest'
874 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1181 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
875 recipefile = bb_vars['FILE'] 1182 recipefile = bb_vars['FILE']
876 src_uri = bb_vars['SRC_URI'] 1183 src_uri = bb_vars['SRC_URI']
@@ -904,28 +1211,12 @@ class DevtoolUpdateTests(DevtoolBase):
904 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1211 self._check_repo_status(os.path.dirname(recipefile), expected_status)
905 1212
906 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile)) 1213 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"'] 1214 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git;branch=master"']
908 srcurilines = src_uri.split() 1215 srcurilines = src_uri.split()
909 srcurilines[0] = 'SRC_URI = "' + srcurilines[0] 1216 srcurilines[0] = 'SRC_URI = "' + srcurilines[0]
910 srcurilines.append('"') 1217 srcurilines.append('"')
911 removelines = ['SRCREV = ".*"'] + srcurilines 1218 removelines = ['SRCREV = ".*"'] + srcurilines
912 for line in result.output.splitlines(): 1219 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 1220 # Now try with auto mode
930 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile))) 1221 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
931 result = runCmd('devtool update-recipe %s' % testrecipe) 1222 result = runCmd('devtool update-recipe %s' % testrecipe)
@@ -975,7 +1266,7 @@ class DevtoolUpdateTests(DevtoolBase):
975 self.assertExists(patchfile, 'Patch file not created') 1266 self.assertExists(patchfile, 'Patch file not created')
976 1267
977 # Check bbappend contents 1268 # Check bbappend contents
978 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n', 1269 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
979 '\n', 1270 '\n',
980 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n', 1271 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n',
981 '\n'] 1272 '\n']
@@ -990,7 +1281,7 @@ class DevtoolUpdateTests(DevtoolBase):
990 result = runCmd('git reset HEAD^', cwd=tempsrcdir) 1281 result = runCmd('git reset HEAD^', cwd=tempsrcdir)
991 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1282 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
992 self.assertNotExists(patchfile, 'Patch file not deleted') 1283 self.assertNotExists(patchfile, 'Patch file not deleted')
993 expectedlines2 = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n', 1284 expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
994 '\n'] 1285 '\n']
995 with open(bbappendfile, 'r') as f: 1286 with open(bbappendfile, 'r') as f:
996 self.assertEqual(expectedlines2, f.readlines()) 1287 self.assertEqual(expectedlines2, f.readlines())
@@ -1007,10 +1298,11 @@ class DevtoolUpdateTests(DevtoolBase):
1007 1298
1008 def test_devtool_update_recipe_append_git(self): 1299 def test_devtool_update_recipe_append_git(self):
1009 # Check preconditions 1300 # Check preconditions
1010 testrecipe = 'mtd-utils' 1301 testrecipe = 'mtd-utils-selftest'
1011 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1302 bb_vars = get_bb_vars(['FILE', 'SRC_URI', 'LAYERSERIES_CORENAMES'], testrecipe)
1012 recipefile = bb_vars['FILE'] 1303 recipefile = bb_vars['FILE']
1013 src_uri = bb_vars['SRC_URI'] 1304 src_uri = bb_vars['SRC_URI']
1305 corenames = bb_vars['LAYERSERIES_CORENAMES']
1014 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe) 1306 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
1015 for entry in src_uri.split(): 1307 for entry in src_uri.split():
1016 if entry.startswith('git://'): 1308 if entry.startswith('git://'):
@@ -1041,7 +1333,7 @@ class DevtoolUpdateTests(DevtoolBase):
1041 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n') 1333 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n')
1042 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n') 1334 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n')
1043 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n') 1335 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n')
1044 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "${LAYERSERIES_COMPAT_core}"\n') 1336 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "%s"\n' % corenames)
1045 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir) 1337 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
1046 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir) 1338 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
1047 # Create the bbappend 1339 # Create the bbappend
@@ -1117,14 +1409,30 @@ class DevtoolUpdateTests(DevtoolBase):
1117 runCmd('echo "Bar" > new-file', cwd=tempdir) 1409 runCmd('echo "Bar" > new-file', cwd=tempdir)
1118 runCmd('git add new-file', cwd=tempdir) 1410 runCmd('git add new-file', cwd=tempdir)
1119 runCmd('git commit -m "Add new file"', cwd=tempdir) 1411 runCmd('git commit -m "Add new file"', cwd=tempdir)
1120 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
1121 os.path.dirname(recipefile))
1122 runCmd('devtool update-recipe %s' % testrecipe) 1412 runCmd('devtool update-recipe %s' % testrecipe)
1123 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1413 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1124 (' M', '.*/makedevs/makedevs.c$'), 1414 (' M', '.*/makedevs/makedevs.c$'),
1125 ('??', '.*/makedevs/new-local$'), 1415 ('??', '.*/makedevs/new-local$'),
1126 ('??', '.*/makedevs/0001-Add-new-file.patch$')] 1416 ('??', '.*/makedevs/0001-Add-new-file.patch$')]
1127 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1417 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1418 # Now try to update recipe in another layer, so first, clean it
1419 runCmd('cd %s; git clean -fd .; git checkout .' % os.path.dirname(recipefile))
1420 # Create a temporary layer and add it to bblayers.conf
1421 self._create_temp_layer(templayerdir, True, 'templayer')
1422 # Update recipe in templayer
1423 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
1424 self.assertNotIn('WARNING:', result.output)
1425 # Check recipe is still clean
1426 self._check_repo_status(os.path.dirname(recipefile), [])
1427 splitpath = os.path.dirname(recipefile).split(os.sep)
1428 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
1429 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
1430 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-new-file.patch')
1431 new_local_file = os.path.join(appenddir, testrecipe, 'new_local')
1432 local_file = os.path.join(appenddir, testrecipe, 'makedevs.c')
1433 self.assertExists(patchfile, 'Patch file 0001-Add-new-file.patch not created')
1434 self.assertExists(local_file, 'File makedevs.c not created')
1435 self.assertExists(patchfile, 'File new_local not created')
1128 1436
1129 def test_devtool_update_recipe_local_files_2(self): 1437 def test_devtool_update_recipe_local_files_2(self):
1130 """Check local source files support when oe-local-files is in Git""" 1438 """Check local source files support when oe-local-files is in Git"""
@@ -1259,7 +1567,7 @@ class DevtoolUpdateTests(DevtoolBase):
1259 # Modify one file 1567 # Modify one file
1260 srctree = os.path.join(self.workspacedir, 'sources', testrecipe) 1568 srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
1261 runCmd('echo "Another line" >> README', cwd=srctree) 1569 runCmd('echo "Another line" >> README', cwd=srctree)
1262 runCmd('git commit -a --amend --no-edit', cwd=srctree) 1570 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))) 1571 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) 1572 result = runCmd('devtool update-recipe %s' % testrecipe)
1265 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)] 1573 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
@@ -1295,6 +1603,121 @@ class DevtoolUpdateTests(DevtoolBase):
1295 expected_status = [] 1603 expected_status = []
1296 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1604 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1297 1605
1606 def test_devtool_finish_modify_git_subdir(self):
1607 # Check preconditions
1608 testrecipe = 'dos2unix'
1609 self.append_config('ERROR_QA:remove:pn-dos2unix = "patch-status"\n')
1610 bb_vars = get_bb_vars(['SRC_URI', 'S', 'WORKDIR', 'FILE'], testrecipe)
1611 self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe)
1612 workdir_git = '%s/git/' % bb_vars['WORKDIR']
1613 if not bb_vars['S'].startswith(workdir_git):
1614 self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe)
1615 subdir = bb_vars['S'].split(workdir_git, 1)[1]
1616 # Clean up anything in the workdir/sysroot/sstate cache
1617 bitbake('%s -c cleansstate' % testrecipe)
1618 # Try modifying a recipe
1619 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1620 self.track_for_cleanup(tempdir)
1621 self.track_for_cleanup(self.workspacedir)
1622 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1623 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1624 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1625 testsrcfile = os.path.join(tempdir, subdir, 'dos2unix.c')
1626 self.assertExists(testsrcfile, 'Extracted source could not be found')
1627 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
1628 self.assertNotExists(os.path.join(tempdir, subdir, '.git'), 'Subdirectory has been initialised as a git repo')
1629 # Check git repo
1630 self._check_src_repo(tempdir)
1631 # Modify file
1632 runCmd("sed -i '1s:^:/* Add a comment */\\n:' %s" % testsrcfile)
1633 result = runCmd('git commit -a -m "Add a comment"', cwd=tempdir)
1634 # Now try updating original recipe
1635 recipefile = bb_vars['FILE']
1636 recipedir = os.path.dirname(recipefile)
1637 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
1638 result = runCmd('devtool update-recipe %s' % testrecipe)
1639 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1640 ('??', '.*/%s/%s/$' % (testrecipe, testrecipe))]
1641 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1642 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
1643 removelines = ['SRC_URI = "git://.*"']
1644 addlines = [
1645 'SRC_URI = "git://.* \\\\',
1646 'file://0001-Add-a-comment.patch;patchdir=.. \\\\',
1647 '"'
1648 ]
1649 self._check_diff(result.output, addlines, removelines)
1650 # Put things back so we can run devtool finish on a different layer
1651 runCmd('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
1652 # Run devtool finish
1653 res = re.search('recipes-.*', recipedir)
1654 self.assertTrue(res, 'Unable to find recipe subdirectory')
1655 recipesubdir = res[0]
1656 self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, recipesubdir))
1657 result = runCmd('devtool finish %s meta-selftest' % testrecipe)
1658 # Check bbappend file contents
1659 appendfn = os.path.join(self.testlayer_path, recipesubdir, '%s_%%.bbappend' % testrecipe)
1660 with open(appendfn, 'r') as f:
1661 appendlines = f.readlines()
1662 expected_appendlines = [
1663 'FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
1664 '\n',
1665 'SRC_URI += "file://0001-Add-a-comment.patch;patchdir=.."\n',
1666 '\n'
1667 ]
1668 self.assertEqual(appendlines, expected_appendlines)
1669 self.assertExists(os.path.join(os.path.dirname(appendfn), testrecipe, '0001-Add-a-comment.patch'))
1670 # Try building
1671 bitbake('%s -c patch' % testrecipe)
1672
1673 def test_devtool_git_submodules(self):
1674 # This tests if we can add a patch in a git submodule and extract it properly using devtool finish
1675 # Check preconditions
1676 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1677 self.track_for_cleanup(self.workspacedir)
1678 recipe = 'vulkan-samples'
1679 src_uri = get_bb_var('SRC_URI', recipe)
1680 self.assertIn('gitsm://', src_uri, 'This test expects the %s recipe to be a git recipe with submodules' % recipe)
1681 oldrecipefile = get_bb_var('FILE', recipe)
1682 recipedir = os.path.dirname(oldrecipefile)
1683 result = runCmd('git status --porcelain .', cwd=recipedir)
1684 if result.output.strip():
1685 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
1686 self.assertIn('/meta/', recipedir)
1687 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1688 self.track_for_cleanup(tempdir)
1689 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1690 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
1691 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
1692 # Test devtool status
1693 result = runCmd('devtool status')
1694 self.assertIn(recipe, result.output)
1695 self.assertIn(tempdir, result.output)
1696 # Modify a source file in a submodule, (grab the first one)
1697 result = runCmd('git submodule --quiet foreach \'echo $sm_path\'', cwd=tempdir)
1698 submodule = result.output.splitlines()[0]
1699 submodule_path = os.path.join(tempdir, submodule)
1700 runCmd('echo "#This is a first comment" >> testfile', cwd=submodule_path)
1701 result = runCmd('git status --porcelain . ', cwd=submodule_path)
1702 self.assertIn("testfile", result.output)
1703 runCmd('git add testfile; git commit -m "Adding a new file"', cwd=submodule_path)
1704
1705 # Try finish to the original layer
1706 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1707 runCmd('devtool finish -f %s meta' % recipe)
1708 result = runCmd('devtool status')
1709 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1710 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
1711 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
1712 ('??', '.*/.*-Adding-a-new-file.patch$')]
1713 self._check_repo_status(recipedir, expected_status)
1714 # Make sure the patch is added to the recipe with the correct "patchdir" option
1715 result = runCmd('git diff .', cwd=recipedir)
1716 addlines = [
1717 'file://0001-Adding-a-new-file.patch;patchdir=%s \\\\' % submodule
1718 ]
1719 self._check_diff(result.output, addlines, [])
1720
1298class DevtoolExtractTests(DevtoolBase): 1721class DevtoolExtractTests(DevtoolBase):
1299 1722
1300 def test_devtool_extract(self): 1723 def test_devtool_extract(self):
@@ -1343,33 +1766,15 @@ class DevtoolExtractTests(DevtoolBase):
1343 matches2 = glob.glob(stampprefix2 + '*') 1766 matches2 = glob.glob(stampprefix2 + '*')
1344 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2) 1767 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
1345 1768
1769 @OETestTag("runqemu")
1346 def test_devtool_deploy_target(self): 1770 def test_devtool_deploy_target(self):
1347 # NOTE: Whilst this test would seemingly be better placed as a runtime test, 1771 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') 1772 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1370 # Definitions 1773 # Definitions
1371 testrecipe = 'mdadm' 1774 testrecipe = 'mdadm'
1372 testfile = '/sbin/mdadm' 1775 testfile = '/sbin/mdadm'
1776 if "usrmerge" in get_bb_var('DISTRO_FEATURES'):
1777 testfile = '/usr/sbin/mdadm'
1373 testimage = 'oe-selftest-image' 1778 testimage = 'oe-selftest-image'
1374 testcommand = '/sbin/mdadm --help' 1779 testcommand = '/sbin/mdadm --help'
1375 # Build an image to run 1780 # Build an image to run
@@ -1463,6 +1868,14 @@ class DevtoolExtractTests(DevtoolBase):
1463 1868
1464class DevtoolUpgradeTests(DevtoolBase): 1869class DevtoolUpgradeTests(DevtoolBase):
1465 1870
1871 def setUp(self):
1872 super().setUp()
1873 try:
1874 runCmd("git config --global user.name")
1875 runCmd("git config --global user.email")
1876 except:
1877 self.skip("Git user.name and user.email must be set")
1878
1466 def test_devtool_upgrade(self): 1879 def test_devtool_upgrade(self):
1467 # Check preconditions 1880 # Check preconditions
1468 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1881 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
@@ -1543,6 +1956,54 @@ class DevtoolUpgradeTests(DevtoolBase):
1543 self.assertNotIn(recipe, result.output) 1956 self.assertNotIn(recipe, result.output)
1544 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') 1957 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
1545 1958
1959 def test_devtool_upgrade_drop_md5sum(self):
1960 # Check preconditions
1961 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1962 self.track_for_cleanup(self.workspacedir)
1963 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1964 # For the moment, we are using a real recipe.
1965 recipe = 'devtool-upgrade-test3'
1966 version = '1.6.0'
1967 oldrecipefile = get_bb_var('FILE', recipe)
1968 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1969 self.track_for_cleanup(tempdir)
1970 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
1971 # we are downgrading instead of upgrading.
1972 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
1973 # Check new recipe file is present
1974 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
1975 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
1976 # Check recipe got changed as expected
1977 with open(oldrecipefile + '.upgraded', 'r') as f:
1978 desiredlines = f.readlines()
1979 with open(newrecipefile, 'r') as f:
1980 newlines = f.readlines()
1981 self.assertEqual(desiredlines, newlines)
1982
1983 def test_devtool_upgrade_all_checksums(self):
1984 # Check preconditions
1985 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1986 self.track_for_cleanup(self.workspacedir)
1987 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1988 # For the moment, we are using a real recipe.
1989 recipe = 'devtool-upgrade-test4'
1990 version = '1.6.0'
1991 oldrecipefile = get_bb_var('FILE', recipe)
1992 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1993 self.track_for_cleanup(tempdir)
1994 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
1995 # we are downgrading instead of upgrading.
1996 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
1997 # Check new recipe file is present
1998 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
1999 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
2000 # Check recipe got changed as expected
2001 with open(oldrecipefile + '.upgraded', 'r') as f:
2002 desiredlines = f.readlines()
2003 with open(newrecipefile, 'r') as f:
2004 newlines = f.readlines()
2005 self.assertEqual(desiredlines, newlines)
2006
1546 def test_devtool_layer_plugins(self): 2007 def test_devtool_layer_plugins(self):
1547 """Test that devtool can use plugins from other layers. 2008 """Test that devtool can use plugins from other layers.
1548 2009
@@ -1561,7 +2022,15 @@ class DevtoolUpgradeTests(DevtoolBase):
1561 for p in paths: 2022 for p in paths:
1562 dstdir = os.path.join(dstdir, p) 2023 dstdir = os.path.join(dstdir, p)
1563 if not os.path.exists(dstdir): 2024 if not os.path.exists(dstdir):
1564 os.makedirs(dstdir) 2025 try:
2026 os.makedirs(dstdir)
2027 except PermissionError:
2028 return False
2029 except OSError as e:
2030 if e.errno == errno.EROFS:
2031 return False
2032 else:
2033 raise e
1565 if p == "lib": 2034 if p == "lib":
1566 # Can race with other tests 2035 # Can race with other tests
1567 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir) 2036 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir)
@@ -1569,8 +2038,12 @@ class DevtoolUpgradeTests(DevtoolBase):
1569 self.track_for_cleanup(dstdir) 2038 self.track_for_cleanup(dstdir)
1570 dstfile = os.path.join(dstdir, os.path.basename(srcfile)) 2039 dstfile = os.path.join(dstdir, os.path.basename(srcfile))
1571 if srcfile != dstfile: 2040 if srcfile != dstfile:
1572 shutil.copy(srcfile, dstfile) 2041 try:
2042 shutil.copy(srcfile, dstfile)
2043 except PermissionError:
2044 return False
1573 self.track_for_cleanup(dstfile) 2045 self.track_for_cleanup(dstfile)
2046 return True
1574 2047
1575 def test_devtool_load_plugin(self): 2048 def test_devtool_load_plugin(self):
1576 """Test that devtool loads only the first found plugin in BBPATH.""" 2049 """Test that devtool loads only the first found plugin in BBPATH."""
@@ -1588,15 +2061,17 @@ class DevtoolUpgradeTests(DevtoolBase):
1588 plugincontent = fh.readlines() 2061 plugincontent = fh.readlines()
1589 try: 2062 try:
1590 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found') 2063 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
1591 for path in searchpath: 2064 searchpath = [
1592 self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool') 2065 path for path in searchpath
2066 if self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
2067 ]
1593 result = runCmd("devtool --quiet count") 2068 result = runCmd("devtool --quiet count")
1594 self.assertEqual(result.output, '1') 2069 self.assertEqual(result.output, '1')
1595 result = runCmd("devtool --quiet multiloaded") 2070 result = runCmd("devtool --quiet multiloaded")
1596 self.assertEqual(result.output, "no") 2071 self.assertEqual(result.output, "no")
1597 for path in searchpath: 2072 for path in searchpath:
1598 result = runCmd("devtool --quiet bbdir") 2073 result = runCmd("devtool --quiet bbdir")
1599 self.assertEqual(result.output, path) 2074 self.assertEqual(os.path.realpath(result.output), os.path.realpath(path))
1600 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py')) 2075 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
1601 finally: 2076 finally:
1602 with open(srcfile, 'w') as fh: 2077 with open(srcfile, 'w') as fh:
@@ -1777,6 +2252,52 @@ class DevtoolUpgradeTests(DevtoolBase):
1777 if files: 2252 if files:
1778 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files)) 2253 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
1779 2254
2255 def test_devtool_finish_update_patch(self):
2256 # This test uses a modified version of the sysdig recipe from meta-oe.
2257 # - The patches have been renamed.
2258 # - The dependencies are commented out since the recipe is not being
2259 # built.
2260 #
2261 # The sysdig recipe is interesting in that it fetches two different Git
2262 # repositories, and there are patches for both. This leads to that
2263 # devtool will create ignore commits as it uses Git submodules to keep
2264 # track of the second repository.
2265 #
2266 # This test will verify that the ignored commits actually are ignored
2267 # when a commit in between is modified. It will also verify that the
2268 # updated patch keeps its original name.
2269
2270 # Check preconditions
2271 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2272 # Try modifying a recipe
2273 self.track_for_cleanup(self.workspacedir)
2274 recipe = 'sysdig-selftest'
2275 recipefile = get_bb_var('FILE', recipe)
2276 recipedir = os.path.dirname(recipefile)
2277 result = runCmd('git status --porcelain .', cwd=recipedir)
2278 if result.output.strip():
2279 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
2280 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2281 self.track_for_cleanup(tempdir)
2282 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2283 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
2284 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (recipedir, recipe, recipe, os.path.basename(recipefile)))
2285 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
2286 # Make a change to one of the existing commits
2287 result = runCmd('echo "# A comment " >> CMakeLists.txt', cwd=tempdir)
2288 result = runCmd('git status --porcelain', cwd=tempdir)
2289 self.assertIn('M CMakeLists.txt', result.output)
2290 result = runCmd('git commit --fixup HEAD^ CMakeLists.txt', cwd=tempdir)
2291 result = runCmd('git show -s --format=%s', cwd=tempdir)
2292 self.assertIn('fixup! cmake: Pass PROBE_NAME via CFLAGS', result.output)
2293 result = runCmd('GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash devtool-base', cwd=tempdir)
2294 result = runCmd('devtool finish %s meta-selftest' % recipe)
2295 result = runCmd('devtool status')
2296 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
2297 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
2298 expected_status = [(' M', '.*/0099-cmake-Pass-PROBE_NAME-via-CFLAGS.patch$')]
2299 self._check_repo_status(recipedir, expected_status)
2300
1780 def test_devtool_rename(self): 2301 def test_devtool_rename(self):
1781 # Check preconditions 2302 # Check preconditions
1782 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2303 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
@@ -1813,7 +2334,6 @@ class DevtoolUpgradeTests(DevtoolBase):
1813 self._test_recipe_contents(newrecipefile, checkvars, []) 2334 self._test_recipe_contents(newrecipefile, checkvars, [])
1814 # Try again - change just name this time 2335 # Try again - change just name this time
1815 result = runCmd('devtool reset -n %s' % newrecipename) 2336 result = runCmd('devtool reset -n %s' % newrecipename)
1816 shutil.rmtree(newsrctree)
1817 add_recipe() 2337 add_recipe()
1818 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever)) 2338 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
1819 result = runCmd('devtool rename %s %s' % (recipename, newrecipename)) 2339 result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
@@ -1826,7 +2346,6 @@ class DevtoolUpgradeTests(DevtoolBase):
1826 self._test_recipe_contents(newrecipefile, checkvars, []) 2346 self._test_recipe_contents(newrecipefile, checkvars, [])
1827 # Try again - change just version this time 2347 # Try again - change just version this time
1828 result = runCmd('devtool reset -n %s' % newrecipename) 2348 result = runCmd('devtool reset -n %s' % newrecipename)
1829 shutil.rmtree(newsrctree)
1830 add_recipe() 2349 add_recipe()
1831 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever)) 2350 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
1832 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever)) 2351 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
@@ -1858,8 +2377,9 @@ class DevtoolUpgradeTests(DevtoolBase):
1858 Expected: devtool modify is able to checkout the source of the kernel 2377 Expected: devtool modify is able to checkout the source of the kernel
1859 and modification to the source and configurations are reflected 2378 and modification to the source and configurations are reflected
1860 when building the kernel. 2379 when building the kernel.
1861 """ 2380 """
1862 kernel_provider = get_bb_var('PREFERRED_PROVIDER_virtual/kernel') 2381 kernel_provider = self.td['PREFERRED_PROVIDER_virtual/kernel']
2382
1863 # Clean up the environment 2383 # Clean up the environment
1864 bitbake('%s -c clean' % kernel_provider) 2384 bitbake('%s -c clean' % kernel_provider)
1865 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2385 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
@@ -1886,33 +2406,545 @@ class DevtoolUpgradeTests(DevtoolBase):
1886 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found') 2406 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
1887 #Step 4.2 2407 #Step 4.2
1888 configfile = os.path.join(tempdir,'.config') 2408 configfile = os.path.join(tempdir,'.config')
1889 diff = runCmd('diff %s %s' % (tmpconfig, configfile)) 2409 runCmd('diff %s %s' % (tmpconfig, configfile))
1890 self.assertEqual(0,diff.status,'Kernel .config file is not the same using bitbake and devtool') 2410
1891 #Step 4.3 2411 #Step 4.3
1892 #NOTE: virtual/kernel is mapped to kernel_provider 2412 #NOTE: virtual/kernel is mapped to kernel_provider
1893 result = runCmd('devtool build %s' % kernel_provider) 2413 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') 2414 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
1896 self.assertExists(kernelfile, 'Kernel was not build correctly') 2415 self.assertExists(kernelfile, 'Kernel was not build correctly')
1897 2416
1898 #Modify the kernel source 2417 #Modify the kernel source
1899 modfile = os.path.join(tempdir,'arch/x86/boot/header.S') 2418 modfile = os.path.join(tempdir, 'init/version.c')
1900 modstring = "Use a boot loader. Devtool testing." 2419 # Moved to uts.h in 6.1 onwards
1901 modapplied = runCmd("sed -i 's/Use a boot loader./%s/' %s" % (modstring, modfile)) 2420 modfile2 = os.path.join(tempdir, 'include/linux/uts.h')
1902 self.assertEqual(0,modapplied.status,'Modification to %s on kernel source failed' % modfile) 2421 runCmd("sed -i 's/Linux/LiNuX/g' %s %s" % (modfile, modfile2))
2422
1903 #Modify the configuration 2423 #Modify the configuration
1904 codeconfigfile = os.path.join(tempdir,'.config.new') 2424 codeconfigfile = os.path.join(tempdir, '.config.new')
1905 modconfopt = "CONFIG_SG_POOL=n" 2425 modconfopt = "CONFIG_SG_POOL=n"
1906 modconf = runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile)) 2426 runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
1907 self.assertEqual(0,modconf.status,'Modification to %s failed' % codeconfigfile) 2427
1908 #Build again kernel with devtool 2428 #Build again kernel with devtool
1909 rebuild = runCmd('devtool build %s' % kernel_provider) 2429 runCmd('devtool build %s' % kernel_provider)
1910 self.assertEqual(0,rebuild.status,'Fail to build kernel after modification of source and config') 2430
1911 #Step 4.4 2431 #Step 4.4
1912 bzimagename = 'bzImage-' + get_bb_var('KERNEL_VERSION_NAME', kernel_provider) 2432 runCmd("grep '%s' %s" % ('LiNuX', kernelfile))
1913 bzimagefile = os.path.join(get_bb_var('D', kernel_provider),'boot', bzimagename) 2433
1914 checkmodcode = runCmd("grep '%s' %s" % (modstring, bzimagefile))
1915 self.assertEqual(0,checkmodcode.status,'Modification on kernel source failed')
1916 #Step 4.5 2434 #Step 4.5
1917 checkmodconfg = runCmd("grep %s %s" % (modconfopt, codeconfigfile)) 2435 runCmd("grep %s %s" % (modconfopt, codeconfigfile))
1918 self.assertEqual(0,checkmodconfg.status,'Modification to configuration file failed') 2436
2437
2438class DevtoolIdeSdkTests(DevtoolBase):
2439 def _write_bb_config(self, recipe_names):
2440 """Helper to write the bitbake local.conf file"""
2441 conf_lines = [
2442 'IMAGE_CLASSES += "image-combined-dbg"',
2443 'IMAGE_GEN_DEBUGFS = "1"',
2444 'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join(
2445 [r + '-ptest' for r in recipe_names])
2446 ]
2447 self.write_config("\n".join(conf_lines))
2448
2449 def _check_workspace(self):
2450 """Check if a workspace directory is available and setup the cleanup"""
2451 self.assertTrue(not os.path.exists(self.workspacedir),
2452 'This test cannot be run with a workspace directory under the build directory')
2453 self.track_for_cleanup(self.workspacedir)
2454 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2455
2456 def _workspace_scripts_dir(self, recipe_name):
2457 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts'))
2458
2459 def _sources_scripts_dir(self, src_dir):
2460 return os.path.realpath(os.path.join(src_dir, 'oe-scripts'))
2461
2462 def _workspace_gdbinit_dir(self, recipe_name):
2463 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts', 'gdbinit'))
2464
2465 def _sources_gdbinit_dir(self, src_dir):
2466 return os.path.realpath(os.path.join(src_dir, 'oe-gdbinit'))
2467
2468 def _devtool_ide_sdk_recipe(self, recipe_name, build_file, testimage):
2469 """Setup a recipe for working with devtool ide-sdk
2470
2471 Basically devtool modify -x followed by some tests
2472 """
2473 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2474 self.track_for_cleanup(tempdir)
2475 self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name)
2476
2477 result = runCmd('devtool modify %s -x %s' % (recipe_name, tempdir))
2478 self.assertExists(os.path.join(tempdir, build_file),
2479 'Extracted source could not be found')
2480 self.assertExists(os.path.join(self.workspacedir, 'conf',
2481 'layer.conf'), 'Workspace directory not created')
2482 matches = glob.glob(os.path.join(self.workspacedir,
2483 'appends', recipe_name + '.bbappend'))
2484 self.assertTrue(matches, 'bbappend not created %s' % result.output)
2485
2486 # Test devtool status
2487 result = runCmd('devtool status')
2488 self.assertIn(recipe_name, result.output)
2489 self.assertIn(tempdir, result.output)
2490 self._check_src_repo(tempdir)
2491
2492 # Usually devtool ide-sdk would initiate the build of the SDK.
2493 # But there is a circular dependency with starting Qemu and passing the IP of runqemu to devtool ide-sdk.
2494 if testimage:
2495 bitbake("%s qemu-native qemu-helper-native" % testimage)
2496 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
2497 self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
2498 self.add_command_to_tearDown(
2499 'rm -f %s/%s*' % (deploy_dir_image, testimage))
2500
2501 return tempdir
2502
2503 def _get_recipe_ids(self, recipe_name):
2504 """IDs needed to write recipe specific config entries into IDE config files"""
2505 package_arch = get_bb_var('PACKAGE_ARCH', recipe_name)
2506 recipe_id = recipe_name + "-" + package_arch
2507 recipe_id_pretty = recipe_name + ": " + package_arch
2508 return (recipe_id, recipe_id_pretty)
2509
2510 def _verify_install_script_code(self, tempdir, recipe_name):
2511 """Verify the scripts referred by the tasks.json file are fine.
2512
2513 This function does not depend on Qemu. Therefore it verifies the scripts
2514 exists and the delete step works as expected. But it does not try to
2515 deploy to Qemu.
2516 """
2517 recipe_id, recipe_id_pretty = self._get_recipe_ids(recipe_name)
2518 with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j:
2519 tasks_d = json.load(tasks_j)
2520 tasks = tasks_d["tasks"]
2521 task_install = next(
2522 (task for task in tasks if task["label"] == "install && deploy-target %s" % recipe_id_pretty), None)
2523 self.assertIsNot(task_install, None)
2524 # execute only the bb_run_do_install script since the deploy would require e.g. Qemu running.
2525 i_and_d_script = "install_and_deploy_" + recipe_id
2526 i_and_d_script_path = os.path.join(
2527 self._workspace_scripts_dir(recipe_name), i_and_d_script)
2528 self.assertExists(i_and_d_script_path)
2529 del_script = "delete_package_dirs_" + recipe_id
2530 del_script_path = os.path.join(
2531 self._workspace_scripts_dir(recipe_name), del_script)
2532 self.assertExists(del_script_path)
2533 runCmd(del_script_path, cwd=tempdir)
2534
2535 def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe):
2536 """Verify deployment and execution in Qemu system work for one recipe.
2537
2538 This function checks the entire SDK workflow: changing the code, recompiling
2539 it and deploying it back to Qemu, and checking that the changes have been
2540 incorporated into the provided binaries. It also runs the tests of the recipe.
2541 """
2542 recipe_id, _ = self._get_recipe_ids(recipe_name)
2543 i_and_d_script = "install_and_deploy_" + recipe_id
2544 install_deploy_cmd = os.path.join(
2545 self._workspace_scripts_dir(recipe_name), i_and_d_script)
2546 self.assertExists(install_deploy_cmd,
2547 '%s script not found' % install_deploy_cmd)
2548 runCmd(install_deploy_cmd)
2549
2550 MAGIC_STRING_ORIG = "Magic: 123456789"
2551 MAGIC_STRING_NEW = "Magic: 987654321"
2552 ptest_cmd = "ptest-runner " + recipe_name
2553
2554 # validate that SSH is working
2555 status, _ = qemu.run("uname")
2556 self.assertEqual(
2557 status, 0, msg="Failed to connect to the SSH server on Qemu")
2558
2559 # Verify the unmodified example prints the magic string
2560 status, output = qemu.run(example_exe)
2561 self.assertEqual(status, 0, msg="%s failed: %s" %
2562 (example_exe, output))
2563 self.assertIn(MAGIC_STRING_ORIG, output)
2564
2565 # Verify the unmodified ptests work
2566 status, output = qemu.run(ptest_cmd)
2567 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
2568 self.assertIn("PASS: cpp-example-lib", output)
2569
2570 # Verify remote debugging works
2571 self._gdb_cross_debugging(
2572 qemu, recipe_name, example_exe, MAGIC_STRING_ORIG)
2573
2574 # Replace the Magic String in the code, compile and deploy to Qemu
2575 cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp')
2576 with open(cpp_example_lib_hpp, 'r') as file:
2577 cpp_code = file.read()
2578 cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW)
2579 with open(cpp_example_lib_hpp, 'w') as file:
2580 file.write(cpp_code)
2581 runCmd(install_deploy_cmd, cwd=tempdir)
2582
2583 # Verify the modified example prints the modified magic string
2584 status, output = qemu.run(example_exe)
2585 self.assertEqual(status, 0, msg="%s failed: %s" %
2586 (example_exe, output))
2587 self.assertNotIn(MAGIC_STRING_ORIG, output)
2588 self.assertIn(MAGIC_STRING_NEW, output)
2589
2590 # Verify the modified example ptests work
2591 status, output = qemu.run(ptest_cmd)
2592 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
2593 self.assertIn("PASS: cpp-example-lib", output)
2594
2595 # Verify remote debugging works wit the modified magic string
2596 self._gdb_cross_debugging(
2597 qemu, recipe_name, example_exe, MAGIC_STRING_NEW)
2598
2599 def _gdb_cross(self):
2600 """Verify gdb-cross is provided by devtool ide-sdk"""
2601 target_arch = self.td["TARGET_ARCH"]
2602 target_sys = self.td["TARGET_SYS"]
2603 gdb_recipe = "gdb-cross-" + target_arch
2604 gdb_binary = target_sys + "-gdb"
2605
2606 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe)
2607 r = runCmd("%s --version" % gdb_binary,
2608 native_sysroot=native_sysroot, target_sys=target_sys)
2609 self.assertEqual(r.status, 0)
2610 self.assertIn("GNU gdb", r.output)
2611
2612 def _gdb_cross_debugging(self, qemu, recipe_name, example_exe, magic_string):
2613 """Verify gdb-cross is working
2614
2615 Test remote debugging:
2616 break main
2617 run
2618 continue
2619 break CppExample::print_json()
2620 continue
2621 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789")
2622 $1 = 0
2623 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789aaa")
2624 $2 = -3
2625 list cpp-example-lib.hpp:13,13
2626 13 inline static const std::string test_string = "cpp-example-lib Magic: 123456789";
2627 continue
2628 """
2629 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
2630 gdbserver_script = os.path.join(self._workspace_scripts_dir(
2631 recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_m')
2632 gdb_script = os.path.join(self._workspace_scripts_dir(
2633 recipe_name), 'gdb_1234_usr-bin-' + example_exe)
2634
2635 # Start a gdbserver
2636 r = runCmd(gdbserver_script)
2637 self.assertEqual(r.status, 0)
2638
2639 # Check there is a gdbserver running
2640 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
2641 self.assertEqual(r.status, 0)
2642 self.assertIn("gdbserver ", r.output)
2643
2644 # Check the pid file is correct
2645 test_cmd = "cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \
2646 example_exe + "/pid)/cmdline"
2647 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd))
2648 self.assertEqual(r.status, 0)
2649 self.assertIn("gdbserver", r.output)
2650
2651 # Test remote debugging works
2652 gdb_batch_cmd = " --batch -ex 'break main' -ex 'run'"
2653 gdb_batch_cmd += " -ex 'break CppExample::print_json()' -ex 'continue'"
2654 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %s\")'" % magic_string
2655 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %saaa\")'" % magic_string
2656 gdb_batch_cmd += " -ex 'list cpp-example-lib.hpp:13,13'"
2657 gdb_batch_cmd += " -ex 'continue'"
2658 r = runCmd(gdb_script + gdb_batch_cmd)
2659 self.logger.debug("%s %s returned: %s", gdb_script,
2660 gdb_batch_cmd, r.output)
2661 self.assertEqual(r.status, 0)
2662 self.assertIn("Breakpoint 1, main", r.output)
2663 self.assertIn("$1 = 0", r.output) # test.string.compare equal
2664 self.assertIn("$2 = -3", r.output) # test.string.compare longer
2665 self.assertIn(
2666 'inline static const std::string test_string = "cpp-example-lib %s";' % magic_string, r.output)
2667 self.assertIn("exited normally", r.output)
2668
2669 # Stop the gdbserver
2670 r = runCmd(gdbserver_script + ' stop')
2671 self.assertEqual(r.status, 0)
2672
2673 # Check there is no gdbserver running
2674 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
2675 self.assertEqual(r.status, 0)
2676 self.assertNotIn("gdbserver ", r.output)
2677
2678 def _verify_cmake_preset(self, tempdir):
2679 """Verify the generated cmake preset works as expected
2680
2681 Check if compiling works
2682 Check if unit tests can be executed in qemu (not qemu-system)
2683 """
2684 with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j:
2685 cmake_preset_d = json.load(cmake_preset_j)
2686 config_presets = cmake_preset_d["configurePresets"]
2687 self.assertEqual(len(config_presets), 1)
2688 cmake_exe = config_presets[0]["cmakeExecutable"]
2689 preset_name = config_presets[0]["name"]
2690
2691 # Verify the wrapper for cmake native is available
2692 self.assertExists(cmake_exe)
2693
2694 # Verify the cmake preset generated by devtool ide-sdk is available
2695 result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir)
2696 self.assertIn(preset_name, result.output)
2697
2698 # Verify cmake re-uses the o files compiled by bitbake
2699 result = runCmd('%s --build --preset %s' %
2700 (cmake_exe, preset_name), cwd=tempdir)
2701 self.assertIn("ninja: no work to do.", result.output)
2702
2703 # Verify the unit tests work (in Qemu user mode)
2704 result = runCmd('%s --build --preset %s --target test' %
2705 (cmake_exe, preset_name), cwd=tempdir)
2706 self.assertIn("100% tests passed", result.output)
2707
2708 # Verify re-building and testing works again
2709 result = runCmd('%s --build --preset %s --target clean' %
2710 (cmake_exe, preset_name), cwd=tempdir)
2711 self.assertIn("Cleaning", result.output)
2712 result = runCmd('%s --build --preset %s' %
2713 (cmake_exe, preset_name), cwd=tempdir)
2714 self.assertIn("Building", result.output)
2715 self.assertIn("Linking", result.output)
2716 result = runCmd('%s --build --preset %s --target test' %
2717 (cmake_exe, preset_name), cwd=tempdir)
2718 self.assertIn("Running tests...", result.output)
2719 self.assertIn("100% tests passed", result.output)
2720
2721 @OETestTag("runqemu")
2722 def test_devtool_ide_sdk_none_qemu(self):
2723 """Start qemu-system and run tests for multiple recipes. ide=none is used."""
2724 recipe_names = ["cmake-example", "meson-example"]
2725 testimage = "oe-selftest-image"
2726
2727 self._check_workspace()
2728 self._write_bb_config(recipe_names)
2729 self._check_runqemu_prerequisites()
2730
2731 # Verify deployment to Qemu (system mode) works
2732 bitbake(testimage)
2733 with runqemu(testimage, runqemuparams="nographic") as qemu:
2734 # cmake-example recipe
2735 recipe_name = "cmake-example"
2736 example_exe = "cmake-example"
2737 build_file = "CMakeLists.txt"
2738 tempdir = self._devtool_ide_sdk_recipe(
2739 recipe_name, build_file, testimage)
2740 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
2741 recipe_name, testimage, qemu.ip)
2742 runCmd(bitbake_sdk_cmd)
2743 self._gdb_cross()
2744 self._verify_cmake_preset(tempdir)
2745 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
2746 # Verify the oe-scripts sym-link is valid
2747 self.assertEqual(self._workspace_scripts_dir(
2748 recipe_name), self._sources_scripts_dir(tempdir))
2749
2750 # meson-example recipe
2751 recipe_name = "meson-example"
2752 example_exe = "mesonex"
2753 build_file = "meson.build"
2754 tempdir = self._devtool_ide_sdk_recipe(
2755 recipe_name, build_file, testimage)
2756 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
2757 recipe_name, testimage, qemu.ip)
2758 runCmd(bitbake_sdk_cmd)
2759 self._gdb_cross()
2760 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
2761 # Verify the oe-scripts sym-link is valid
2762 self.assertEqual(self._workspace_scripts_dir(
2763 recipe_name), self._sources_scripts_dir(tempdir))
2764
2765 def test_devtool_ide_sdk_code_cmake(self):
2766 """Verify a cmake recipe works with ide=code mode"""
2767 recipe_name = "cmake-example"
2768 build_file = "CMakeLists.txt"
2769 testimage = "oe-selftest-image"
2770
2771 self._check_workspace()
2772 self._write_bb_config([recipe_name])
2773 tempdir = self._devtool_ide_sdk_recipe(
2774 recipe_name, build_file, testimage)
2775 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
2776 recipe_name, testimage)
2777 runCmd(bitbake_sdk_cmd)
2778 self._verify_cmake_preset(tempdir)
2779 self._verify_install_script_code(tempdir, recipe_name)
2780 self._gdb_cross()
2781
2782 def test_devtool_ide_sdk_code_meson(self):
2783 """Verify a meson recipe works with ide=code mode"""
2784 recipe_name = "meson-example"
2785 build_file = "meson.build"
2786 testimage = "oe-selftest-image"
2787
2788 self._check_workspace()
2789 self._write_bb_config([recipe_name])
2790 tempdir = self._devtool_ide_sdk_recipe(
2791 recipe_name, build_file, testimage)
2792 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
2793 recipe_name, testimage)
2794 runCmd(bitbake_sdk_cmd)
2795
2796 with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j:
2797 settings_d = json.load(settings_j)
2798 meson_exe = settings_d["mesonbuild.mesonPath"]
2799 meson_build_folder = settings_d["mesonbuild.buildFolder"]
2800
2801 # Verify the wrapper for meson native is available
2802 self.assertExists(meson_exe)
2803
2804 # Verify meson re-uses the o files compiled by bitbake
2805 result = runCmd('%s compile -C %s' %
2806 (meson_exe, meson_build_folder), cwd=tempdir)
2807 self.assertIn("ninja: no work to do.", result.output)
2808
2809 # Verify the unit tests work (in Qemu)
2810 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
2811
2812 # Verify re-building and testing works again
2813 result = runCmd('%s compile -C %s --clean' %
2814 (meson_exe, meson_build_folder), cwd=tempdir)
2815 self.assertIn("Cleaning...", result.output)
2816 result = runCmd('%s compile -C %s' %
2817 (meson_exe, meson_build_folder), cwd=tempdir)
2818 self.assertIn("Linking target", result.output)
2819 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
2820
2821 self._verify_install_script_code(tempdir, recipe_name)
2822 self._gdb_cross()
2823
2824 def test_devtool_ide_sdk_shared_sysroots(self):
2825 """Verify the shared sysroot SDK"""
2826
2827 # Handle the workspace (which is not needed by this test case)
2828 self._check_workspace()
2829
2830 result_init = runCmd(
2831 'devtool ide-sdk -m shared oe-selftest-image cmake-example meson-example --ide=code')
2832 bb_vars = get_bb_vars(
2833 ['REAL_MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE'], "meta-ide-support")
2834 environment_script = 'environment-setup-%s' % bb_vars['REAL_MULTIMACH_TARGET_SYS']
2835 deploydir = bb_vars['DEPLOY_DIR_IMAGE']
2836 environment_script_path = os.path.join(deploydir, environment_script)
2837 cpp_example_src = os.path.join(
2838 bb_vars['COREBASE'], 'meta-selftest', 'recipes-test', 'cpp', 'files')
2839
2840 # Verify the cross environment script is available
2841 self.assertExists(environment_script_path)
2842
2843 def runCmdEnv(cmd, cwd):
2844 cmd = '/bin/sh -c ". %s > /dev/null && %s"' % (
2845 environment_script_path, cmd)
2846 return runCmd(cmd, cwd)
2847
2848 # Verify building the C++ example works with CMake
2849 tempdir_cmake = tempfile.mkdtemp(prefix='devtoolqa')
2850 self.track_for_cleanup(tempdir_cmake)
2851
2852 result_cmake = runCmdEnv("which cmake", cwd=tempdir_cmake)
2853 cmake_native = os.path.normpath(result_cmake.output.strip())
2854 self.assertExists(cmake_native)
2855
2856 runCmdEnv('cmake %s' % cpp_example_src, cwd=tempdir_cmake)
2857 runCmdEnv('cmake --build %s' % tempdir_cmake, cwd=tempdir_cmake)
2858
2859 # Verify the printed note really referres to a cmake executable
2860 cmake_native_code = ""
2861 for line in result_init.output.splitlines():
2862 m = re.search(r'"cmake.cmakePath": "(.*)"', line)
2863 if m:
2864 cmake_native_code = m.group(1)
2865 break
2866 self.assertExists(cmake_native_code)
2867 self.assertEqual(cmake_native, cmake_native_code)
2868
2869 # Verify building the C++ example works with Meson
2870 tempdir_meson = tempfile.mkdtemp(prefix='devtoolqa')
2871 self.track_for_cleanup(tempdir_meson)
2872
2873 result_cmake = runCmdEnv("which meson", cwd=tempdir_meson)
2874 meson_native = os.path.normpath(result_cmake.output.strip())
2875 self.assertExists(meson_native)
2876
2877 runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src)
2878 runCmdEnv('meson compile', cwd=tempdir_meson)
2879
2880 def test_devtool_ide_sdk_plugins(self):
2881 """Test that devtool ide-sdk can use plugins from other layers."""
2882
2883 # We need a workspace layer and a modified recipe (but no image)
2884 modified_recipe_name = "meson-example"
2885 modified_build_file = "meson.build"
2886 testimage = "oe-selftest-image"
2887 shared_recipe_name = "cmake-example"
2888
2889 self._check_workspace()
2890 self._write_bb_config([modified_recipe_name])
2891 tempdir = self._devtool_ide_sdk_recipe(
2892 modified_recipe_name, modified_build_file, None)
2893
2894 IDE_RE = re.compile(r'.*--ide \{(.*)\}.*')
2895
2896 def get_ides_from_help(help_str):
2897 m = IDE_RE.search(help_str)
2898 return m.group(1).split(',')
2899
2900 # verify the default plugins are available but the foo plugin is not
2901 result = runCmd('devtool ide-sdk -h')
2902 found_ides = get_ides_from_help(result.output)
2903 self.assertIn('code', found_ides)
2904 self.assertIn('none', found_ides)
2905 self.assertNotIn('foo', found_ides)
2906
2907 shared_config_file = os.path.join(tempdir, 'shared-config.txt')
2908 shared_config_str = 'Dummy shared IDE config'
2909 modified_config_file = os.path.join(tempdir, 'modified-config.txt')
2910 modified_config_str = 'Dummy modified IDE config'
2911
2912 # Generate a foo plugin in the workspace layer
2913 plugin_dir = os.path.join(
2914 self.workspacedir, 'lib', 'devtool', 'ide_plugins')
2915 os.makedirs(plugin_dir)
2916 plugin_code = 'from devtool.ide_plugins import IdeBase\n\n'
2917 plugin_code += 'class IdeFoo(IdeBase):\n'
2918 plugin_code += ' def setup_shared_sysroots(self, shared_env):\n'
2919 plugin_code += ' with open("%s", "w") as config_file:\n' % shared_config_file
2920 plugin_code += ' config_file.write("%s")\n\n' % shared_config_str
2921 plugin_code += ' def setup_modified_recipe(self, args, image_recipe, modified_recipe):\n'
2922 plugin_code += ' with open("%s", "w") as config_file:\n' % modified_config_file
2923 plugin_code += ' config_file.write("%s")\n\n' % modified_config_str
2924 plugin_code += 'def register_ide_plugin(ide_plugins):\n'
2925 plugin_code += ' ide_plugins["foo"] = IdeFoo\n'
2926
2927 plugin_py = os.path.join(plugin_dir, 'ide_foo.py')
2928 with open(plugin_py, 'w') as plugin_file:
2929 plugin_file.write(plugin_code)
2930
2931 # Verify the foo plugin is available as well
2932 result = runCmd('devtool ide-sdk -h')
2933 found_ides = get_ides_from_help(result.output)
2934 self.assertIn('code', found_ides)
2935 self.assertIn('none', found_ides)
2936 self.assertIn('foo', found_ides)
2937
2938 # Verify the foo plugin generates a shared config
2939 result = runCmd(
2940 'devtool ide-sdk -m shared --skip-bitbake --ide foo %s' % shared_recipe_name)
2941 with open(shared_config_file) as shared_config:
2942 shared_config_new = shared_config.read()
2943 self.assertEqual(shared_config_str, shared_config_new)
2944
2945 # Verify the foo plugin generates a modified config
2946 result = runCmd('devtool ide-sdk --skip-bitbake --ide foo %s %s' %
2947 (modified_recipe_name, testimage))
2948 with open(modified_config_file) as modified_config:
2949 modified_config_new = modified_config.read()
2950 self.assertEqual(modified_config_str, modified_config_new)