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.toml') 1031 with open(cargo_config_path, "r") as f: 1032 cargo_config_contents = [line.strip('\n') for line in f.readlines()] 1033 1034 # Get back git dependencies of the recipe (ignoring the main one) 1035 # and check that they are all correctly patched to be fetched locally 1036 git_deps = [uri for uri in src_uri if uri.startswith("git://")][1:] 1037 for git_dep in git_deps: 1038 raw_url, _, raw_parms = git_dep.partition(";") 1039 parms = {} 1040 for parm in raw_parms.split(";"): 1041 name_parm, _, value_parm = parm.partition('=') 1042 parms[name_parm]=value_parm 1043 self.assertIn('protocol', parms, 'git dependencies uri should contain the "protocol" parameter') 1044 self.assertIn('name', parms, 'git dependencies uri should contain the "name" parameter') 1045 self.assertIn('destsuffix', parms, 'git dependencies uri should contain the "destsuffix" parameter') 1046 self.assertIn('type', parms, 'git dependencies uri should contain the "type" parameter') 1047 self.assertEqual(parms['type'], 'git-dependency', 'git dependencies uri should have "type=git-dependency"') 1048 raw_url = raw_url.replace("git://", '%s://' % parms['protocol']) 1049 patch_line = '[patch."%s"]' % raw_url 1050 path_patched = os.path.join(unpackdir, parms['destsuffix']) 1051 path_override_line = '%s = { path = "%s" }' % (parms['name'], path_patched) 1052 # Would have been better to use tomllib to read this file :/ 1053 self.assertIn(patch_line, cargo_config_contents) 1054 self.assertIn(path_override_line, cargo_config_contents) 1055 1056 # Try to package the recipe 1057 bitbake('-c package_qa %s' % testrecipe) 1058 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 cleanup_cmd = 'cd %s; rm %s/*.patch; git add %s; git checkout %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)) 1189 self.add_command_to_tearDown(cleanup_cmd) 1190 result = runCmd('devtool update-recipe %s' % testrecipe) 1191 result = runCmd('git add minicom', cwd=os.path.dirname(recipefile)) 1192 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1193 ('A ', '.*/0001-Change-the-README.patch$'), 1194 ('A ', '.*/0002-Add-a-new-file.patch$')] 1195 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1196 result = runCmd(cleanup_cmd) 1197 self._check_repo_status(os.path.dirname(recipefile), []) 1198 1199 def test_devtool_update_recipe_git(self): 1200 # Check preconditions 1201 testrecipe = 'mtd-utils-selftest' 1202 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1203 recipefile = bb_vars['FILE'] 1204 src_uri = bb_vars['SRC_URI'] 1205 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe) 1206 patches = [] 1207 for entry in src_uri.split(): 1208 if entry.startswith('file://') and entry.endswith('.patch'): 1209 patches.append(entry[7:].split(';')[0]) 1210 self.assertGreater(len(patches), 0, 'The %s recipe does not appear to contain any patches, so this test will not be effective' % testrecipe) 1211 self._check_repo_status(os.path.dirname(recipefile), []) 1212 # First, modify a recipe 1213 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1214 self.track_for_cleanup(tempdir) 1215 self.track_for_cleanup(self.workspacedir) 1216 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1217 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1218 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1219 # Check git repo 1220 self._check_src_repo(tempdir) 1221 # Add a couple of commits 1222 # FIXME: this only tests adding, need to also test update and remove 1223 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir) 1224 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir) 1225 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir) 1226 result = runCmd('git add devtool-new-file', cwd=tempdir) 1227 result = runCmd('git commit -m "Add a new file"', cwd=tempdir) 1228 self.add_command_to_tearDown('cd %s; rm -rf %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1229 result = runCmd('devtool update-recipe -m srcrev %s' % testrecipe) 1230 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile))] + \ 1231 [(' D', '.*/%s$' % patch) for patch in patches] 1232 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1233 1234 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile)) 1235 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git;branch=master"'] 1236 srcurilines = src_uri.split() 1237 srcurilines[0] = 'SRC_URI = "' + srcurilines[0] 1238 srcurilines.append('"') 1239 removelines = ['SRCREV = ".*"'] + srcurilines 1240 self._check_diff(result.output, addlines, removelines) 1241 # Now try with auto mode 1242 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile))) 1243 result = runCmd('devtool update-recipe %s' % testrecipe) 1244 result = runCmd('git rev-parse --show-toplevel', cwd=os.path.dirname(recipefile)) 1245 topleveldir = result.output.strip() 1246 relpatchpath = os.path.join(os.path.relpath(os.path.dirname(recipefile), topleveldir), testrecipe) 1247 expected_status = [(' M', os.path.relpath(recipefile, topleveldir)), 1248 ('??', '%s/0001-Change-the-Makefile.patch' % relpatchpath), 1249 ('??', '%s/0002-Add-a-new-file.patch' % relpatchpath)] 1250 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1251 1252 def test_devtool_update_recipe_append(self): 1253 # Check preconditions 1254 testrecipe = 'minicom' 1255 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1256 recipefile = bb_vars['FILE'] 1257 src_uri = bb_vars['SRC_URI'] 1258 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe) 1259 self._check_repo_status(os.path.dirname(recipefile), []) 1260 # First, modify a recipe 1261 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1262 tempsrcdir = os.path.join(tempdir, 'source') 1263 templayerdir = os.path.join(tempdir, 'layer') 1264 self.track_for_cleanup(tempdir) 1265 self.track_for_cleanup(self.workspacedir) 1266 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1267 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1268 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir)) 1269 # Check git repo 1270 self._check_src_repo(tempsrcdir) 1271 # Add a commit 1272 result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir) 1273 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) 1274 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe)) 1275 # Create a temporary layer and add it to bblayers.conf 1276 self._create_temp_layer(templayerdir, True, 'selftestupdaterecipe') 1277 # Create the bbappend 1278 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1279 self.assertNotIn('WARNING:', result.output) 1280 # Check recipe is still clean 1281 self._check_repo_status(os.path.dirname(recipefile), []) 1282 # Check bbappend was created 1283 splitpath = os.path.dirname(recipefile).split(os.sep) 1284 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1]) 1285 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir) 1286 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-our-custom-version.patch') 1287 self.assertExists(patchfile, 'Patch file not created') 1288 1289 # Check bbappend contents 1290 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 1291 '\n', 1292 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n', 1293 '\n'] 1294 with open(bbappendfile, 'r') as f: 1295 self.assertEqual(expectedlines, f.readlines()) 1296 1297 # Check we can run it again and bbappend isn't modified 1298 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1299 with open(bbappendfile, 'r') as f: 1300 self.assertEqual(expectedlines, f.readlines()) 1301 # Drop new commit and check patch gets deleted 1302 result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir) 1303 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1304 self.assertNotExists(patchfile, 'Patch file not deleted') 1305 expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 1306 '\n'] 1307 with open(bbappendfile, 'r') as f: 1308 self.assertEqual(expectedlines2, f.readlines()) 1309 # Put commit back and check we can run it if layer isn't in bblayers.conf 1310 os.remove(bbappendfile) 1311 result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir) 1312 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) 1313 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) 1314 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1315 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output) 1316 self.assertExists(patchfile, 'Patch file not created (with disabled layer)') 1317 with open(bbappendfile, 'r') as f: 1318 self.assertEqual(expectedlines, f.readlines()) 1319 # Deleting isn't expected to work under these circumstances 1320 1321 def test_devtool_update_recipe_append_git(self): 1322 # Check preconditions 1323 testrecipe = 'mtd-utils-selftest' 1324 bb_vars = get_bb_vars(['FILE', 'SRC_URI', 'LAYERSERIES_CORENAMES'], testrecipe) 1325 recipefile = bb_vars['FILE'] 1326 src_uri = bb_vars['SRC_URI'] 1327 corenames = bb_vars['LAYERSERIES_CORENAMES'] 1328 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe) 1329 for entry in src_uri.split(): 1330 if entry.startswith('git://'): 1331 git_uri = entry 1332 break 1333 self._check_repo_status(os.path.dirname(recipefile), []) 1334 # First, modify a recipe 1335 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1336 tempsrcdir = os.path.join(tempdir, 'source') 1337 templayerdir = os.path.join(tempdir, 'layer') 1338 self.track_for_cleanup(tempdir) 1339 self.track_for_cleanup(self.workspacedir) 1340 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1341 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1342 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir)) 1343 # Check git repo 1344 self._check_src_repo(tempsrcdir) 1345 # Add a commit 1346 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir) 1347 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir) 1348 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe)) 1349 # Create a temporary layer 1350 os.makedirs(os.path.join(templayerdir, 'conf')) 1351 with open(os.path.join(templayerdir, 'conf', 'layer.conf'), 'w') as f: 1352 f.write('BBPATH .= ":${LAYERDIR}"\n') 1353 f.write('BBFILES += "${LAYERDIR}/recipes-*/*/*.bbappend"\n') 1354 f.write('BBFILE_COLLECTIONS += "oeselftesttemplayer"\n') 1355 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n') 1356 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n') 1357 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n') 1358 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "%s"\n' % corenames) 1359 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir) 1360 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir) 1361 # Create the bbappend 1362 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1363 self.assertNotIn('WARNING:', result.output) 1364 # Check recipe is still clean 1365 self._check_repo_status(os.path.dirname(recipefile), []) 1366 # Check bbappend was created 1367 splitpath = os.path.dirname(recipefile).split(os.sep) 1368 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1]) 1369 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir) 1370 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') 1371 1372 # Check bbappend contents 1373 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) 1374 expectedlines = set(['SRCREV = "%s"\n' % result.output, 1375 '\n', 1376 'SRC_URI = "%s"\n' % git_uri, 1377 '\n']) 1378 with open(bbappendfile, 'r') as f: 1379 self.assertEqual(expectedlines, set(f.readlines())) 1380 1381 # Check we can run it again and bbappend isn't modified 1382 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1383 with open(bbappendfile, 'r') as f: 1384 self.assertEqual(expectedlines, set(f.readlines())) 1385 # Drop new commit and check SRCREV changes 1386 result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir) 1387 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1388 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') 1389 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) 1390 expectedlines = set(['SRCREV = "%s"\n' % result.output, 1391 '\n', 1392 'SRC_URI = "%s"\n' % git_uri, 1393 '\n']) 1394 with open(bbappendfile, 'r') as f: 1395 self.assertEqual(expectedlines, set(f.readlines())) 1396 # Put commit back and check we can run it if layer isn't in bblayers.conf 1397 os.remove(bbappendfile) 1398 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir) 1399 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir) 1400 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) 1401 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1402 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output) 1403 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') 1404 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) 1405 expectedlines = set(['SRCREV = "%s"\n' % result.output, 1406 '\n', 1407 'SRC_URI = "%s"\n' % git_uri, 1408 '\n']) 1409 with open(bbappendfile, 'r') as f: 1410 self.assertEqual(expectedlines, set(f.readlines())) 1411 # Deleting isn't expected to work under these circumstances 1412 1413 def test_devtool_update_recipe_local_files(self): 1414 """Check that local source files are copied over instead of patched""" 1415 testrecipe = 'makedevs' 1416 recipefile = get_bb_var('FILE', testrecipe) 1417 # Setup srctree for modifying the recipe 1418 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1419 self.track_for_cleanup(tempdir) 1420 self.track_for_cleanup(self.workspacedir) 1421 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1422 # (don't bother with cleaning the recipe on teardown, we won't be 1423 # building it) 1424 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1425 # Check git repo 1426 self._check_src_repo(tempdir) 1427 # Try building just to ensure we haven't broken that 1428 bitbake("%s" % testrecipe) 1429 # Edit / commit local source 1430 runCmd('echo "/* Foobar */" >> makedevs.c', cwd=tempdir) 1431 runCmd('echo "Foo" > new-local', cwd=tempdir) 1432 runCmd('echo "Bar" > new-file', cwd=tempdir) 1433 runCmd('git add new-file', cwd=tempdir) 1434 runCmd('git commit -m "Add new file"', cwd=tempdir) 1435 runCmd('git add new-local', cwd=tempdir) 1436 runCmd('devtool update-recipe %s' % testrecipe) 1437 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1438 (' M', '.*/makedevs/makedevs.c$'), 1439 ('??', '.*/makedevs/new-local$'), 1440 ('??', '.*/makedevs/0001-Add-new-file.patch$')] 1441 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1442 # Now try to update recipe in another layer, so first, clean it 1443 runCmd('cd %s; git clean -fd .; git checkout .' % os.path.dirname(recipefile)) 1444 # Create a temporary layer and add it to bblayers.conf 1445 self._create_temp_layer(templayerdir, True, 'templayer') 1446 # Update recipe in templayer 1447 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1448 self.assertNotIn('WARNING:', result.output) 1449 # Check recipe is still clean 1450 self._check_repo_status(os.path.dirname(recipefile), []) 1451 splitpath = os.path.dirname(recipefile).split(os.sep) 1452 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1]) 1453 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir) 1454 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-new-file.patch') 1455 new_local_file = os.path.join(appenddir, testrecipe, 'new_local') 1456 local_file = os.path.join(appenddir, testrecipe, 'makedevs.c') 1457 self.assertExists(patchfile, 'Patch file 0001-Add-new-file.patch not created') 1458 self.assertExists(local_file, 'File makedevs.c not created') 1459 self.assertExists(patchfile, 'File new_local not created') 1460 1461 def _test_devtool_update_recipe_local_files_2(self): 1462 """Check local source files support when editing local files in Git""" 1463 testrecipe = 'devtool-test-local' 1464 recipefile = get_bb_var('FILE', testrecipe) 1465 recipedir = os.path.dirname(recipefile) 1466 result = runCmd('git status --porcelain .', cwd=recipedir) 1467 if result.output.strip(): 1468 self.fail('Recipe directory for %s contains uncommitted changes' % testrecipe) 1469 # Setup srctree for modifying the recipe 1470 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1471 self.track_for_cleanup(tempdir) 1472 self.track_for_cleanup(self.workspacedir) 1473 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1474 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1475 # Check git repo 1476 self._check_src_repo(tempdir) 1477 # Edit / commit local sources 1478 runCmd('echo "# Foobar" >> file1', cwd=tempdir) 1479 runCmd('git commit -am "Edit existing file"', cwd=tempdir) 1480 runCmd('git rm file2', cwd=tempdir) 1481 runCmd('git commit -m"Remove file"', cwd=tempdir) 1482 runCmd('echo "Foo" > new-local', cwd=tempdir) 1483 runCmd('git add new-local', cwd=tempdir) 1484 runCmd('git commit -m "Add new local file"', cwd=tempdir) 1485 runCmd('echo "Gar" > new-file', cwd=tempdir) 1486 runCmd('git add new-file', cwd=tempdir) 1487 runCmd('git commit -m "Add new file"', cwd=tempdir) 1488 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' % 1489 os.path.dirname(recipefile)) 1490 # Checkout unmodified file to working copy -> devtool should still pick 1491 # the modified version from HEAD 1492 runCmd('git checkout HEAD^ -- file1', cwd=tempdir) 1493 runCmd('devtool update-recipe %s' % testrecipe) 1494 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1495 (' M', '.*/file1$'), 1496 (' D', '.*/file2$'), 1497 ('??', '.*/new-local$'), 1498 ('??', '.*/0001-Add-new-file.patch$')] 1499 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1500 1501 def test_devtool_update_recipe_with_gitignore(self): 1502 # First, modify the recipe 1503 testrecipe = 'devtool-test-ignored' 1504 bb_vars = get_bb_vars(['FILE'], testrecipe) 1505 recipefile = bb_vars['FILE'] 1506 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch') 1507 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch.expected') 1508 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1509 self.track_for_cleanup(tempdir) 1510 self.track_for_cleanup(self.workspacedir) 1511 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1512 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1513 result = runCmd('devtool modify %s' % testrecipe) 1514 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1515 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe) 1516 # Check recipe got changed as expected 1517 with open(newpatchfile, 'r') as f: 1518 desiredlines = f.readlines() 1519 with open(patchfile, 'r') as f: 1520 newlines = f.readlines() 1521 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo 1522 # which changes the metadata subject which is added into the patch, but keep 1523 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh 1524 # devtool-test-ignored manually, then it should generate exactly the same .patch file 1525 self.assertEqual(desiredlines[5:], newlines[5:]) 1526 1527 def test_devtool_update_recipe_long_filename(self): 1528 # First, modify the recipe 1529 testrecipe = 'devtool-test-long-filename' 1530 bb_vars = get_bb_vars(['FILE'], testrecipe) 1531 recipefile = bb_vars['FILE'] 1532 patchfilename = '0001-I-ll-patch-you-only-if-devtool-lets-me-to-do-it-corr.patch' 1533 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename) 1534 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename + '.expected') 1535 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1536 self.track_for_cleanup(tempdir) 1537 self.track_for_cleanup(self.workspacedir) 1538 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1539 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1540 result = runCmd('devtool modify %s' % testrecipe) 1541 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1542 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe) 1543 # Check recipe got changed as expected 1544 with open(newpatchfile, 'r') as f: 1545 desiredlines = f.readlines() 1546 with open(patchfile, 'r') as f: 1547 newlines = f.readlines() 1548 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo 1549 # which changes the metadata subject which is added into the patch, but keep 1550 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh 1551 # devtool-test-ignored manually, then it should generate exactly the same .patch file 1552 self.assertEqual(desiredlines[5:], newlines[5:]) 1553 1554 def test_devtool_update_recipe_local_files_3(self): 1555 # First, modify the recipe 1556 testrecipe = 'devtool-test-localonly' 1557 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1558 recipefile = bb_vars['FILE'] 1559 src_uri = bb_vars['SRC_URI'] 1560 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1561 self.track_for_cleanup(tempdir) 1562 self.track_for_cleanup(self.workspacedir) 1563 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1564 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1565 result = runCmd('devtool modify %s' % testrecipe) 1566 # Modify one file 1567 runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe)) 1568 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1569 result = runCmd('devtool update-recipe %s' % testrecipe) 1570 expected_status = [(' M', '.*/%s/file2$' % testrecipe)] 1571 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1572 1573 def test_devtool_update_recipe_local_patch_gz(self): 1574 # First, modify the recipe 1575 testrecipe = 'devtool-test-patch-gz' 1576 if get_bb_var('DISTRO') == 'poky-tiny': 1577 self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe) 1578 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1579 recipefile = bb_vars['FILE'] 1580 src_uri = bb_vars['SRC_URI'] 1581 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1582 self.track_for_cleanup(tempdir) 1583 self.track_for_cleanup(self.workspacedir) 1584 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1585 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1586 result = runCmd('devtool modify %s' % testrecipe) 1587 # Modify one file 1588 srctree = os.path.join(self.workspacedir, 'sources', testrecipe) 1589 runCmd('echo "Another line" >> README', cwd=srctree) 1590 runCmd('git commit -a --amend --no-edit --no-verify', cwd=srctree) 1591 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1592 result = runCmd('devtool update-recipe %s' % testrecipe) 1593 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)] 1594 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1595 patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz') 1596 result = runCmd('file %s' % patch_gz) 1597 if 'gzip compressed data' not in result.output: 1598 self.fail('New patch file is not gzipped - file reports:\n%s' % result.output) 1599 1600 def test_devtool_update_recipe_local_files_subdir(self): 1601 # Try devtool update-recipe on a recipe that has a file with subdir= set in 1602 # SRC_URI such that it overwrites a file that was in an archive that 1603 # was also in SRC_URI 1604 # First, modify the recipe 1605 testrecipe = 'devtool-test-subdir' 1606 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1607 recipefile = bb_vars['FILE'] 1608 src_uri = bb_vars['SRC_URI'] 1609 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1610 self.track_for_cleanup(tempdir) 1611 self.track_for_cleanup(self.workspacedir) 1612 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1613 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1614 result = runCmd('devtool modify %s' % testrecipe) 1615 testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile') 1616 self.assertExists(testfile, 'Extracted source could not be found') 1617 with open(testfile, 'r') as f: 1618 contents = f.read().rstrip() 1619 self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been') 1620 # Test devtool update-recipe without modifying any files 1621 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1622 result = runCmd('devtool update-recipe %s' % testrecipe) 1623 expected_status = [] 1624 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1625 1626 def test_devtool_finish_modify_git_subdir(self): 1627 # Check preconditions 1628 testrecipe = 'dos2unix' 1629 self.append_config('ERROR_QA:remove:pn-dos2unix = "patch-status"\n') 1630 bb_vars = get_bb_vars(['SRC_URI', 'S', 'WORKDIR', 'FILE'], testrecipe) 1631 self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe) 1632 workdir_git = '%s/git/' % bb_vars['WORKDIR'] 1633 if not bb_vars['S'].startswith(workdir_git): 1634 self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe) 1635 subdir = bb_vars['S'].split(workdir_git, 1)[1] 1636 # Clean up anything in the workdir/sysroot/sstate cache 1637 bitbake('%s -c cleansstate' % testrecipe) 1638 # Try modifying a recipe 1639 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1640 self.track_for_cleanup(tempdir) 1641 self.track_for_cleanup(self.workspacedir) 1642 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 1643 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1644 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1645 testsrcfile = os.path.join(tempdir, subdir, 'dos2unix.c') 1646 self.assertExists(testsrcfile, 'Extracted source could not be found') 1647 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output) 1648 self.assertNotExists(os.path.join(tempdir, subdir, '.git'), 'Subdirectory has been initialised as a git repo') 1649 # Check git repo 1650 self._check_src_repo(tempdir) 1651 # Modify file 1652 runCmd("sed -i '1s:^:/* Add a comment */\\n:' %s" % testsrcfile) 1653 result = runCmd('git commit -a -m "Add a comment"', cwd=tempdir) 1654 # Now try updating original recipe 1655 recipefile = bb_vars['FILE'] 1656 recipedir = os.path.dirname(recipefile) 1657 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe)) 1658 result = runCmd('devtool update-recipe %s' % testrecipe) 1659 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1660 ('??', '.*/%s/%s/$' % (testrecipe, testrecipe))] 1661 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1662 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile)) 1663 removelines = ['SRC_URI = "git://.*"'] 1664 addlines = [ 1665 'SRC_URI = "git://.* \\\\', 1666 'file://0001-Add-a-comment.patch;patchdir=.. \\\\', 1667 '"' 1668 ] 1669 self._check_diff(result.output, addlines, removelines) 1670 # Put things back so we can run devtool finish on a different layer 1671 runCmd('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe)) 1672 # Run devtool finish 1673 res = re.search('recipes-.*', recipedir) 1674 self.assertTrue(res, 'Unable to find recipe subdirectory') 1675 recipesubdir = res[0] 1676 self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, recipesubdir)) 1677 result = runCmd('devtool finish %s meta-selftest' % testrecipe) 1678 # Check bbappend file contents 1679 appendfn = os.path.join(self.testlayer_path, recipesubdir, '%s_%%.bbappend' % testrecipe) 1680 with open(appendfn, 'r') as f: 1681 appendlines = f.readlines() 1682 expected_appendlines = [ 1683 'FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 1684 '\n', 1685 'SRC_URI += "file://0001-Add-a-comment.patch;patchdir=.."\n', 1686 '\n' 1687 ] 1688 self.assertEqual(appendlines, expected_appendlines) 1689 self.assertExists(os.path.join(os.path.dirname(appendfn), testrecipe, '0001-Add-a-comment.patch')) 1690 # Try building 1691 bitbake('%s -c patch' % testrecipe) 1692 1693 def test_devtool_git_submodules(self): 1694 # This tests if we can add a patch in a git submodule and extract it properly using devtool finish 1695 # Check preconditions 1696 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1697 self.track_for_cleanup(self.workspacedir) 1698 recipe = 'vulkan-samples' 1699 src_uri = get_bb_var('SRC_URI', recipe) 1700 self.assertIn('gitsm://', src_uri, 'This test expects the %s recipe to be a git recipe with submodules' % recipe) 1701 oldrecipefile = get_bb_var('FILE', recipe) 1702 recipedir = os.path.dirname(oldrecipefile) 1703 result = runCmd('git status --porcelain .', cwd=recipedir) 1704 if result.output.strip(): 1705 self.fail('Recipe directory for %s contains uncommitted changes' % recipe) 1706 self.assertIn('/meta/', recipedir) 1707 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1708 self.track_for_cleanup(tempdir) 1709 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1710 result = runCmd('devtool modify %s %s' % (recipe, tempdir)) 1711 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found') 1712 # Test devtool status 1713 result = runCmd('devtool status') 1714 self.assertIn(recipe, result.output) 1715 self.assertIn(tempdir, result.output) 1716 # Modify a source file in a submodule, (grab the first one) 1717 result = runCmd('git submodule --quiet foreach \'echo $sm_path\'', cwd=tempdir) 1718 submodule = result.output.splitlines()[0] 1719 submodule_path = os.path.join(tempdir, submodule) 1720 runCmd('echo "#This is a first comment" >> testfile', cwd=submodule_path) 1721 result = runCmd('git status --porcelain . ', cwd=submodule_path) 1722 self.assertIn("testfile", result.output) 1723 runCmd('git add testfile; git commit -m "Adding a new file"', cwd=submodule_path) 1724 1725 # Try finish to the original layer 1726 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 1727 runCmd('devtool finish -f %s meta' % recipe) 1728 result = runCmd('devtool status') 1729 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 1730 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 1731 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)), 1732 ('??', '.*/.*-Adding-a-new-file.patch$')] 1733 self._check_repo_status(recipedir, expected_status) 1734 # Make sure the patch is added to the recipe with the correct "patchdir" option 1735 result = runCmd('git diff .', cwd=recipedir) 1736 addlines = [ 1737 'file://0001-Adding-a-new-file.patch;patchdir=%s \\\\' % submodule 1738 ] 1739 self._check_diff(result.output, addlines, []) 1740 1741class DevtoolExtractTests(DevtoolBase): 1742 1743 def test_devtool_extract(self): 1744 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1745 # Try devtool extract 1746 self.track_for_cleanup(tempdir) 1747 self.track_for_cleanup(self.workspacedir) 1748 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1749 result = runCmd('devtool extract matchbox-terminal %s' % tempdir) 1750 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') 1751 self._check_src_repo(tempdir) 1752 1753 def test_devtool_extract_virtual(self): 1754 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1755 # Try devtool extract 1756 self.track_for_cleanup(tempdir) 1757 self.track_for_cleanup(self.workspacedir) 1758 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1759 result = runCmd('devtool extract virtual/make %s' % tempdir) 1760 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') 1761 self._check_src_repo(tempdir) 1762 1763class DevtoolResetTests(DevtoolBase): 1764 1765 def test_devtool_reset_all(self): 1766 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1767 self.track_for_cleanup(tempdir) 1768 self.track_for_cleanup(self.workspacedir) 1769 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1770 testrecipe1 = 'mdadm' 1771 testrecipe2 = 'cronie' 1772 result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1))) 1773 result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2))) 1774 result = runCmd('devtool build %s' % testrecipe1) 1775 result = runCmd('devtool build %s' % testrecipe2) 1776 stampprefix1 = get_bb_var('STAMP', testrecipe1) 1777 self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1) 1778 stampprefix2 = get_bb_var('STAMP', testrecipe2) 1779 self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2) 1780 result = runCmd('devtool reset -a') 1781 self.assertIn(testrecipe1, result.output) 1782 self.assertIn(testrecipe2, result.output) 1783 result = runCmd('devtool status') 1784 self.assertNotIn(testrecipe1, result.output) 1785 self.assertNotIn(testrecipe2, result.output) 1786 matches1 = glob.glob(stampprefix1 + '*') 1787 self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1) 1788 matches2 = glob.glob(stampprefix2 + '*') 1789 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2) 1790 1791 def test_devtool_reset_re_plus_plus(self): 1792 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1793 self.track_for_cleanup(tempdir) 1794 self.track_for_cleanup(self.workspacedir) 1795 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1796 testrecipe = 'devtool-test-reset-re++' 1797 result = runCmd('devtool modify %s' % testrecipe) 1798 result = runCmd('devtool reset -n %s' % testrecipe) 1799 self.assertIn(testrecipe, result.output) 1800 result = runCmd('devtool status') 1801 self.assertNotIn(testrecipe, result.output) 1802 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', testrecipe), 'Recipe directory should not exist after resetting') 1803 1804class DevtoolDeployTargetTests(DevtoolBase): 1805 1806 @OETestTag("runqemu") 1807 def test_devtool_deploy_target(self): 1808 self._check_runqemu_prerequisites() 1809 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1810 # Definitions 1811 testrecipe = 'mdadm' 1812 testfile = '/sbin/mdadm' 1813 if "usrmerge" in get_bb_var('DISTRO_FEATURES'): 1814 testfile = '/usr/sbin/mdadm' 1815 testimage = 'oe-selftest-image' 1816 testcommand = '/sbin/mdadm --help' 1817 # Build an image to run 1818 bitbake("%s qemu-native qemu-helper-native" % testimage) 1819 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 1820 self.add_command_to_tearDown('bitbake -c clean %s' % testimage) 1821 self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage)) 1822 # Clean recipe so the first deploy will fail 1823 bitbake("%s -c clean" % testrecipe) 1824 # Try devtool modify 1825 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1826 self.track_for_cleanup(tempdir) 1827 self.track_for_cleanup(self.workspacedir) 1828 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 1829 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1830 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1831 # Test that deploy-target at this point fails (properly) 1832 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe, ignore_status=True) 1833 self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output) 1834 self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output) 1835 result = runCmd('devtool build %s' % testrecipe) 1836 # First try a dry-run of deploy-target 1837 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe) 1838 self.assertIn(' %s' % testfile, result.output) 1839 # Boot the image 1840 with runqemu(testimage) as qemu: 1841 # Now really test deploy-target 1842 result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, qemu.ip)) 1843 # Run a test command to see if it was installed properly 1844 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' 1845 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand)) 1846 # Check if it deployed all of the files with the right ownership/perms 1847 # First look on the host - need to do this under pseudo to get the correct ownership/perms 1848 bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe) 1849 installdir = bb_vars['D'] 1850 fakerootenv = bb_vars['FAKEROOTENV'] 1851 fakerootcmd = bb_vars['FAKEROOTCMD'] 1852 result = runCmd('%s %s find . -type f -exec ls -l {} \\;' % (fakerootenv, fakerootcmd), cwd=installdir) 1853 filelist1 = self._process_ls_output(result.output) 1854 1855 # Now look on the target 1856 tempdir2 = tempfile.mkdtemp(prefix='devtoolqa') 1857 self.track_for_cleanup(tempdir2) 1858 tmpfilelist = os.path.join(tempdir2, 'files.txt') 1859 with open(tmpfilelist, 'w') as f: 1860 for line in filelist1: 1861 splitline = line.split() 1862 f.write(splitline[-1] + '\n') 1863 result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip)) 1864 filelist2 = self._process_ls_output(result.output) 1865 filelist1.sort(key=lambda item: item.split()[-1]) 1866 filelist2.sort(key=lambda item: item.split()[-1]) 1867 self.assertEqual(filelist1, filelist2) 1868 # Test undeploy-target 1869 result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip)) 1870 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True) 1871 self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') 1872 1873class DevtoolBuildImageTests(DevtoolBase): 1874 1875 def test_devtool_build_image(self): 1876 """Test devtool build-image plugin""" 1877 # Check preconditions 1878 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1879 image = 'core-image-minimal' 1880 self.track_for_cleanup(self.workspacedir) 1881 self.add_command_to_tearDown('bitbake -c clean %s' % image) 1882 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1883 bitbake('%s -c clean' % image) 1884 # Add target and native recipes to workspace 1885 recipes = ['mdadm', 'parted-native'] 1886 for recipe in recipes: 1887 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1888 self.track_for_cleanup(tempdir) 1889 self.add_command_to_tearDown('bitbake -c clean %s' % recipe) 1890 runCmd('devtool modify %s -x %s' % (recipe, tempdir)) 1891 # Try to build image 1892 result = runCmd('devtool build-image %s' % image) 1893 self.assertNotEqual(result, 0, 'devtool build-image failed') 1894 # Check if image contains expected packages 1895 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 1896 image_link_name = get_bb_var('IMAGE_LINK_NAME', image) 1897 reqpkgs = [item for item in recipes if not item.endswith('-native')] 1898 with open(os.path.join(deploy_dir_image, image_link_name + '.manifest'), 'r') as f: 1899 for line in f: 1900 splitval = line.split() 1901 if splitval: 1902 pkg = splitval[0] 1903 if pkg in reqpkgs: 1904 reqpkgs.remove(pkg) 1905 if reqpkgs: 1906 self.fail('The following packages were not present in the image as expected: %s' % ', '.join(reqpkgs)) 1907 1908class DevtoolUpgradeTests(DevtoolBase): 1909 1910 def setUp(self): 1911 super().setUp() 1912 try: 1913 runCmd("git config --global user.name") 1914 runCmd("git config --global user.email") 1915 except: 1916 self.skip("Git user.name and user.email must be set") 1917 1918 def test_devtool_upgrade(self): 1919 # Check preconditions 1920 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1921 self.track_for_cleanup(self.workspacedir) 1922 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1923 # Check parameters 1924 result = runCmd('devtool upgrade -h') 1925 for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split(): 1926 self.assertIn(param, result.output) 1927 # For the moment, we are using a real recipe. 1928 recipe = 'devtool-upgrade-test1' 1929 version = '1.6.0' 1930 oldrecipefile = get_bb_var('FILE', recipe) 1931 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1932 self.track_for_cleanup(tempdir) 1933 # Check that recipe is not already under devtool control 1934 result = runCmd('devtool status') 1935 self.assertNotIn(recipe, result.output) 1936 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that 1937 # we are downgrading instead of upgrading. 1938 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version)) 1939 # Check if srctree at least is populated 1940 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, version)) 1941 # Check new recipe subdirectory is present 1942 self.assertExists(os.path.join(self.workspacedir, 'recipes', recipe, '%s-%s' % (recipe, version)), 'Recipe folder should exist') 1943 # Check new recipe file is present 1944 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version)) 1945 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') 1946 # Check devtool status and make sure recipe is present 1947 result = runCmd('devtool status') 1948 self.assertIn(recipe, result.output) 1949 self.assertIn(tempdir, result.output) 1950 # Check recipe got changed as expected 1951 with open(oldrecipefile + '.upgraded', 'r') as f: 1952 desiredlines = f.readlines() 1953 with open(newrecipefile, 'r') as f: 1954 newlines = f.readlines() 1955 self.assertEqual(desiredlines, newlines) 1956 # Check devtool reset recipe 1957 result = runCmd('devtool reset %s -n' % recipe) 1958 result = runCmd('devtool status') 1959 self.assertNotIn(recipe, result.output) 1960 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') 1961 1962 def test_devtool_upgrade_git(self): 1963 # Check preconditions 1964 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1965 self.track_for_cleanup(self.workspacedir) 1966 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1967 recipe = 'devtool-upgrade-test2' 1968 commit = '6cc6077a36fe2648a5f993fe7c16c9632f946517' 1969 oldrecipefile = get_bb_var('FILE', recipe) 1970 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1971 self.track_for_cleanup(tempdir) 1972 # Check that recipe is not already under devtool control 1973 result = runCmd('devtool status') 1974 self.assertNotIn(recipe, result.output) 1975 # Check upgrade 1976 result = runCmd('devtool upgrade %s %s -S %s' % (recipe, tempdir, commit)) 1977 # Check if srctree at least is populated 1978 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit)) 1979 # Check new recipe file is present 1980 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile)) 1981 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') 1982 # Check devtool status and make sure recipe is present 1983 result = runCmd('devtool status') 1984 self.assertIn(recipe, result.output) 1985 self.assertIn(tempdir, result.output) 1986 # Check recipe got changed as expected 1987 with open(oldrecipefile + '.upgraded', 'r') as f: 1988 desiredlines = f.readlines() 1989 with open(newrecipefile, 'r') as f: 1990 newlines = f.readlines() 1991 self.assertEqual(desiredlines, newlines) 1992 # Check devtool reset recipe 1993 result = runCmd('devtool reset %s -n' % recipe) 1994 result = runCmd('devtool status') 1995 self.assertNotIn(recipe, result.output) 1996 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') 1997 1998 def test_devtool_upgrade_drop_md5sum(self): 1999 # Check preconditions 2000 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2001 self.track_for_cleanup(self.workspacedir) 2002 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2003 # For the moment, we are using a real recipe. 2004 recipe = 'devtool-upgrade-test3' 2005 version = '1.6.0' 2006 oldrecipefile = get_bb_var('FILE', recipe) 2007 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2008 self.track_for_cleanup(tempdir) 2009 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that 2010 # we are downgrading instead of upgrading. 2011 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version)) 2012 # Check new recipe file is present 2013 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version)) 2014 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') 2015 # Check recipe got changed as expected 2016 with open(oldrecipefile + '.upgraded', 'r') as f: 2017 desiredlines = f.readlines() 2018 with open(newrecipefile, 'r') as f: 2019 newlines = f.readlines() 2020 self.assertEqual(desiredlines, newlines) 2021 2022 def test_devtool_upgrade_all_checksums(self): 2023 # Check preconditions 2024 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2025 self.track_for_cleanup(self.workspacedir) 2026 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2027 # For the moment, we are using a real recipe. 2028 recipe = 'devtool-upgrade-test4' 2029 version = '1.6.0' 2030 oldrecipefile = get_bb_var('FILE', recipe) 2031 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2032 self.track_for_cleanup(tempdir) 2033 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that 2034 # we are downgrading instead of upgrading. 2035 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version)) 2036 # Check new recipe file is present 2037 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version)) 2038 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') 2039 # Check recipe got changed as expected 2040 with open(oldrecipefile + '.upgraded', 'r') as f: 2041 desiredlines = f.readlines() 2042 with open(newrecipefile, 'r') as f: 2043 newlines = f.readlines() 2044 self.assertEqual(desiredlines, newlines) 2045 2046 def test_devtool_upgrade_recipe_upgrade_extra_tasks(self): 2047 # Check preconditions 2048 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2049 self.track_for_cleanup(self.workspacedir) 2050 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2051 recipe = 'python3-guessing-game' 2052 version = '0.2.0' 2053 commit = '40cf004c2772ffa20ea803fa3be1528a75be3e98' 2054 oldrecipefile = get_bb_var('FILE', recipe) 2055 oldcratesincfile = os.path.join(os.path.dirname(oldrecipefile), os.path.basename(oldrecipefile).strip('_git.bb') + '-crates.inc') 2056 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2057 self.track_for_cleanup(tempdir) 2058 # Check that recipe is not already under devtool control 2059 result = runCmd('devtool status') 2060 self.assertNotIn(recipe, result.output) 2061 # Check upgrade 2062 result = runCmd('devtool upgrade %s %s --version %s --srcrev %s' % (recipe, tempdir, version, commit)) 2063 # Check if srctree at least is populated 2064 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit)) 2065 # Check new recipe file and new -crates.inc files are present 2066 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile)) 2067 newcratesincfile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldcratesincfile)) 2068 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') 2069 self.assertExists(newcratesincfile, 'Recipe crates.inc file should exist after upgrade') 2070 # Check devtool status and make sure recipe is present 2071 result = runCmd('devtool status') 2072 self.assertIn(recipe, result.output) 2073 self.assertIn(tempdir, result.output) 2074 # Check recipe got changed as expected 2075 with open(oldrecipefile + '.upgraded', 'r') as f: 2076 desiredlines = f.readlines() 2077 with open(newrecipefile, 'r') as f: 2078 newlines = f.readlines() 2079 self.assertEqual(desiredlines, newlines) 2080 # Check crates.inc got changed as expected 2081 with open(oldcratesincfile + '.upgraded', 'r') as f: 2082 desiredlines = f.readlines() 2083 with open(newcratesincfile, 'r') as f: 2084 newlines = f.readlines() 2085 self.assertEqual(desiredlines, newlines) 2086 # Check devtool reset recipe 2087 result = runCmd('devtool reset %s -n' % recipe) 2088 result = runCmd('devtool status') 2089 self.assertNotIn(recipe, result.output) 2090 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') 2091 2092 def test_devtool_layer_plugins(self): 2093 """Test that devtool can use plugins from other layers. 2094 2095 This test executes the selftest-reverse command from meta-selftest.""" 2096 2097 self.track_for_cleanup(self.workspacedir) 2098 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2099 2100 s = "Microsoft Made No Profit From Anyone's Zunes Yo" 2101 result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s) 2102 self.assertEqual(result.output, s[::-1]) 2103 2104 def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths): 2105 dstdir = basedstdir 2106 self.assertExists(dstdir) 2107 for p in paths: 2108 dstdir = os.path.join(dstdir, p) 2109 if not os.path.exists(dstdir): 2110 try: 2111 os.makedirs(dstdir) 2112 except PermissionError: 2113 return False 2114 except OSError as e: 2115 if e.errno == errno.EROFS: 2116 return False 2117 else: 2118 raise e 2119 if p == "lib": 2120 # Can race with other tests 2121 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir) 2122 else: 2123 self.track_for_cleanup(dstdir) 2124 dstfile = os.path.join(dstdir, os.path.basename(srcfile)) 2125 if srcfile != dstfile: 2126 try: 2127 shutil.copy(srcfile, dstfile) 2128 except PermissionError: 2129 return False 2130 self.track_for_cleanup(dstfile) 2131 return True 2132 2133 def test_devtool_load_plugin(self): 2134 """Test that devtool loads only the first found plugin in BBPATH.""" 2135 2136 self.track_for_cleanup(self.workspacedir) 2137 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2138 2139 devtool = runCmd("which devtool") 2140 fromname = runCmd("devtool --quiet pluginfile") 2141 srcfile = fromname.output 2142 bbpath = get_bb_var('BBPATH') 2143 searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)] 2144 plugincontent = [] 2145 with open(srcfile) as fh: 2146 plugincontent = fh.readlines() 2147 try: 2148 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found') 2149 searchpath = [ 2150 path for path in searchpath 2151 if self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool') 2152 ] 2153 result = runCmd("devtool --quiet count") 2154 self.assertEqual(result.output, '1') 2155 result = runCmd("devtool --quiet multiloaded") 2156 self.assertEqual(result.output, "no") 2157 for path in searchpath: 2158 result = runCmd("devtool --quiet bbdir") 2159 self.assertEqual(os.path.realpath(result.output), os.path.realpath(path)) 2160 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py')) 2161 finally: 2162 with open(srcfile, 'w') as fh: 2163 fh.writelines(plugincontent) 2164 2165 def _setup_test_devtool_finish_upgrade(self): 2166 # Check preconditions 2167 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2168 self.track_for_cleanup(self.workspacedir) 2169 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2170 # Use a "real" recipe from meta-selftest 2171 recipe = 'devtool-upgrade-test1' 2172 oldversion = '1.5.3' 2173 newversion = '1.6.0' 2174 oldrecipefile = get_bb_var('FILE', recipe) 2175 recipedir = os.path.dirname(oldrecipefile) 2176 result = runCmd('git status --porcelain .', cwd=recipedir) 2177 if result.output.strip(): 2178 self.fail('Recipe directory for %s contains uncommitted changes' % recipe) 2179 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2180 self.track_for_cleanup(tempdir) 2181 # Check that recipe is not already under devtool control 2182 result = runCmd('devtool status') 2183 self.assertNotIn(recipe, result.output) 2184 # Do the upgrade 2185 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion)) 2186 # Check devtool status and make sure recipe is present 2187 result = runCmd('devtool status') 2188 self.assertIn(recipe, result.output) 2189 self.assertIn(tempdir, result.output) 2190 # Make a change to the source 2191 result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir) 2192 result = runCmd('git status --porcelain', cwd=tempdir) 2193 self.assertIn('M src/pv/number.c', result.output) 2194 result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir) 2195 # Check if patch is there 2196 recipedir = os.path.dirname(oldrecipefile) 2197 olddir = os.path.join(recipedir, recipe + '-' + oldversion) 2198 patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch' 2199 backportedpatchfn = 'backported.patch' 2200 self.assertExists(os.path.join(olddir, patchfn), 'Original patch file does not exist') 2201 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Backported patch file does not exist') 2202 return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn 2203 2204 def test_devtool_finish_upgrade_origlayer(self): 2205 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade() 2206 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 2207 self.assertIn('/meta-selftest/', recipedir) 2208 # Try finish to the original layer 2209 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 2210 result = runCmd('devtool finish %s meta-selftest' % recipe) 2211 result = runCmd('devtool status') 2212 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 2213 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 2214 self.assertNotExists(oldrecipefile, 'Old recipe file should have been deleted but wasn\'t') 2215 self.assertNotExists(os.path.join(olddir, patchfn), 'Old patch file should have been deleted but wasn\'t') 2216 self.assertNotExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should have been deleted but wasn\'t') 2217 newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion)) 2218 newdir = os.path.join(recipedir, recipe + '-' + newversion) 2219 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t') 2220 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t') 2221 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was') 2222 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t') 2223 with open(newrecipefile, 'r') as f: 2224 newcontent = f.read() 2225 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't") 2226 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was") 2227 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't") 2228 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI") 2229 2230 2231 def test_devtool_finish_upgrade_otherlayer(self): 2232 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade() 2233 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 2234 self.assertIn('/meta-selftest/', recipedir) 2235 # Try finish to a different layer - should create a bbappend 2236 # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here 2237 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 2238 oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta') 2239 newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool') 2240 newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion)) 2241 self.track_for_cleanup(newrecipedir) 2242 result = runCmd('devtool finish %s oe-core' % recipe) 2243 result = runCmd('devtool status') 2244 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 2245 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 2246 self.assertExists(oldrecipefile, 'Old recipe file should not have been deleted') 2247 self.assertExists(os.path.join(olddir, patchfn), 'Old patch file should not have been deleted') 2248 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should not have been deleted') 2249 newdir = os.path.join(newrecipedir, recipe + '-' + newversion) 2250 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t') 2251 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t') 2252 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was') 2253 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t') 2254 with open(newrecipefile, 'r') as f: 2255 newcontent = f.read() 2256 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't") 2257 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was") 2258 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't") 2259 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI") 2260 2261 def _setup_test_devtool_finish_modify(self): 2262 # Check preconditions 2263 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2264 # Try modifying a recipe 2265 self.track_for_cleanup(self.workspacedir) 2266 recipe = 'mdadm' 2267 oldrecipefile = get_bb_var('FILE', recipe) 2268 recipedir = os.path.dirname(oldrecipefile) 2269 result = runCmd('git status --porcelain .', cwd=recipedir) 2270 if result.output.strip(): 2271 self.fail('Recipe directory for %s contains uncommitted changes' % recipe) 2272 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2273 self.track_for_cleanup(tempdir) 2274 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2275 result = runCmd('devtool modify %s %s' % (recipe, tempdir)) 2276 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found') 2277 # Test devtool status 2278 result = runCmd('devtool status') 2279 self.assertIn(recipe, result.output) 2280 self.assertIn(tempdir, result.output) 2281 # Make a change to the source 2282 result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir) 2283 result = runCmd('git status --porcelain', cwd=tempdir) 2284 self.assertIn('M maps.c', result.output) 2285 result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir) 2286 for entry in os.listdir(recipedir): 2287 filesdir = os.path.join(recipedir, entry) 2288 if os.path.isdir(filesdir): 2289 break 2290 else: 2291 self.fail('Unable to find recipe files directory for %s' % recipe) 2292 return recipe, oldrecipefile, recipedir, filesdir 2293 2294 def test_devtool_finish_modify_origlayer(self): 2295 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify() 2296 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 2297 self.assertIn('/meta/', recipedir) 2298 # Try finish to the original layer 2299 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 2300 result = runCmd('devtool finish %s meta' % recipe) 2301 result = runCmd('devtool status') 2302 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 2303 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 2304 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)), 2305 ('??', '.*/.*-Add-a-comment-to-the-code.patch$')] 2306 self._check_repo_status(recipedir, expected_status) 2307 2308 def test_devtool_finish_modify_otherlayer(self): 2309 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify() 2310 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 2311 self.assertIn('/meta/', recipedir) 2312 relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta')) 2313 appenddir = os.path.join(get_test_layer(), relpth) 2314 self.track_for_cleanup(appenddir) 2315 # Try finish to the original layer 2316 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 2317 result = runCmd('devtool finish %s meta-selftest' % recipe) 2318 result = runCmd('devtool status') 2319 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 2320 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 2321 result = runCmd('git status --porcelain .', cwd=recipedir) 2322 if result.output.strip(): 2323 self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip())) 2324 recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0] 2325 recipefn = recipefn.split('_')[0] + '_%' 2326 appendfile = os.path.join(appenddir, recipefn + '.bbappend') 2327 self.assertExists(appendfile, 'bbappend %s should have been created but wasn\'t' % appendfile) 2328 newdir = os.path.join(appenddir, recipe) 2329 files = os.listdir(newdir) 2330 foundpatch = None 2331 for fn in files: 2332 if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'): 2333 foundpatch = fn 2334 if not foundpatch: 2335 self.fail('No patch file created next to bbappend') 2336 files.remove(foundpatch) 2337 if files: 2338 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files)) 2339 2340 def test_devtool_finish_update_patch(self): 2341 # This test uses a modified version of the sysdig recipe from meta-oe. 2342 # - The patches have been renamed. 2343 # - The dependencies are commented out since the recipe is not being 2344 # built. 2345 # 2346 # The sysdig recipe is interesting in that it fetches two different Git 2347 # repositories, and there are patches for both. This leads to that 2348 # devtool will create ignore commits as it uses Git submodules to keep 2349 # track of the second repository. 2350 # 2351 # This test will verify that the ignored commits actually are ignored 2352 # when a commit in between is modified. It will also verify that the 2353 # updated patch keeps its original name. 2354 2355 # Check preconditions 2356 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2357 # Try modifying a recipe 2358 self.track_for_cleanup(self.workspacedir) 2359 recipe = 'sysdig-selftest' 2360 recipefile = get_bb_var('FILE', recipe) 2361 recipedir = os.path.dirname(recipefile) 2362 result = runCmd('git status --porcelain .', cwd=recipedir) 2363 if result.output.strip(): 2364 self.fail('Recipe directory for %s contains uncommitted changes' % recipe) 2365 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2366 self.track_for_cleanup(tempdir) 2367 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2368 result = runCmd('devtool modify %s %s' % (recipe, tempdir)) 2369 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (recipedir, recipe, recipe, os.path.basename(recipefile))) 2370 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found') 2371 # Make a change to one of the existing commits 2372 result = runCmd('echo "# A comment " >> CMakeLists.txt', cwd=tempdir) 2373 result = runCmd('git status --porcelain', cwd=tempdir) 2374 self.assertIn('M CMakeLists.txt', result.output) 2375 result = runCmd('git commit --fixup HEAD^ CMakeLists.txt', cwd=tempdir) 2376 result = runCmd('git show -s --format=%s', cwd=tempdir) 2377 self.assertIn('fixup! cmake: Pass PROBE_NAME via CFLAGS', result.output) 2378 result = runCmd('GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash devtool-base', cwd=tempdir) 2379 result = runCmd('devtool finish %s meta-selftest' % recipe) 2380 result = runCmd('devtool status') 2381 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 2382 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 2383 expected_status = [(' M', '.*/0099-cmake-Pass-PROBE_NAME-via-CFLAGS.patch$')] 2384 self._check_repo_status(recipedir, expected_status) 2385 2386 def test_devtool_rename(self): 2387 # Check preconditions 2388 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 2389 self.track_for_cleanup(self.workspacedir) 2390 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2391 2392 # First run devtool add 2393 # We already have this recipe in OE-Core, but that doesn't matter 2394 recipename = 'i2c-tools' 2395 recipever = '3.1.2' 2396 recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever)) 2397 url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever 2398 def add_recipe(): 2399 result = runCmd('devtool add %s' % url) 2400 self.assertExists(recipefile, 'Expected recipe file not created') 2401 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory not created') 2402 checkvars = {} 2403 checkvars['S'] = None 2404 checkvars['SRC_URI'] = url.replace(recipever, '${PV}') 2405 self._test_recipe_contents(recipefile, checkvars, []) 2406 add_recipe() 2407 # Now rename it - change both name and version 2408 newrecipename = 'mynewrecipe' 2409 newrecipever = '456' 2410 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever)) 2411 result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever)) 2412 self.assertExists(newrecipefile, 'Recipe file not renamed') 2413 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists') 2414 newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename) 2415 self.assertExists(newsrctree, 'Source directory not renamed') 2416 checkvars = {} 2417 checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever) 2418 checkvars['SRC_URI'] = url 2419 self._test_recipe_contents(newrecipefile, checkvars, []) 2420 # Try again - change just name this time 2421 result = runCmd('devtool reset -n %s' % newrecipename) 2422 add_recipe() 2423 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever)) 2424 result = runCmd('devtool rename %s %s' % (recipename, newrecipename)) 2425 self.assertExists(newrecipefile, 'Recipe file not renamed') 2426 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists') 2427 self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed') 2428 checkvars = {} 2429 checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename 2430 checkvars['SRC_URI'] = url.replace(recipever, '${PV}') 2431 self._test_recipe_contents(newrecipefile, checkvars, []) 2432 # Try again - change just version this time 2433 result = runCmd('devtool reset -n %s' % newrecipename) 2434 add_recipe() 2435 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever)) 2436 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever)) 2437 self.assertExists(newrecipefile, 'Recipe file not renamed') 2438 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists') 2439 checkvars = {} 2440 checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever 2441 checkvars['SRC_URI'] = url 2442 self._test_recipe_contents(newrecipefile, checkvars, []) 2443 2444 def test_devtool_virtual_kernel_modify(self): 2445 """ 2446 Summary: The purpose of this test case is to verify that 2447 devtool modify works correctly when building 2448 the kernel. 2449 Dependencies: NA 2450 Steps: 1. Build kernel with bitbake. 2451 2. Save the config file generated. 2452 3. Clean the environment. 2453 4. Use `devtool modify virtual/kernel` to validate following: 2454 4.1 The source is checked out correctly. 2455 4.2 The resulting configuration is the same as 2456 what was get on step 2. 2457 4.3 The Kernel can be build correctly. 2458 4.4 Changes made on the source are reflected on the 2459 subsequent builds. 2460 4.5 Changes on the configuration are reflected on the 2461 subsequent builds 2462 Expected: devtool modify is able to checkout the source of the kernel 2463 and modification to the source and configurations are reflected 2464 when building the kernel. 2465 """ 2466 kernel_provider = self.td['PREFERRED_PROVIDER_virtual/kernel'] 2467 2468 # Clean up the environment 2469 bitbake('%s -c clean' % kernel_provider) 2470 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2471 tempdir_cfg = tempfile.mkdtemp(prefix='config_qa') 2472 self.track_for_cleanup(tempdir) 2473 self.track_for_cleanup(tempdir_cfg) 2474 self.track_for_cleanup(self.workspacedir) 2475 self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider) 2476 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2477 #Step 1 2478 #Here is just generated the config file instead of all the kernel to optimize the 2479 #time of executing this test case. 2480 bitbake('%s -c configure' % kernel_provider) 2481 bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config') 2482 #Step 2 2483 runCmd('cp %s %s' % (bbconfig, tempdir_cfg)) 2484 self.assertExists(os.path.join(tempdir_cfg, '.config'), 'Could not copy .config file from kernel') 2485 2486 tmpconfig = os.path.join(tempdir_cfg, '.config') 2487 #Step 3 2488 bitbake('%s -c clean' % kernel_provider) 2489 #Step 4.1 2490 runCmd('devtool modify virtual/kernel -x %s' % tempdir) 2491 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found') 2492 #Step 4.2 2493 configfile = os.path.join(tempdir,'.config') 2494 runCmd('diff %s %s' % (tmpconfig, configfile)) 2495 2496 #Step 4.3 2497 #NOTE: virtual/kernel is mapped to kernel_provider 2498 runCmd('devtool build %s' % kernel_provider) 2499 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux') 2500 self.assertExists(kernelfile, 'Kernel was not build correctly') 2501 2502 #Modify the kernel source 2503 modfile = os.path.join(tempdir, 'init/version.c') 2504 # Moved to uts.h in 6.1 onwards 2505 modfile2 = os.path.join(tempdir, 'include/linux/uts.h') 2506 runCmd("sed -i 's/Linux/LiNuX/g' %s %s" % (modfile, modfile2)) 2507 2508 #Modify the configuration 2509 codeconfigfile = os.path.join(tempdir, '.config.new') 2510 modconfopt = "CONFIG_SG_POOL=n" 2511 runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile)) 2512 2513 #Build again kernel with devtool 2514 runCmd('devtool build %s' % kernel_provider) 2515 2516 #Step 4.4 2517 runCmd("grep '%s' %s" % ('LiNuX', kernelfile)) 2518 2519 #Step 4.5 2520 runCmd("grep %s %s" % (modconfopt, codeconfigfile)) 2521 2522 2523class DevtoolIdeSdkTests(DevtoolBase): 2524 def _write_bb_config(self, recipe_names): 2525 """Helper to write the bitbake local.conf file""" 2526 conf_lines = [ 2527 'IMAGE_CLASSES += "image-combined-dbg"', 2528 'IMAGE_GEN_DEBUGFS = "1"', 2529 'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join( 2530 [r + '-ptest' for r in recipe_names]) 2531 ] 2532 self.write_config("\n".join(conf_lines)) 2533 2534 def _check_workspace(self): 2535 """Check if a workspace directory is available and setup the cleanup""" 2536 self.assertTrue(not os.path.exists(self.workspacedir), 2537 'This test cannot be run with a workspace directory under the build directory') 2538 self.track_for_cleanup(self.workspacedir) 2539 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 2540 2541 def _workspace_scripts_dir(self, recipe_name): 2542 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts')) 2543 2544 def _sources_scripts_dir(self, src_dir): 2545 return os.path.realpath(os.path.join(src_dir, 'oe-scripts')) 2546 2547 def _workspace_gdbinit_dir(self, recipe_name): 2548 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts', 'gdbinit')) 2549 2550 def _sources_gdbinit_dir(self, src_dir): 2551 return os.path.realpath(os.path.join(src_dir, 'oe-gdbinit')) 2552 2553 def _devtool_ide_sdk_recipe(self, recipe_name, build_file, testimage): 2554 """Setup a recipe for working with devtool ide-sdk 2555 2556 Basically devtool modify -x followed by some tests 2557 """ 2558 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 2559 self.track_for_cleanup(tempdir) 2560 self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name) 2561 2562 result = runCmd('devtool modify %s -x %s --debug-build' % (recipe_name, tempdir)) 2563 self.assertExists(os.path.join(tempdir, build_file), 2564 'Extracted source could not be found') 2565 self.assertExists(os.path.join(self.workspacedir, 'conf', 2566 'layer.conf'), 'Workspace directory not created') 2567 matches = glob.glob(os.path.join(self.workspacedir, 2568 'appends', recipe_name + '.bbappend')) 2569 self.assertTrue(matches, 'bbappend not created %s' % result.output) 2570 2571 # Test devtool status 2572 result = runCmd('devtool status') 2573 self.assertIn(recipe_name, result.output) 2574 self.assertIn(tempdir, result.output) 2575 self._check_src_repo(tempdir) 2576 2577 # Usually devtool ide-sdk would initiate the build of the SDK. 2578 # But there is a circular dependency with starting Qemu and passing the IP of runqemu to devtool ide-sdk. 2579 if testimage: 2580 bitbake("%s qemu-native qemu-helper-native" % testimage) 2581 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 2582 self.add_command_to_tearDown('bitbake -c clean %s' % testimage) 2583 self.add_command_to_tearDown( 2584 'rm -f %s/%s*' % (deploy_dir_image, testimage)) 2585 2586 return tempdir 2587 2588 def _get_recipe_ids(self, recipe_name): 2589 """IDs needed to write recipe specific config entries into IDE config files""" 2590 package_arch = get_bb_var('PACKAGE_ARCH', recipe_name) 2591 recipe_id = recipe_name + "-" + package_arch 2592 recipe_id_pretty = recipe_name + ": " + package_arch 2593 return (recipe_id, recipe_id_pretty) 2594 2595 def _verify_install_script_code(self, tempdir, recipe_name): 2596 """Verify the scripts referred by the tasks.json file are fine. 2597 2598 This function does not depend on Qemu. Therefore it verifies the scripts 2599 exists and the delete step works as expected. But it does not try to 2600 deploy to Qemu. 2601 """ 2602 recipe_id, recipe_id_pretty = self._get_recipe_ids(recipe_name) 2603 with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j: 2604 tasks_d = json.load(tasks_j) 2605 tasks = tasks_d["tasks"] 2606 task_install = next( 2607 (task for task in tasks if task["label"] == "install && deploy-target %s" % recipe_id_pretty), None) 2608 self.assertIsNot(task_install, None) 2609 # execute only the bb_run_do_install script since the deploy would require e.g. Qemu running. 2610 i_and_d_script = "install_and_deploy_" + recipe_id 2611 i_and_d_script_path = os.path.join( 2612 self._workspace_scripts_dir(recipe_name), i_and_d_script) 2613 self.assertExists(i_and_d_script_path) 2614 2615 def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe): 2616 """Verify deployment and execution in Qemu system work for one recipe. 2617 2618 This function checks the entire SDK workflow: changing the code, recompiling 2619 it and deploying it back to Qemu, and checking that the changes have been 2620 incorporated into the provided binaries. It also runs the tests of the recipe. 2621 """ 2622 recipe_id, _ = self._get_recipe_ids(recipe_name) 2623 i_and_d_script = "install_and_deploy_" + recipe_id 2624 install_deploy_cmd = os.path.join( 2625 self._workspace_scripts_dir(recipe_name), i_and_d_script) 2626 self.assertExists(install_deploy_cmd, 2627 '%s script not found' % install_deploy_cmd) 2628 runCmd(install_deploy_cmd) 2629 2630 MAGIC_STRING_ORIG = "Magic: 123456789" 2631 MAGIC_STRING_NEW = "Magic: 987654321" 2632 ptest_cmd = "ptest-runner " + recipe_name 2633 2634 # validate that SSH is working 2635 status, _ = qemu.run("uname") 2636 self.assertEqual( 2637 status, 0, msg="Failed to connect to the SSH server on Qemu") 2638 2639 # Verify the unmodified example prints the magic string 2640 status, output = qemu.run(example_exe) 2641 self.assertEqual(status, 0, msg="%s failed: %s" % 2642 (example_exe, output)) 2643 self.assertIn(MAGIC_STRING_ORIG, output) 2644 2645 # Verify the unmodified ptests work 2646 status, output = qemu.run(ptest_cmd) 2647 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output)) 2648 self.assertIn("PASS: cpp-example-lib", output) 2649 2650 # Verify remote debugging works 2651 self._gdb_cross_debugging( 2652 qemu, recipe_name, example_exe, MAGIC_STRING_ORIG) 2653 2654 # Replace the Magic String in the code, compile and deploy to Qemu 2655 cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp') 2656 with open(cpp_example_lib_hpp, 'r') as file: 2657 cpp_code = file.read() 2658 cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW) 2659 with open(cpp_example_lib_hpp, 'w') as file: 2660 file.write(cpp_code) 2661 runCmd(install_deploy_cmd, cwd=tempdir) 2662 2663 # Verify the modified example prints the modified magic string 2664 status, output = qemu.run(example_exe) 2665 self.assertEqual(status, 0, msg="%s failed: %s" % 2666 (example_exe, output)) 2667 self.assertNotIn(MAGIC_STRING_ORIG, output) 2668 self.assertIn(MAGIC_STRING_NEW, output) 2669 2670 # Verify the modified example ptests work 2671 status, output = qemu.run(ptest_cmd) 2672 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output)) 2673 self.assertIn("PASS: cpp-example-lib", output) 2674 2675 # Verify remote debugging works wit the modified magic string 2676 self._gdb_cross_debugging( 2677 qemu, recipe_name, example_exe, MAGIC_STRING_NEW) 2678 2679 def _gdb_cross(self): 2680 """Verify gdb-cross is provided by devtool ide-sdk""" 2681 target_arch = self.td["TARGET_ARCH"] 2682 target_sys = self.td["TARGET_SYS"] 2683 gdb_recipe = "gdb-cross-" + target_arch 2684 gdb_binary = target_sys + "-gdb" 2685 2686 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe) 2687 r = runCmd("%s --version" % gdb_binary, 2688 native_sysroot=native_sysroot, target_sys=target_sys) 2689 self.assertEqual(r.status, 0) 2690 self.assertIn("GNU gdb", r.output) 2691 2692 def _gdb_cross_debugging(self, qemu, recipe_name, example_exe, magic_string): 2693 """Verify gdb-cross is working 2694 2695 Test remote debugging: 2696 break main 2697 run 2698 continue 2699 break CppExample::print_json() 2700 continue 2701 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789") 2702 $1 = 0 2703 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789aaa") 2704 $2 = -3 2705 list cpp-example-lib.hpp:13,13 2706 13 inline static const std::string test_string = "cpp-example-lib Magic: 123456789"; 2707 continue 2708 """ 2709 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' 2710 gdbserver_script = os.path.join(self._workspace_scripts_dir( 2711 recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_m') 2712 gdb_script = os.path.join(self._workspace_scripts_dir( 2713 recipe_name), 'gdb_1234_usr-bin-' + example_exe) 2714 2715 # Start a gdbserver 2716 r = runCmd(gdbserver_script) 2717 self.assertEqual(r.status, 0) 2718 2719 # Check there is a gdbserver running 2720 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps')) 2721 self.assertEqual(r.status, 0) 2722 self.assertIn("gdbserver ", r.output) 2723 2724 # Check the pid file is correct 2725 test_cmd = "cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \ 2726 example_exe + "/pid)/cmdline" 2727 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd)) 2728 self.assertEqual(r.status, 0) 2729 self.assertIn("gdbserver", r.output) 2730 2731 # Test remote debugging works 2732 gdb_batch_cmd = " --batch -ex 'break main' -ex 'run'" 2733 gdb_batch_cmd += " -ex 'break CppExample::print_json()' -ex 'continue'" 2734 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %s\")'" % magic_string 2735 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %saaa\")'" % magic_string 2736 gdb_batch_cmd += " -ex 'list cpp-example-lib.hpp:13,13'" 2737 gdb_batch_cmd += " -ex 'continue'" 2738 r = runCmd(gdb_script + gdb_batch_cmd) 2739 self.logger.debug("%s %s returned: %s", gdb_script, 2740 gdb_batch_cmd, r.output) 2741 self.assertEqual(r.status, 0) 2742 self.assertIn("Breakpoint 1, main", r.output) 2743 self.assertIn("$1 = 0", r.output) # test.string.compare equal 2744 self.assertIn("$2 = -3", r.output) # test.string.compare longer 2745 self.assertIn( 2746 'inline static const std::string test_string = "cpp-example-lib %s";' % magic_string, r.output) 2747 self.assertIn("exited normally", r.output) 2748 2749 # Stop the gdbserver 2750 r = runCmd(gdbserver_script + ' stop') 2751 self.assertEqual(r.status, 0) 2752 2753 # Check there is no gdbserver running 2754 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps')) 2755 self.assertEqual(r.status, 0) 2756 self.assertNotIn("gdbserver ", r.output) 2757 2758 def _verify_cmake_preset(self, tempdir): 2759 """Verify the generated cmake preset works as expected 2760 2761 Check if compiling works 2762 Check if unit tests can be executed in qemu (not qemu-system) 2763 """ 2764 with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j: 2765 cmake_preset_d = json.load(cmake_preset_j) 2766 config_presets = cmake_preset_d["configurePresets"] 2767 self.assertEqual(len(config_presets), 1) 2768 cmake_exe = config_presets[0]["cmakeExecutable"] 2769 preset_name = config_presets[0]["name"] 2770 2771 # Verify the wrapper for cmake native is available 2772 self.assertExists(cmake_exe) 2773 2774 # Verify the cmake preset generated by devtool ide-sdk is available 2775 result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir) 2776 self.assertIn(preset_name, result.output) 2777 2778 # Verify cmake re-uses the o files compiled by bitbake 2779 result = runCmd('%s --build --preset %s' % 2780 (cmake_exe, preset_name), cwd=tempdir) 2781 self.assertIn("ninja: no work to do.", result.output) 2782 2783 # Verify the unit tests work (in Qemu user mode) 2784 result = runCmd('%s --build --preset %s --target test' % 2785 (cmake_exe, preset_name), cwd=tempdir) 2786 self.assertIn("100% tests passed", result.output) 2787 2788 # Verify re-building and testing works again 2789 result = runCmd('%s --build --preset %s --target clean' % 2790 (cmake_exe, preset_name), cwd=tempdir) 2791 self.assertIn("Cleaning", result.output) 2792 result = runCmd('%s --build --preset %s' % 2793 (cmake_exe, preset_name), cwd=tempdir) 2794 self.assertIn("Building", result.output) 2795 self.assertIn("Linking", result.output) 2796 result = runCmd('%s --build --preset %s --target test' % 2797 (cmake_exe, preset_name), cwd=tempdir) 2798 self.assertIn("Running tests...", result.output) 2799 self.assertIn("100% tests passed", result.output) 2800 2801 @OETestTag("runqemu") 2802 def test_devtool_ide_sdk_none_qemu(self): 2803 """Start qemu-system and run tests for multiple recipes. ide=none is used.""" 2804 recipe_names = ["cmake-example", "meson-example"] 2805 testimage = "oe-selftest-image" 2806 2807 self._check_workspace() 2808 self._write_bb_config(recipe_names) 2809 self._check_runqemu_prerequisites() 2810 2811 # Verify deployment to Qemu (system mode) works 2812 bitbake(testimage) 2813 with runqemu(testimage, runqemuparams="nographic") as qemu: 2814 # cmake-example recipe 2815 recipe_name = "cmake-example" 2816 example_exe = "cmake-example" 2817 build_file = "CMakeLists.txt" 2818 tempdir = self._devtool_ide_sdk_recipe( 2819 recipe_name, build_file, testimage) 2820 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % ( 2821 recipe_name, testimage, qemu.ip) 2822 runCmd(bitbake_sdk_cmd) 2823 self._gdb_cross() 2824 self._verify_cmake_preset(tempdir) 2825 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe) 2826 # Verify the oe-scripts sym-link is valid 2827 self.assertEqual(self._workspace_scripts_dir( 2828 recipe_name), self._sources_scripts_dir(tempdir)) 2829 2830 # meson-example recipe 2831 recipe_name = "meson-example" 2832 example_exe = "mesonex" 2833 build_file = "meson.build" 2834 tempdir = self._devtool_ide_sdk_recipe( 2835 recipe_name, build_file, testimage) 2836 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % ( 2837 recipe_name, testimage, qemu.ip) 2838 runCmd(bitbake_sdk_cmd) 2839 self._gdb_cross() 2840 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe) 2841 # Verify the oe-scripts sym-link is valid 2842 self.assertEqual(self._workspace_scripts_dir( 2843 recipe_name), self._sources_scripts_dir(tempdir)) 2844 2845 def test_devtool_ide_sdk_code_cmake(self): 2846 """Verify a cmake recipe works with ide=code mode""" 2847 recipe_name = "cmake-example" 2848 build_file = "CMakeLists.txt" 2849 testimage = "oe-selftest-image" 2850 2851 self._check_workspace() 2852 self._write_bb_config([recipe_name]) 2853 tempdir = self._devtool_ide_sdk_recipe( 2854 recipe_name, build_file, testimage) 2855 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % ( 2856 recipe_name, testimage) 2857 runCmd(bitbake_sdk_cmd) 2858 self._verify_cmake_preset(tempdir) 2859 self._verify_install_script_code(tempdir, recipe_name) 2860 self._gdb_cross() 2861 2862 def test_devtool_ide_sdk_code_meson(self): 2863 """Verify a meson recipe works with ide=code mode""" 2864 recipe_name = "meson-example" 2865 build_file = "meson.build" 2866 testimage = "oe-selftest-image" 2867 2868 self._check_workspace() 2869 self._write_bb_config([recipe_name]) 2870 tempdir = self._devtool_ide_sdk_recipe( 2871 recipe_name, build_file, testimage) 2872 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % ( 2873 recipe_name, testimage) 2874 runCmd(bitbake_sdk_cmd) 2875 2876 with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j: 2877 settings_d = json.load(settings_j) 2878 meson_exe = settings_d["mesonbuild.mesonPath"] 2879 meson_build_folder = settings_d["mesonbuild.buildFolder"] 2880 2881 # Verify the wrapper for meson native is available 2882 self.assertExists(meson_exe) 2883 2884 # Verify meson re-uses the o files compiled by bitbake 2885 result = runCmd('%s compile -C %s' % 2886 (meson_exe, meson_build_folder), cwd=tempdir) 2887 self.assertIn("ninja: no work to do.", result.output) 2888 2889 # Verify the unit tests work (in Qemu) 2890 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) 2891 2892 # Verify re-building and testing works again 2893 result = runCmd('%s compile -C %s --clean' % 2894 (meson_exe, meson_build_folder), cwd=tempdir) 2895 self.assertIn("Cleaning...", result.output) 2896 result = runCmd('%s compile -C %s' % 2897 (meson_exe, meson_build_folder), cwd=tempdir) 2898 self.assertIn("Linking target", result.output) 2899 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) 2900 2901 self._verify_install_script_code(tempdir, recipe_name) 2902 self._gdb_cross() 2903 2904 def test_devtool_ide_sdk_shared_sysroots(self): 2905 """Verify the shared sysroot SDK""" 2906 2907 # Handle the workspace (which is not needed by this test case) 2908 self._check_workspace() 2909 2910 result_init = runCmd( 2911 'devtool ide-sdk -m shared oe-selftest-image cmake-example meson-example --ide=code') 2912 bb_vars = get_bb_vars( 2913 ['REAL_MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE'], "meta-ide-support") 2914 environment_script = 'environment-setup-%s' % bb_vars['REAL_MULTIMACH_TARGET_SYS'] 2915 deploydir = bb_vars['DEPLOY_DIR_IMAGE'] 2916 environment_script_path = os.path.join(deploydir, environment_script) 2917 cpp_example_src = os.path.join( 2918 bb_vars['COREBASE'], 'meta-selftest', 'recipes-test', 'cpp', 'files') 2919 2920 # Verify the cross environment script is available 2921 self.assertExists(environment_script_path) 2922 2923 def runCmdEnv(cmd, cwd): 2924 cmd = '/bin/sh -c ". %s > /dev/null && %s"' % ( 2925 environment_script_path, cmd) 2926 return runCmd(cmd, cwd) 2927 2928 # Verify building the C++ example works with CMake 2929 tempdir_cmake = tempfile.mkdtemp(prefix='devtoolqa') 2930 self.track_for_cleanup(tempdir_cmake) 2931 2932 result_cmake = runCmdEnv("which cmake", cwd=tempdir_cmake) 2933 cmake_native = os.path.normpath(result_cmake.output.strip()) 2934 self.assertExists(cmake_native) 2935 2936 runCmdEnv('cmake %s' % cpp_example_src, cwd=tempdir_cmake) 2937 runCmdEnv('cmake --build %s' % tempdir_cmake, cwd=tempdir_cmake) 2938 2939 # Verify the printed note really referres to a cmake executable 2940 cmake_native_code = "" 2941 for line in result_init.output.splitlines(): 2942 m = re.search(r'"cmake.cmakePath": "(.*)"', line) 2943 if m: 2944 cmake_native_code = m.group(1) 2945 break 2946 self.assertExists(cmake_native_code) 2947 self.assertEqual(cmake_native, cmake_native_code) 2948 2949 # Verify building the C++ example works with Meson 2950 tempdir_meson = tempfile.mkdtemp(prefix='devtoolqa') 2951 self.track_for_cleanup(tempdir_meson) 2952 2953 result_cmake = runCmdEnv("which meson", cwd=tempdir_meson) 2954 meson_native = os.path.normpath(result_cmake.output.strip()) 2955 self.assertExists(meson_native) 2956 2957 runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src) 2958 runCmdEnv('meson compile', cwd=tempdir_meson) 2959 2960 def test_devtool_ide_sdk_plugins(self): 2961 """Test that devtool ide-sdk can use plugins from other layers.""" 2962 2963 # We need a workspace layer and a modified recipe (but no image) 2964 modified_recipe_name = "meson-example" 2965 modified_build_file = "meson.build" 2966 testimage = "oe-selftest-image" 2967 shared_recipe_name = "cmake-example" 2968 2969 self._check_workspace() 2970 self._write_bb_config([modified_recipe_name]) 2971 tempdir = self._devtool_ide_sdk_recipe( 2972 modified_recipe_name, modified_build_file, None) 2973 2974 IDE_RE = re.compile(r'.*--ide \{(.*)\}.*') 2975 2976 def get_ides_from_help(help_str): 2977 m = IDE_RE.search(help_str) 2978 return m.group(1).split(',') 2979 2980 # verify the default plugins are available but the foo plugin is not 2981 result = runCmd('devtool ide-sdk -h') 2982 found_ides = get_ides_from_help(result.output) 2983 self.assertIn('code', found_ides) 2984 self.assertIn('none', found_ides) 2985 self.assertNotIn('foo', found_ides) 2986 2987 shared_config_file = os.path.join(tempdir, 'shared-config.txt') 2988 shared_config_str = 'Dummy shared IDE config' 2989 modified_config_file = os.path.join(tempdir, 'modified-config.txt') 2990 modified_config_str = 'Dummy modified IDE config' 2991 2992 # Generate a foo plugin in the workspace layer 2993 plugin_dir = os.path.join( 2994 self.workspacedir, 'lib', 'devtool', 'ide_plugins') 2995 os.makedirs(plugin_dir) 2996 plugin_code = 'from devtool.ide_plugins import IdeBase\n\n' 2997 plugin_code += 'class IdeFoo(IdeBase):\n' 2998 plugin_code += ' def setup_shared_sysroots(self, shared_env):\n' 2999 plugin_code += ' with open("%s", "w") as config_file:\n' % shared_config_file 3000 plugin_code += ' config_file.write("%s")\n\n' % shared_config_str 3001 plugin_code += ' def setup_modified_recipe(self, args, image_recipe, modified_recipe):\n' 3002 plugin_code += ' with open("%s", "w") as config_file:\n' % modified_config_file 3003 plugin_code += ' config_file.write("%s")\n\n' % modified_config_str 3004 plugin_code += 'def register_ide_plugin(ide_plugins):\n' 3005 plugin_code += ' ide_plugins["foo"] = IdeFoo\n' 3006 3007 plugin_py = os.path.join(plugin_dir, 'ide_foo.py') 3008 with open(plugin_py, 'w') as plugin_file: 3009 plugin_file.write(plugin_code) 3010 3011 # Verify the foo plugin is available as well 3012 result = runCmd('devtool ide-sdk -h') 3013 found_ides = get_ides_from_help(result.output) 3014 self.assertIn('code', found_ides) 3015 self.assertIn('none', found_ides) 3016 self.assertIn('foo', found_ides) 3017 3018 # Verify the foo plugin generates a shared config 3019 result = runCmd( 3020 'devtool ide-sdk -m shared --skip-bitbake --ide foo %s' % shared_recipe_name) 3021 with open(shared_config_file) as shared_config: 3022 shared_config_new = shared_config.read() 3023 self.assertEqual(shared_config_str, shared_config_new) 3024 3025 # Verify the foo plugin generates a modified config 3026 result = runCmd('devtool ide-sdk --skip-bitbake --ide foo %s %s' % 3027 (modified_recipe_name, testimage)) 3028 with open(modified_config_file) as modified_config: 3029 modified_config_new = modified_config.read() 3030 self.assertEqual(modified_config_str, modified_config_new) 3031