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