1# 2# SPDX-License-Identifier: MIT 3# 4 5import os 6import re 7import shutil 8import tempfile 9import glob 10import fnmatch 11 12from oeqa.selftest.case import OESelftestTestCase 13from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer 14from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer 15from oeqa.core.decorator import OETestTag 16 17oldmetapath = None 18 19def setUpModule(): 20 import bb.utils 21 22 global templayerdir 23 templayerdir = tempfile.mkdtemp(prefix='devtoolqa') 24 corecopydir = os.path.join(templayerdir, 'core-copy') 25 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf') 26 edited_layers = [] 27 28 # We need to take a copy of the meta layer so we can modify it and not 29 # have any races against other tests that might be running in parallel 30 # however things like COREBASE mean that you can't just copy meta, you 31 # need the whole repository. 32 def bblayers_edit_cb(layerpath, canonical_layerpath): 33 global oldmetapath 34 if not canonical_layerpath.endswith('/'): 35 # This helps us match exactly when we're using this path later 36 canonical_layerpath += '/' 37 if not edited_layers and canonical_layerpath.endswith('/meta/'): 38 canonical_layerpath = os.path.realpath(canonical_layerpath) + '/' 39 edited_layers.append(layerpath) 40 oldmetapath = os.path.realpath(layerpath) 41 result = runCmd('git rev-parse --show-toplevel', cwd=canonical_layerpath) 42 oldreporoot = result.output.rstrip() 43 newmetapath = os.path.join(corecopydir, os.path.relpath(oldmetapath, oldreporoot)) 44 runCmd('git clone %s %s' % (oldreporoot, corecopydir), cwd=templayerdir) 45 # Now we need to copy any modified files 46 # You might ask "why not just copy the entire tree instead of 47 # cloning and doing this?" - well, the problem with that is 48 # TMPDIR or an equally large subdirectory might exist 49 # under COREBASE and we don't want to copy that, so we have 50 # to be selective. 51 result = runCmd('git status --porcelain', cwd=oldreporoot) 52 for line in result.output.splitlines(): 53 if line.startswith(' M ') or line.startswith('?? '): 54 relpth = line.split()[1] 55 pth = os.path.join(oldreporoot, relpth) 56 if pth.startswith(canonical_layerpath): 57 if relpth.endswith('/'): 58 destdir = os.path.join(corecopydir, relpth) 59 # avoid race condition by not copying .pyc files YPBZ#13421,13803 60 shutil.copytree(pth, destdir, ignore=shutil.ignore_patterns('*.pyc', '__pycache__')) 61 else: 62 destdir = os.path.join(corecopydir, os.path.dirname(relpth)) 63 bb.utils.mkdirhier(destdir) 64 shutil.copy2(pth, destdir) 65 return newmetapath 66 else: 67 return layerpath 68 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb) 69 70def tearDownModule(): 71 if oldmetapath: 72 edited_layers = [] 73 def bblayers_edit_cb(layerpath, canonical_layerpath): 74 if not edited_layers and canonical_layerpath.endswith('/meta'): 75 edited_layers.append(layerpath) 76 return oldmetapath 77 else: 78 return layerpath 79 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf') 80 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb) 81 shutil.rmtree(templayerdir) 82 83class DevtoolTestCase(OESelftestTestCase): 84 85 def setUp(self): 86 """Test case setup function""" 87 super(DevtoolTestCase, self).setUp() 88 self.workspacedir = os.path.join(self.builddir, 'workspace') 89 self.assertTrue(not os.path.exists(self.workspacedir), 90 'This test cannot be run with a workspace directory ' 91 'under the build directory') 92 93 def _check_src_repo(self, repo_dir): 94 """Check srctree git repository""" 95 self.assertTrue(os.path.isdir(os.path.join(repo_dir, '.git')), 96 'git repository for external source tree not found') 97 result = runCmd('git status --porcelain', cwd=repo_dir) 98 self.assertEqual(result.output.strip(), "", 99 'Created git repo is not clean') 100 result = runCmd('git symbolic-ref HEAD', cwd=repo_dir) 101 self.assertEqual(result.output.strip(), "refs/heads/devtool", 102 'Wrong branch in git repo') 103 104 def _check_repo_status(self, repo_dir, expected_status): 105 """Check the worktree status of a repository""" 106 result = runCmd('git status . --porcelain', 107 cwd=repo_dir) 108 for line in result.output.splitlines(): 109 for ind, (f_status, fn_re) in enumerate(expected_status): 110 if re.match(fn_re, line[3:]): 111 if f_status != line[:2]: 112 self.fail('Unexpected status in line: %s' % line) 113 expected_status.pop(ind) 114 break 115 else: 116 self.fail('Unexpected modified file in line: %s' % line) 117 if expected_status: 118 self.fail('Missing file changes: %s' % expected_status) 119 120 def _test_recipe_contents(self, recipefile, checkvars, checkinherits): 121 with open(recipefile, 'r') as f: 122 invar = None 123 invalue = None 124 inherits = set() 125 for line in f: 126 var = None 127 if invar: 128 value = line.strip().strip('"') 129 if value.endswith('\\'): 130 invalue += ' ' + value[:-1].strip() 131 continue 132 else: 133 invalue += ' ' + value.strip() 134 var = invar 135 value = invalue 136 invar = None 137 elif '=' in line: 138 splitline = line.split('=', 1) 139 var = splitline[0].rstrip() 140 value = splitline[1].strip().strip('"') 141 if value.endswith('\\'): 142 invalue = value[:-1].strip() 143 invar = var 144 continue 145 elif line.startswith('inherit '): 146 inherits.update(line.split()[1:]) 147 148 if var and var in checkvars: 149 needvalue = checkvars.pop(var) 150 if needvalue is None: 151 self.fail('Variable %s should not appear in recipe, but value is being set to "%s"' % (var, value)) 152 if isinstance(needvalue, set): 153 if var == 'LICENSE': 154 value = set(value.split(' & ')) 155 else: 156 value = set(value.split()) 157 self.assertEqual(value, needvalue, 'values for %s do not match' % var) 158 159 160 missingvars = {} 161 for var, value in checkvars.items(): 162 if value is not None: 163 missingvars[var] = value 164 self.assertEqual(missingvars, {}, 'Some expected variables not found in recipe: %s' % checkvars) 165 166 for inherit in checkinherits: 167 self.assertIn(inherit, inherits, 'Missing inherit of %s' % inherit) 168 169 def _check_bbappend(self, testrecipe, recipefile, appenddir): 170 result = runCmd('bitbake-layers show-appends', cwd=self.builddir) 171 resultlines = result.output.splitlines() 172 inrecipe = False 173 bbappends = [] 174 bbappendfile = None 175 for line in resultlines: 176 if inrecipe: 177 if line.startswith(' '): 178 bbappends.append(line.strip()) 179 else: 180 break 181 elif line == '%s:' % os.path.basename(recipefile): 182 inrecipe = True 183 self.assertLessEqual(len(bbappends), 2, '%s recipe is being bbappended by another layer - bbappends found:\n %s' % (testrecipe, '\n '.join(bbappends))) 184 for bbappend in bbappends: 185 if bbappend.startswith(appenddir): 186 bbappendfile = bbappend 187 break 188 else: 189 self.fail('bbappend for recipe %s does not seem to be created in test layer' % testrecipe) 190 return bbappendfile 191 192 def _create_temp_layer(self, templayerdir, addlayer, templayername, priority=999, recipepathspec='recipes-*/*'): 193 create_temp_layer(templayerdir, templayername, priority, recipepathspec) 194 if addlayer: 195 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir) 196 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir) 197 198 def _process_ls_output(self, output): 199 """ 200 Convert ls -l output to a format we can reasonably compare from one context 201 to another (e.g. from host to target) 202 """ 203 filelist = [] 204 for line in output.splitlines(): 205 splitline = line.split() 206 if len(splitline) < 8: 207 self.fail('_process_ls_output: invalid output line: %s' % line) 208 # Remove trailing . on perms 209 splitline[0] = splitline[0].rstrip('.') 210 # Remove leading . on paths 211 splitline[-1] = splitline[-1].lstrip('.') 212 # Drop fields we don't want to compare 213 del splitline[7] 214 del splitline[6] 215 del splitline[5] 216 del splitline[4] 217 del splitline[1] 218 filelist.append(' '.join(splitline)) 219 return filelist 220 221 def _check_diff(self, diffoutput, addlines, removelines): 222 """Check output from 'git diff' matches expectation""" 223 remaining_addlines = addlines[:] 224 remaining_removelines = removelines[:] 225 for line in diffoutput.splitlines(): 226 if line.startswith('+++') or line.startswith('---'): 227 continue 228 elif line.startswith('+'): 229 matched = False 230 for item in addlines: 231 if re.match(item, line[1:].strip()): 232 matched = True 233 remaining_addlines.remove(item) 234 break 235 self.assertTrue(matched, 'Unexpected diff add line: %s' % line) 236 elif line.startswith('-'): 237 matched = False 238 for item in removelines: 239 if re.match(item, line[1:].strip()): 240 matched = True 241 remaining_removelines.remove(item) 242 break 243 self.assertTrue(matched, 'Unexpected diff remove line: %s' % line) 244 if remaining_addlines: 245 self.fail('Expected added lines not found: %s' % remaining_addlines) 246 if remaining_removelines: 247 self.fail('Expected removed lines not found: %s' % remaining_removelines) 248 249 250class DevtoolBase(DevtoolTestCase): 251 252 @classmethod 253 def setUpClass(cls): 254 super(DevtoolBase, cls).setUpClass() 255 bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR']) 256 cls.original_sstate = bb_vars['SSTATE_DIR'] 257 cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool') 258 cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate 259 cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n' 260 % cls.original_sstate) 261 cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687"\n') 262 263 @classmethod 264 def tearDownClass(cls): 265 cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate) 266 runCmd('rm -rf %s' % cls.devtool_sstate) 267 super(DevtoolBase, cls).tearDownClass() 268 269 def setUp(self): 270 """Test case setup function""" 271 super(DevtoolBase, self).setUp() 272 self.append_config(self.sstate_conf) 273 274 275class DevtoolTests(DevtoolBase): 276 277 def test_create_workspace(self): 278 # Check preconditions 279 result = runCmd('bitbake-layers show-layers') 280 self.assertTrue('\nworkspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf') 281 # remove conf/devtool.conf to avoid it corrupting tests 282 devtoolconf = os.path.join(self.builddir, 'conf', 'devtool.conf') 283 self.track_for_cleanup(devtoolconf) 284 # Try creating a workspace layer with a specific path 285 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 286 self.track_for_cleanup(tempdir) 287 result = runCmd('devtool create-workspace %s' % tempdir) 288 self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output) 289 result = runCmd('bitbake-layers show-layers') 290 self.assertIn(tempdir, result.output) 291 # Try creating a workspace layer with the default path 292 self.track_for_cleanup(self.workspacedir) 293 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 294 result = runCmd('devtool create-workspace') 295 self.assertTrue(os.path.isfile(os.path.join(self.workspacedir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output) 296 result = runCmd('bitbake-layers show-layers') 297 self.assertNotIn(tempdir, result.output) 298 self.assertIn(self.workspacedir, result.output) 299 300class DevtoolAddTests(DevtoolBase): 301 302 def test_devtool_add(self): 303 # Fetch source 304 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 305 self.track_for_cleanup(tempdir) 306 pn = 'pv' 307 pv = '1.5.3' 308 url = 'http://downloads.yoctoproject.org/mirror/sources/pv-1.5.3.tar.bz2' 309 result = runCmd('wget %s' % url, cwd=tempdir) 310 result = runCmd('tar xfv %s' % os.path.basename(url), cwd=tempdir) 311 srcdir = os.path.join(tempdir, '%s-%s' % (pn, pv)) 312 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory') 313 # Test devtool add 314 self.track_for_cleanup(self.workspacedir) 315 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn) 316 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 317 result = runCmd('devtool add %s %s' % (pn, srcdir)) 318 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') 319 # Test devtool status 320 result = runCmd('devtool status') 321 recipepath = '%s/recipes/%s/%s_%s.bb' % (self.workspacedir, pn, pn, pv) 322 self.assertIn(recipepath, result.output) 323 self.assertIn(srcdir, result.output) 324 # Test devtool find-recipe 325 result = runCmd('devtool -q find-recipe %s' % pn) 326 self.assertEqual(recipepath, result.output.strip()) 327 # Test devtool edit-recipe 328 result = runCmd('VISUAL="echo 123" devtool -q edit-recipe %s' % pn) 329 self.assertEqual('123 %s' % recipepath, result.output.strip()) 330 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then) 331 bitbake('%s -c cleansstate' % pn) 332 # Test devtool build 333 result = runCmd('devtool build %s' % pn) 334 bb_vars = get_bb_vars(['D', 'bindir'], pn) 335 installdir = bb_vars['D'] 336 self.assertTrue(installdir, 'Could not query installdir variable') 337 bindir = bb_vars['bindir'] 338 self.assertTrue(bindir, 'Could not query bindir variable') 339 if bindir[0] == '/': 340 bindir = bindir[1:] 341 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D') 342 343 def test_devtool_add_git_local(self): 344 # We need dbus built so that DEPENDS recognition works 345 bitbake('dbus') 346 # Fetch source from a remote URL, but do it outside of devtool 347 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 348 self.track_for_cleanup(tempdir) 349 pn = 'dbus-wait' 350 srcrev = '6cc6077a36fe2648a5f993fe7c16c9632f946517' 351 # We choose an https:// git URL here to check rewriting the URL works 352 url = 'https://git.yoctoproject.org/git/dbus-wait' 353 # Force fetching to "noname" subdir so we verify we're picking up the name from autoconf 354 # instead of the directory name 355 result = runCmd('git clone %s noname' % url, cwd=tempdir) 356 srcdir = os.path.join(tempdir, 'noname') 357 result = runCmd('git reset --hard %s' % srcrev, cwd=srcdir) 358 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory') 359 # Test devtool add 360 self.track_for_cleanup(self.workspacedir) 361 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 362 # Don't specify a name since we should be able to auto-detect it 363 result = runCmd('devtool add %s' % srcdir) 364 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') 365 # Check the recipe name is correct 366 recipefile = get_bb_var('FILE', pn) 367 self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named') 368 self.assertIn(recipefile, result.output) 369 # Test devtool status 370 result = runCmd('devtool status') 371 self.assertIn(pn, result.output) 372 self.assertIn(srcdir, result.output) 373 self.assertIn(recipefile, result.output) 374 checkvars = {} 375 checkvars['LICENSE'] = 'GPL-2.0-only' 376 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' 377 checkvars['S'] = '${WORKDIR}/git' 378 checkvars['PV'] = '0.1+git${SRCPV}' 379 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https;branch=master' 380 checkvars['SRCREV'] = srcrev 381 checkvars['DEPENDS'] = set(['dbus']) 382 self._test_recipe_contents(recipefile, checkvars, []) 383 384 def test_devtool_add_library(self): 385 # Fetch source 386 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 387 self.track_for_cleanup(tempdir) 388 version = '1.1' 389 url = 'https://www.intra2net.com/en/developer/libftdi/download/libftdi1-%s.tar.bz2' % version 390 result = runCmd('wget %s' % url, cwd=tempdir) 391 result = runCmd('tar xfv libftdi1-%s.tar.bz2' % version, cwd=tempdir) 392 srcdir = os.path.join(tempdir, 'libftdi1-%s' % version) 393 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory') 394 # Test devtool add (and use -V so we test that too) 395 self.track_for_cleanup(self.workspacedir) 396 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 397 result = runCmd('devtool add libftdi %s -V %s' % (srcdir, version)) 398 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') 399 # Test devtool status 400 result = runCmd('devtool status') 401 self.assertIn('libftdi', result.output) 402 self.assertIn(srcdir, result.output) 403 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then) 404 bitbake('libftdi -c cleansstate') 405 # libftdi's python/CMakeLists.txt is a bit broken, so let's just disable it 406 # There's also the matter of it installing cmake files to a path we don't 407 # normally cover, which triggers the installed-vs-shipped QA test we have 408 # within do_package 409 recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version) 410 result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile) 411 with open(recipefile, 'a') as f: 412 f.write('\nFILES:${PN}-dev += "${datadir}/cmake/Modules"\n') 413 # We don't have the ability to pick up this dependency automatically yet... 414 f.write('\nDEPENDS += "libusb1"\n') 415 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n') 416 # Test devtool build 417 result = runCmd('devtool build libftdi') 418 bb_vars = get_bb_vars(['TESTLIBOUTPUT', 'STAMP'], 'libftdi') 419 staging_libdir = bb_vars['TESTLIBOUTPUT'] 420 self.assertTrue(staging_libdir, 'Could not query TESTLIBOUTPUT variable') 421 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) 422 # Test devtool reset 423 stampprefix = bb_vars['STAMP'] 424 result = runCmd('devtool reset libftdi') 425 result = runCmd('devtool status') 426 self.assertNotIn('libftdi', result.output) 427 self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi') 428 matches = glob.glob(stampprefix + '*') 429 self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned') 430 self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning') 431 432 def test_devtool_add_fetch(self): 433 # Fetch source 434 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 435 self.track_for_cleanup(tempdir) 436 testver = '0.23' 437 url = 'https://files.pythonhosted.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-%s.tar.gz' % testver 438 testrecipe = 'python-markupsafe' 439 srcdir = os.path.join(tempdir, testrecipe) 440 # Test devtool add 441 self.track_for_cleanup(self.workspacedir) 442 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe) 443 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 444 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url)) 445 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output) 446 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory') 447 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created') 448 # Test devtool status 449 result = runCmd('devtool status') 450 self.assertIn(testrecipe, result.output) 451 self.assertIn(srcdir, result.output) 452 # Check recipe 453 recipefile = get_bb_var('FILE', testrecipe) 454 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named') 455 checkvars = {} 456 checkvars['S'] = '${WORKDIR}/MarkupSafe-${PV}' 457 checkvars['SRC_URI'] = url.replace(testver, '${PV}') 458 self._test_recipe_contents(recipefile, checkvars, []) 459 # Try with version specified 460 result = runCmd('devtool reset -n %s' % testrecipe) 461 shutil.rmtree(srcdir) 462 fakever = '1.9' 463 result = runCmd('devtool add %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever)) 464 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory') 465 # Test devtool status 466 result = runCmd('devtool status') 467 self.assertIn(testrecipe, result.output) 468 self.assertIn(srcdir, result.output) 469 # Check recipe 470 recipefile = get_bb_var('FILE', testrecipe) 471 self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named') 472 checkvars = {} 473 checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver 474 checkvars['SRC_URI'] = url 475 self._test_recipe_contents(recipefile, checkvars, []) 476 477 def test_devtool_add_fetch_git(self): 478 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 479 self.track_for_cleanup(tempdir) 480 url = 'gitsm://git.yoctoproject.org/mraa' 481 url_branch = '%s;branch=master' % url 482 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d' 483 testrecipe = 'mraa' 484 srcdir = os.path.join(tempdir, testrecipe) 485 # Test devtool add 486 self.track_for_cleanup(self.workspacedir) 487 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe) 488 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 489 result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url)) 490 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created: %s' % result.output) 491 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory') 492 # Test devtool status 493 result = runCmd('devtool status') 494 self.assertIn(testrecipe, result.output) 495 self.assertIn(srcdir, result.output) 496 # Check recipe 497 recipefile = get_bb_var('FILE', testrecipe) 498 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') 499 checkvars = {} 500 checkvars['S'] = '${WORKDIR}/git' 501 checkvars['PV'] = '1.0+git${SRCPV}' 502 checkvars['SRC_URI'] = url_branch 503 checkvars['SRCREV'] = '${AUTOREV}' 504 self._test_recipe_contents(recipefile, checkvars, []) 505 # Try with revision and version specified 506 result = runCmd('devtool reset -n %s' % testrecipe) 507 shutil.rmtree(srcdir) 508 url_rev = '%s;rev=%s' % (url, checkrev) 509 result = runCmd('devtool add %s %s -f "%s" -V 1.5' % (testrecipe, srcdir, url_rev)) 510 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory') 511 # Test devtool status 512 result = runCmd('devtool status') 513 self.assertIn(testrecipe, result.output) 514 self.assertIn(srcdir, result.output) 515 # Check recipe 516 recipefile = get_bb_var('FILE', testrecipe) 517 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') 518 checkvars = {} 519 checkvars['S'] = '${WORKDIR}/git' 520 checkvars['PV'] = '1.5+git${SRCPV}' 521 checkvars['SRC_URI'] = url_branch 522 checkvars['SRCREV'] = checkrev 523 self._test_recipe_contents(recipefile, checkvars, []) 524 525 def test_devtool_add_fetch_simple(self): 526 # Fetch source from a remote URL, auto-detecting name 527 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 528 self.track_for_cleanup(tempdir) 529 testver = '1.6.0' 530 url = 'http://www.ivarch.com/programs/sources/pv-%s.tar.bz2' % testver 531 testrecipe = 'pv' 532 srcdir = os.path.join(self.workspacedir, 'sources', testrecipe) 533 # Test devtool add 534 self.track_for_cleanup(self.workspacedir) 535 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 536 result = runCmd('devtool add %s' % url) 537 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output) 538 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory') 539 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created') 540 # Test devtool status 541 result = runCmd('devtool status') 542 self.assertIn(testrecipe, result.output) 543 self.assertIn(srcdir, result.output) 544 # Check recipe 545 recipefile = get_bb_var('FILE', testrecipe) 546 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named') 547 checkvars = {} 548 checkvars['S'] = None 549 checkvars['SRC_URI'] = url.replace(testver, '${PV}') 550 self._test_recipe_contents(recipefile, checkvars, []) 551 552 def test_devtool_add_npm(self): 553 collections = get_bb_var('BBFILE_COLLECTIONS').split() 554 if "openembedded-layer" not in collections: 555 self.skipTest("Test needs meta-oe for nodejs") 556 557 pn = 'savoirfairelinux-node-server-example' 558 pv = '1.0.0' 559 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=' + pv 560 # Test devtool add 561 self.track_for_cleanup(self.workspacedir) 562 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn) 563 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 564 result = runCmd('devtool add \'%s\'' % url) 565 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') 566 self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, '%s_%s.bb' % (pn, pv)), 'Recipe not created') 567 self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, pn, 'npm-shrinkwrap.json'), 'Shrinkwrap not created') 568 # Test devtool status 569 result = runCmd('devtool status') 570 self.assertIn(pn, result.output) 571 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then) 572 bitbake('%s -c cleansstate' % pn) 573 # Test devtool build 574 result = runCmd('devtool build %s' % pn) 575 576 def test_devtool_add_python_egg_requires(self): 577 # Fetch source 578 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 579 self.track_for_cleanup(tempdir) 580 testver = '0.14.0' 581 url = 'https://files.pythonhosted.org/packages/e9/9e/25d59f5043cf763833b2581c8027fa92342c4cf8ee523b498ecdf460c16d/uvicorn-%s.tar.gz' % testver 582 testrecipe = 'python3-uvicorn' 583 srcdir = os.path.join(tempdir, testrecipe) 584 # Test devtool add 585 self.track_for_cleanup(self.workspacedir) 586 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 587 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url)) 588 589class DevtoolModifyTests(DevtoolBase): 590 591 def test_devtool_modify(self): 592 import oe.path 593 594 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 595 self.track_for_cleanup(tempdir) 596 self.track_for_cleanup(self.workspacedir) 597 self.add_command_to_tearDown('bitbake -c clean mdadm') 598 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 599 result = runCmd('devtool modify mdadm -x %s' % tempdir) 600 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found') 601 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') 602 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mdadm_*.bbappend')) 603 self.assertTrue(matches, 'bbappend not created %s' % result.output) 604 605 # Test devtool status 606 result = runCmd('devtool status') 607 self.assertIn('mdadm', result.output) 608 self.assertIn(tempdir, result.output) 609 self._check_src_repo(tempdir) 610 611 bitbake('mdadm -C unpack') 612 613 def check_line(checkfile, expected, message, present=True): 614 # Check for $expected, on a line on its own, in checkfile. 615 with open(checkfile, 'r') as f: 616 if present: 617 self.assertIn(expected + '\n', f, message) 618 else: 619 self.assertNotIn(expected + '\n', f, message) 620 621 modfile = os.path.join(tempdir, 'mdadm.8.in') 622 bb_vars = get_bb_vars(['PKGD', 'mandir'], 'mdadm') 623 pkgd = bb_vars['PKGD'] 624 self.assertTrue(pkgd, 'Could not query PKGD variable') 625 mandir = bb_vars['mandir'] 626 self.assertTrue(mandir, 'Could not query mandir variable') 627 manfile = oe.path.join(pkgd, mandir, 'man8', 'mdadm.8') 628 629 check_line(modfile, 'Linux Software RAID', 'Could not find initial string') 630 check_line(modfile, 'antique pin sardine', 'Unexpectedly found replacement string', present=False) 631 632 result = runCmd("sed -i 's!^Linux Software RAID$!antique pin sardine!' %s" % modfile) 633 check_line(modfile, 'antique pin sardine', 'mdadm.8.in file not modified (sed failed)') 634 635 bitbake('mdadm -c package') 636 check_line(manfile, 'antique pin sardine', 'man file not modified. man searched file path: %s' % manfile) 637 638 result = runCmd('git checkout -- %s' % modfile, cwd=tempdir) 639 check_line(modfile, 'Linux Software RAID', 'man .in file not restored (git failed)') 640 641 bitbake('mdadm -c package') 642 check_line(manfile, 'Linux Software RAID', 'man file not updated. man searched file path: %s' % manfile) 643 644 result = runCmd('devtool reset mdadm') 645 result = runCmd('devtool status') 646 self.assertNotIn('mdadm', result.output) 647 648 def test_devtool_buildclean(self): 649 def assertFile(path, *paths): 650 f = os.path.join(path, *paths) 651 self.assertExists(f) 652 def assertNoFile(path, *paths): 653 f = os.path.join(path, *paths) 654 self.assertNotExists(f) 655 656 # Clean up anything in the workdir/sysroot/sstate cache 657 bitbake('mdadm m4 -c cleansstate') 658 # Try modifying a recipe 659 tempdir_mdadm = tempfile.mkdtemp(prefix='devtoolqa') 660 tempdir_m4 = tempfile.mkdtemp(prefix='devtoolqa') 661 builddir_m4 = tempfile.mkdtemp(prefix='devtoolqa') 662 self.track_for_cleanup(tempdir_mdadm) 663 self.track_for_cleanup(tempdir_m4) 664 self.track_for_cleanup(builddir_m4) 665 self.track_for_cleanup(self.workspacedir) 666 self.add_command_to_tearDown('bitbake -c clean mdadm m4') 667 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 668 self.write_recipeinc('m4', 'EXTERNALSRC_BUILD = "%s"\ndo_clean() {\n\t:\n}\n' % builddir_m4) 669 try: 670 runCmd('devtool modify mdadm -x %s' % tempdir_mdadm) 671 runCmd('devtool modify m4 -x %s' % tempdir_m4) 672 assertNoFile(tempdir_mdadm, 'mdadm') 673 assertNoFile(builddir_m4, 'src/m4') 674 result = bitbake('m4 -e') 675 result = bitbake('mdadm m4 -c compile') 676 self.assertEqual(result.status, 0) 677 assertFile(tempdir_mdadm, 'mdadm') 678 assertFile(builddir_m4, 'src/m4') 679 # Check that buildclean task exists and does call make clean 680 bitbake('mdadm m4 -c buildclean') 681 assertNoFile(tempdir_mdadm, 'mdadm') 682 assertNoFile(builddir_m4, 'src/m4') 683 runCmd('echo "#Trigger rebuild" >> %s/Makefile' % tempdir_mdadm) 684 bitbake('mdadm m4 -c compile') 685 assertFile(tempdir_mdadm, 'mdadm') 686 assertFile(builddir_m4, 'src/m4') 687 bitbake('mdadm m4 -c clean') 688 # Check that buildclean task is run before clean for B == S 689 assertNoFile(tempdir_mdadm, 'mdadm') 690 # Check that buildclean task is not run before clean for B != S 691 assertFile(builddir_m4, 'src/m4') 692 finally: 693 self.delete_recipeinc('m4') 694 695 def test_devtool_modify_invalid(self): 696 # Try modifying some recipes 697 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 698 self.track_for_cleanup(tempdir) 699 self.track_for_cleanup(self.workspacedir) 700 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 701 702 testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk'.split() 703 # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose 704 result = runCmd('bitbake-layers show-recipes gcc-source*') 705 for line in result.output.splitlines(): 706 # just match those lines that contain a real target 707 m = re.match('(?P<recipe>^[a-zA-Z0-9.-]+)(?P<colon>:$)', line) 708 if m: 709 testrecipes.append(m.group('recipe')) 710 for testrecipe in testrecipes: 711 # Check it's a valid recipe 712 bitbake('%s -e' % testrecipe) 713 # devtool extract should fail 714 result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True) 715 self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed. devtool output: %s' % (testrecipe, result.output)) 716 self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe) 717 self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe) 718 # devtool modify should fail 719 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True) 720 self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed. devtool output: %s' % (testrecipe, result.output)) 721 self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe) 722 723 def test_devtool_modify_native(self): 724 # Check preconditions 725 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 726 # Try modifying some recipes 727 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 728 self.track_for_cleanup(tempdir) 729 self.track_for_cleanup(self.workspacedir) 730 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 731 732 bbclassextended = False 733 inheritnative = False 734 testrecipes = 'cdrtools-native mtools-native apt-native desktop-file-utils-native'.split() 735 for testrecipe in testrecipes: 736 checkextend = 'native' in (get_bb_var('BBCLASSEXTEND', testrecipe) or '').split() 737 if not bbclassextended: 738 bbclassextended = checkextend 739 if not inheritnative: 740 inheritnative = not checkextend 741 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe))) 742 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool modify output: %s' % result.output) 743 result = runCmd('devtool build %s' % testrecipe) 744 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool build output: %s' % result.output) 745 result = runCmd('devtool reset %s' % testrecipe) 746 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool reset output: %s' % result.output) 747 748 self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes)) 749 self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes)) 750 751 def test_devtool_modify_localfiles_only(self): 752 # Check preconditions 753 testrecipe = 'base-files' 754 src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split() 755 foundlocalonly = False 756 correct_symlink = False 757 for item in src_uri: 758 if item.startswith('file://'): 759 if '.patch' not in item: 760 foundlocalonly = True 761 else: 762 foundlocalonly = False 763 break 764 self.assertTrue(foundlocalonly, 'This test expects the %s recipe to fetch local files only and it seems that it no longer does' % testrecipe) 765 # Clean up anything in the workdir/sysroot/sstate cache 766 bitbake('%s -c cleansstate' % testrecipe) 767 # Try modifying a recipe 768 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 769 self.track_for_cleanup(tempdir) 770 self.track_for_cleanup(self.workspacedir) 771 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 772 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 773 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 774 srcfile = os.path.join(tempdir, 'oe-local-files/share/dot.bashrc') 775 srclink = os.path.join(tempdir, 'share/dot.bashrc') 776 self.assertExists(srcfile, 'Extracted source could not be found') 777 if os.path.islink(srclink) and os.path.exists(srclink) and os.path.samefile(srcfile, srclink): 778 correct_symlink = True 779 self.assertTrue(correct_symlink, 'Source symlink to oe-local-files is broken') 780 781 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe)) 782 self.assertTrue(matches, 'bbappend not created') 783 # Test devtool status 784 result = runCmd('devtool status') 785 self.assertIn(testrecipe, result.output) 786 self.assertIn(tempdir, result.output) 787 # Try building 788 bitbake(testrecipe) 789 790 def test_devtool_modify_git(self): 791 # Check preconditions 792 testrecipe = 'psplash' 793 src_uri = get_bb_var('SRC_URI', testrecipe) 794 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe) 795 # Clean up anything in the workdir/sysroot/sstate cache 796 bitbake('%s -c cleansstate' % testrecipe) 797 # Try modifying a recipe 798 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 799 self.track_for_cleanup(tempdir) 800 self.track_for_cleanup(self.workspacedir) 801 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 802 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 803 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 804 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') 805 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output) 806 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend')) 807 self.assertTrue(matches, 'bbappend not created') 808 # Test devtool status 809 result = runCmd('devtool status') 810 self.assertIn(testrecipe, result.output) 811 self.assertIn(tempdir, result.output) 812 # Check git repo 813 self._check_src_repo(tempdir) 814 # Try building 815 bitbake(testrecipe) 816 817 def test_devtool_modify_localfiles(self): 818 # Check preconditions 819 testrecipe = 'lighttpd' 820 src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split() 821 foundlocal = False 822 for item in src_uri: 823 if item.startswith('file://') and '.patch' not in item: 824 foundlocal = True 825 break 826 self.assertTrue(foundlocal, 'This test expects the %s recipe to fetch local files and it seems that it no longer does' % testrecipe) 827 # Clean up anything in the workdir/sysroot/sstate cache 828 bitbake('%s -c cleansstate' % testrecipe) 829 # Try modifying a recipe 830 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 831 self.track_for_cleanup(tempdir) 832 self.track_for_cleanup(self.workspacedir) 833 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 834 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 835 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 836 self.assertExists(os.path.join(tempdir, 'configure.ac'), 'Extracted source could not be found') 837 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') 838 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe)) 839 self.assertTrue(matches, 'bbappend not created') 840 # Test devtool status 841 result = runCmd('devtool status') 842 self.assertIn(testrecipe, result.output) 843 self.assertIn(tempdir, result.output) 844 # Try building 845 bitbake(testrecipe) 846 847 def test_devtool_modify_virtual(self): 848 # Try modifying a virtual recipe 849 virtrecipe = 'virtual/make' 850 realrecipe = 'make' 851 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 852 self.track_for_cleanup(tempdir) 853 self.track_for_cleanup(self.workspacedir) 854 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 855 result = runCmd('devtool modify %s -x %s' % (virtrecipe, tempdir)) 856 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') 857 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') 858 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % realrecipe)) 859 self.assertTrue(matches, 'bbappend not created %s' % result.output) 860 # Test devtool status 861 result = runCmd('devtool status') 862 self.assertNotIn(virtrecipe, result.output) 863 self.assertIn(realrecipe, result.output) 864 # Check git repo 865 self._check_src_repo(tempdir) 866 # This is probably sufficient 867 868 def test_devtool_modify_overrides(self): 869 # Try modifying a recipe with patches in overrides 870 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 871 self.track_for_cleanup(tempdir) 872 self.track_for_cleanup(self.workspacedir) 873 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 874 result = runCmd('devtool modify devtool-patch-overrides -x %s' % (tempdir)) 875 876 self._check_src_repo(tempdir) 877 source = os.path.join(tempdir, "source") 878 def check(branch, expected): 879 runCmd('git -C %s checkout %s' % (tempdir, branch)) 880 with open(source, "rt") as f: 881 content = f.read() 882 self.assertEquals(content, expected) 883 check('devtool', 'This is a test for something\n') 884 check('devtool-no-overrides', 'This is a test for something\n') 885 check('devtool-override-qemuarm', 'This is a test for qemuarm\n') 886 check('devtool-override-qemux86', 'This is a test for qemux86\n') 887 888class DevtoolUpdateTests(DevtoolBase): 889 890 def test_devtool_update_recipe(self): 891 # Check preconditions 892 testrecipe = 'minicom' 893 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 894 recipefile = bb_vars['FILE'] 895 src_uri = bb_vars['SRC_URI'] 896 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe) 897 self._check_repo_status(os.path.dirname(recipefile), []) 898 # First, modify 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-layers remove-layer */workspace') 903 # (don't bother with cleaning the recipe on teardown, we won't be building it) 904 # We don't use -x here so that we test the behaviour of devtool modify without it 905 result = runCmd('devtool modify %s %s' % (testrecipe, tempdir)) 906 # Check git repo 907 self._check_src_repo(tempdir) 908 # Add a couple of commits 909 # FIXME: this only tests adding, need to also test update and remove 910 result = runCmd('echo "Additional line" >> README', cwd=tempdir) 911 result = runCmd('git commit -a -m "Change the README"', cwd=tempdir) 912 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir) 913 result = runCmd('git add devtool-new-file', cwd=tempdir) 914 result = runCmd('git commit -m "Add a new file"', cwd=tempdir) 915 self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 916 result = runCmd('devtool update-recipe %s' % testrecipe) 917 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 918 ('??', '.*/0001-Change-the-README.patch$'), 919 ('??', '.*/0002-Add-a-new-file.patch$')] 920 self._check_repo_status(os.path.dirname(recipefile), expected_status) 921 922 def test_devtool_update_recipe_git(self): 923 # Check preconditions 924 testrecipe = 'mtd-utils' 925 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 926 recipefile = bb_vars['FILE'] 927 src_uri = bb_vars['SRC_URI'] 928 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe) 929 patches = [] 930 for entry in src_uri.split(): 931 if entry.startswith('file://') and entry.endswith('.patch'): 932 patches.append(entry[7:].split(';')[0]) 933 self.assertGreater(len(patches), 0, 'The %s recipe does not appear to contain any patches, so this test will not be effective' % testrecipe) 934 self._check_repo_status(os.path.dirname(recipefile), []) 935 # First, modify a recipe 936 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 937 self.track_for_cleanup(tempdir) 938 self.track_for_cleanup(self.workspacedir) 939 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 940 # (don't bother with cleaning the recipe on teardown, we won't be building it) 941 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 942 # Check git repo 943 self._check_src_repo(tempdir) 944 # Add a couple of commits 945 # FIXME: this only tests adding, need to also test update and remove 946 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir) 947 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir) 948 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir) 949 result = runCmd('git add devtool-new-file', cwd=tempdir) 950 result = runCmd('git commit -m "Add a new file"', cwd=tempdir) 951 self.add_command_to_tearDown('cd %s; rm -rf %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 952 result = runCmd('devtool update-recipe -m srcrev %s' % testrecipe) 953 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile))] + \ 954 [(' D', '.*/%s$' % patch) for patch in patches] 955 self._check_repo_status(os.path.dirname(recipefile), expected_status) 956 957 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile)) 958 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git;branch=master"'] 959 srcurilines = src_uri.split() 960 srcurilines[0] = 'SRC_URI = "' + srcurilines[0] 961 srcurilines.append('"') 962 removelines = ['SRCREV = ".*"'] + srcurilines 963 self._check_diff(result.output, addlines, removelines) 964 # Now try with auto mode 965 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile))) 966 result = runCmd('devtool update-recipe %s' % testrecipe) 967 result = runCmd('git rev-parse --show-toplevel', cwd=os.path.dirname(recipefile)) 968 topleveldir = result.output.strip() 969 relpatchpath = os.path.join(os.path.relpath(os.path.dirname(recipefile), topleveldir), testrecipe) 970 expected_status = [(' M', os.path.relpath(recipefile, topleveldir)), 971 ('??', '%s/0001-Change-the-Makefile.patch' % relpatchpath), 972 ('??', '%s/0002-Add-a-new-file.patch' % relpatchpath)] 973 self._check_repo_status(os.path.dirname(recipefile), expected_status) 974 975 def test_devtool_update_recipe_append(self): 976 # Check preconditions 977 testrecipe = 'mdadm' 978 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 979 recipefile = bb_vars['FILE'] 980 src_uri = bb_vars['SRC_URI'] 981 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe) 982 self._check_repo_status(os.path.dirname(recipefile), []) 983 # First, modify a recipe 984 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 985 tempsrcdir = os.path.join(tempdir, 'source') 986 templayerdir = os.path.join(tempdir, 'layer') 987 self.track_for_cleanup(tempdir) 988 self.track_for_cleanup(self.workspacedir) 989 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 990 # (don't bother with cleaning the recipe on teardown, we won't be building it) 991 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir)) 992 # Check git repo 993 self._check_src_repo(tempsrcdir) 994 # Add a commit 995 result = runCmd("sed 's!\\(#define VERSION\\W*\"[^\"]*\\)\"!\\1-custom\"!' -i ReadMe.c", cwd=tempsrcdir) 996 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) 997 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe)) 998 # Create a temporary layer and add it to bblayers.conf 999 self._create_temp_layer(templayerdir, True, 'selftestupdaterecipe') 1000 # Create the bbappend 1001 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1002 self.assertNotIn('WARNING:', result.output) 1003 # Check recipe is still clean 1004 self._check_repo_status(os.path.dirname(recipefile), []) 1005 # Check bbappend was created 1006 splitpath = os.path.dirname(recipefile).split(os.sep) 1007 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1]) 1008 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir) 1009 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-our-custom-version.patch') 1010 self.assertExists(patchfile, 'Patch file not created') 1011 1012 # Check bbappend contents 1013 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 1014 '\n', 1015 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n', 1016 '\n'] 1017 with open(bbappendfile, 'r') as f: 1018 self.assertEqual(expectedlines, f.readlines()) 1019 1020 # Check we can run it again and bbappend isn't modified 1021 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1022 with open(bbappendfile, 'r') as f: 1023 self.assertEqual(expectedlines, f.readlines()) 1024 # Drop new commit and check patch gets deleted 1025 result = runCmd('git reset HEAD^', cwd=tempsrcdir) 1026 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1027 self.assertNotExists(patchfile, 'Patch file not deleted') 1028 expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 1029 '\n'] 1030 with open(bbappendfile, 'r') as f: 1031 self.assertEqual(expectedlines2, f.readlines()) 1032 # Put commit back and check we can run it if layer isn't in bblayers.conf 1033 os.remove(bbappendfile) 1034 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) 1035 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) 1036 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) 1037 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output) 1038 self.assertExists(patchfile, 'Patch file not created (with disabled layer)') 1039 with open(bbappendfile, 'r') as f: 1040 self.assertEqual(expectedlines, f.readlines()) 1041 # Deleting isn't expected to work under these circumstances 1042 1043 def test_devtool_update_recipe_append_git(self): 1044 # Check preconditions 1045 testrecipe = 'mtd-utils' 1046 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1047 recipefile = bb_vars['FILE'] 1048 src_uri = bb_vars['SRC_URI'] 1049 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe) 1050 for entry in src_uri.split(): 1051 if entry.startswith('git://'): 1052 git_uri = entry 1053 break 1054 self._check_repo_status(os.path.dirname(recipefile), []) 1055 # First, modify a recipe 1056 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1057 tempsrcdir = os.path.join(tempdir, 'source') 1058 templayerdir = os.path.join(tempdir, 'layer') 1059 self.track_for_cleanup(tempdir) 1060 self.track_for_cleanup(self.workspacedir) 1061 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1062 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1063 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir)) 1064 # Check git repo 1065 self._check_src_repo(tempsrcdir) 1066 # Add a commit 1067 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir) 1068 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir) 1069 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe)) 1070 # Create a temporary layer 1071 os.makedirs(os.path.join(templayerdir, 'conf')) 1072 with open(os.path.join(templayerdir, 'conf', 'layer.conf'), 'w') as f: 1073 f.write('BBPATH .= ":${LAYERDIR}"\n') 1074 f.write('BBFILES += "${LAYERDIR}/recipes-*/*/*.bbappend"\n') 1075 f.write('BBFILE_COLLECTIONS += "oeselftesttemplayer"\n') 1076 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n') 1077 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n') 1078 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n') 1079 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "${LAYERSERIES_COMPAT_core}"\n') 1080 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir) 1081 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir) 1082 # Create the bbappend 1083 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1084 self.assertNotIn('WARNING:', result.output) 1085 # Check recipe is still clean 1086 self._check_repo_status(os.path.dirname(recipefile), []) 1087 # Check bbappend was created 1088 splitpath = os.path.dirname(recipefile).split(os.sep) 1089 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1]) 1090 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir) 1091 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') 1092 1093 # Check bbappend contents 1094 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) 1095 expectedlines = set(['SRCREV = "%s"\n' % result.output, 1096 '\n', 1097 'SRC_URI = "%s"\n' % git_uri, 1098 '\n']) 1099 with open(bbappendfile, 'r') as f: 1100 self.assertEqual(expectedlines, set(f.readlines())) 1101 1102 # Check we can run it again and bbappend isn't modified 1103 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1104 with open(bbappendfile, 'r') as f: 1105 self.assertEqual(expectedlines, set(f.readlines())) 1106 # Drop new commit and check SRCREV changes 1107 result = runCmd('git reset HEAD^', cwd=tempsrcdir) 1108 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1109 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') 1110 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) 1111 expectedlines = set(['SRCREV = "%s"\n' % result.output, 1112 '\n', 1113 'SRC_URI = "%s"\n' % git_uri, 1114 '\n']) 1115 with open(bbappendfile, 'r') as f: 1116 self.assertEqual(expectedlines, set(f.readlines())) 1117 # Put commit back and check we can run it if layer isn't in bblayers.conf 1118 os.remove(bbappendfile) 1119 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir) 1120 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) 1121 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) 1122 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output) 1123 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') 1124 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) 1125 expectedlines = set(['SRCREV = "%s"\n' % result.output, 1126 '\n', 1127 'SRC_URI = "%s"\n' % git_uri, 1128 '\n']) 1129 with open(bbappendfile, 'r') as f: 1130 self.assertEqual(expectedlines, set(f.readlines())) 1131 # Deleting isn't expected to work under these circumstances 1132 1133 def test_devtool_update_recipe_local_files(self): 1134 """Check that local source files are copied over instead of patched""" 1135 testrecipe = 'makedevs' 1136 recipefile = get_bb_var('FILE', testrecipe) 1137 # Setup srctree for modifying the recipe 1138 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1139 self.track_for_cleanup(tempdir) 1140 self.track_for_cleanup(self.workspacedir) 1141 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1142 # (don't bother with cleaning the recipe on teardown, we won't be 1143 # building it) 1144 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1145 # Check git repo 1146 self._check_src_repo(tempdir) 1147 # Try building just to ensure we haven't broken that 1148 bitbake("%s" % testrecipe) 1149 # Edit / commit local source 1150 runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir) 1151 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir) 1152 runCmd('echo "Bar" > new-file', cwd=tempdir) 1153 runCmd('git add new-file', cwd=tempdir) 1154 runCmd('git commit -m "Add new file"', cwd=tempdir) 1155 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' % 1156 os.path.dirname(recipefile)) 1157 runCmd('devtool update-recipe %s' % testrecipe) 1158 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1159 (' M', '.*/makedevs/makedevs.c$'), 1160 ('??', '.*/makedevs/new-local$'), 1161 ('??', '.*/makedevs/0001-Add-new-file.patch$')] 1162 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1163 1164 def test_devtool_update_recipe_local_files_2(self): 1165 """Check local source files support when oe-local-files is in Git""" 1166 testrecipe = 'devtool-test-local' 1167 recipefile = get_bb_var('FILE', testrecipe) 1168 recipedir = os.path.dirname(recipefile) 1169 result = runCmd('git status --porcelain .', cwd=recipedir) 1170 if result.output.strip(): 1171 self.fail('Recipe directory for %s contains uncommitted changes' % testrecipe) 1172 # Setup srctree for modifying the recipe 1173 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1174 self.track_for_cleanup(tempdir) 1175 self.track_for_cleanup(self.workspacedir) 1176 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1177 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1178 # Check git repo 1179 self._check_src_repo(tempdir) 1180 # Add oe-local-files to Git 1181 runCmd('rm oe-local-files/.gitignore', cwd=tempdir) 1182 runCmd('git add oe-local-files', cwd=tempdir) 1183 runCmd('git commit -m "Add local sources"', cwd=tempdir) 1184 # Edit / commit local sources 1185 runCmd('echo "# Foobar" >> oe-local-files/file1', cwd=tempdir) 1186 runCmd('git commit -am "Edit existing file"', cwd=tempdir) 1187 runCmd('git rm oe-local-files/file2', cwd=tempdir) 1188 runCmd('git commit -m"Remove file"', cwd=tempdir) 1189 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir) 1190 runCmd('git add oe-local-files/new-local', cwd=tempdir) 1191 runCmd('git commit -m "Add new local file"', cwd=tempdir) 1192 runCmd('echo "Gar" > new-file', cwd=tempdir) 1193 runCmd('git add new-file', cwd=tempdir) 1194 runCmd('git commit -m "Add new file"', cwd=tempdir) 1195 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' % 1196 os.path.dirname(recipefile)) 1197 # Checkout unmodified file to working copy -> devtool should still pick 1198 # the modified version from HEAD 1199 runCmd('git checkout HEAD^ -- oe-local-files/file1', cwd=tempdir) 1200 runCmd('devtool update-recipe %s' % testrecipe) 1201 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1202 (' M', '.*/file1$'), 1203 (' D', '.*/file2$'), 1204 ('??', '.*/new-local$'), 1205 ('??', '.*/0001-Add-new-file.patch$')] 1206 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1207 1208 def test_devtool_update_recipe_with_gitignore(self): 1209 # First, modify the recipe 1210 testrecipe = 'devtool-test-ignored' 1211 bb_vars = get_bb_vars(['FILE'], testrecipe) 1212 recipefile = bb_vars['FILE'] 1213 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch') 1214 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch.expected') 1215 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1216 self.track_for_cleanup(tempdir) 1217 self.track_for_cleanup(self.workspacedir) 1218 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1219 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1220 result = runCmd('devtool modify %s' % testrecipe) 1221 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1222 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe) 1223 # Check recipe got changed as expected 1224 with open(newpatchfile, 'r') as f: 1225 desiredlines = f.readlines() 1226 with open(patchfile, 'r') as f: 1227 newlines = f.readlines() 1228 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo 1229 # which changes the metadata subject which is added into the patch, but keep 1230 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh 1231 # devtool-test-ignored manually, then it should generate exactly the same .patch file 1232 self.assertEqual(desiredlines[5:], newlines[5:]) 1233 1234 def test_devtool_update_recipe_long_filename(self): 1235 # First, modify the recipe 1236 testrecipe = 'devtool-test-long-filename' 1237 bb_vars = get_bb_vars(['FILE'], testrecipe) 1238 recipefile = bb_vars['FILE'] 1239 patchfilename = '0001-I-ll-patch-you-only-if-devtool-lets-me-to-do-it-corr.patch' 1240 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename) 1241 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename + '.expected') 1242 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1243 self.track_for_cleanup(tempdir) 1244 self.track_for_cleanup(self.workspacedir) 1245 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1246 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1247 result = runCmd('devtool modify %s' % testrecipe) 1248 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1249 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe) 1250 # Check recipe got changed as expected 1251 with open(newpatchfile, 'r') as f: 1252 desiredlines = f.readlines() 1253 with open(patchfile, 'r') as f: 1254 newlines = f.readlines() 1255 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo 1256 # which changes the metadata subject which is added into the patch, but keep 1257 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh 1258 # devtool-test-ignored manually, then it should generate exactly the same .patch file 1259 self.assertEqual(desiredlines[5:], newlines[5:]) 1260 1261 def test_devtool_update_recipe_local_files_3(self): 1262 # First, modify the recipe 1263 testrecipe = 'devtool-test-localonly' 1264 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1265 recipefile = bb_vars['FILE'] 1266 src_uri = bb_vars['SRC_URI'] 1267 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1268 self.track_for_cleanup(tempdir) 1269 self.track_for_cleanup(self.workspacedir) 1270 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1271 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1272 result = runCmd('devtool modify %s' % testrecipe) 1273 # Modify one file 1274 runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files')) 1275 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1276 result = runCmd('devtool update-recipe %s' % testrecipe) 1277 expected_status = [(' M', '.*/%s/file2$' % testrecipe)] 1278 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1279 1280 def test_devtool_update_recipe_local_patch_gz(self): 1281 # First, modify the recipe 1282 testrecipe = 'devtool-test-patch-gz' 1283 if get_bb_var('DISTRO') == 'poky-tiny': 1284 self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe) 1285 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1286 recipefile = bb_vars['FILE'] 1287 src_uri = bb_vars['SRC_URI'] 1288 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1289 self.track_for_cleanup(tempdir) 1290 self.track_for_cleanup(self.workspacedir) 1291 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1292 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1293 result = runCmd('devtool modify %s' % testrecipe) 1294 # Modify one file 1295 srctree = os.path.join(self.workspacedir, 'sources', testrecipe) 1296 runCmd('echo "Another line" >> README', cwd=srctree) 1297 runCmd('git commit -a --amend --no-edit', cwd=srctree) 1298 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1299 result = runCmd('devtool update-recipe %s' % testrecipe) 1300 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)] 1301 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1302 patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz') 1303 result = runCmd('file %s' % patch_gz) 1304 if 'gzip compressed data' not in result.output: 1305 self.fail('New patch file is not gzipped - file reports:\n%s' % result.output) 1306 1307 def test_devtool_update_recipe_local_files_subdir(self): 1308 # Try devtool update-recipe on a recipe that has a file with subdir= set in 1309 # SRC_URI such that it overwrites a file that was in an archive that 1310 # was also in SRC_URI 1311 # First, modify the recipe 1312 testrecipe = 'devtool-test-subdir' 1313 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) 1314 recipefile = bb_vars['FILE'] 1315 src_uri = bb_vars['SRC_URI'] 1316 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1317 self.track_for_cleanup(tempdir) 1318 self.track_for_cleanup(self.workspacedir) 1319 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1320 # (don't bother with cleaning the recipe on teardown, we won't be building it) 1321 result = runCmd('devtool modify %s' % testrecipe) 1322 testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile') 1323 self.assertExists(testfile, 'Extracted source could not be found') 1324 with open(testfile, 'r') as f: 1325 contents = f.read().rstrip() 1326 self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been') 1327 # Test devtool update-recipe without modifying any files 1328 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) 1329 result = runCmd('devtool update-recipe %s' % testrecipe) 1330 expected_status = [] 1331 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1332 1333 def test_devtool_finish_modify_git_subdir(self): 1334 # Check preconditions 1335 testrecipe = 'dos2unix' 1336 bb_vars = get_bb_vars(['SRC_URI', 'S', 'WORKDIR', 'FILE'], testrecipe) 1337 self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe) 1338 workdir_git = '%s/git/' % bb_vars['WORKDIR'] 1339 if not bb_vars['S'].startswith(workdir_git): 1340 self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe) 1341 subdir = bb_vars['S'].split(workdir_git, 1)[1] 1342 # Clean up anything in the workdir/sysroot/sstate cache 1343 bitbake('%s -c cleansstate' % testrecipe) 1344 # Try modifying a recipe 1345 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1346 self.track_for_cleanup(tempdir) 1347 self.track_for_cleanup(self.workspacedir) 1348 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 1349 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1350 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1351 testsrcfile = os.path.join(tempdir, subdir, 'dos2unix.c') 1352 self.assertExists(testsrcfile, 'Extracted source could not be found') 1353 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output) 1354 self.assertNotExists(os.path.join(tempdir, subdir, '.git'), 'Subdirectory has been initialised as a git repo') 1355 # Check git repo 1356 self._check_src_repo(tempdir) 1357 # Modify file 1358 runCmd("sed -i '1s:^:/* Add a comment */\\n:' %s" % testsrcfile) 1359 result = runCmd('git commit -a -m "Add a comment"', cwd=tempdir) 1360 # Now try updating original recipe 1361 recipefile = bb_vars['FILE'] 1362 recipedir = os.path.dirname(recipefile) 1363 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe)) 1364 result = runCmd('devtool update-recipe %s' % testrecipe) 1365 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1366 ('??', '.*/%s/%s/$' % (testrecipe, testrecipe))] 1367 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1368 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile)) 1369 removelines = ['SRC_URI = "git://.*"'] 1370 addlines = [ 1371 'SRC_URI = "git://.* \\\\', 1372 'file://0001-Add-a-comment.patch;patchdir=.. \\\\', 1373 '"' 1374 ] 1375 self._check_diff(result.output, addlines, removelines) 1376 # Put things back so we can run devtool finish on a different layer 1377 runCmd('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe)) 1378 # Run devtool finish 1379 res = re.search('recipes-.*', recipedir) 1380 self.assertTrue(res, 'Unable to find recipe subdirectory') 1381 recipesubdir = res[0] 1382 self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, recipesubdir)) 1383 result = runCmd('devtool finish %s meta-selftest' % testrecipe) 1384 # Check bbappend file contents 1385 appendfn = os.path.join(self.testlayer_path, recipesubdir, '%s_%%.bbappend' % testrecipe) 1386 with open(appendfn, 'r') as f: 1387 appendlines = f.readlines() 1388 expected_appendlines = [ 1389 'FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 1390 '\n', 1391 'SRC_URI += "file://0001-Add-a-comment.patch;patchdir=.."\n', 1392 '\n' 1393 ] 1394 self.assertEqual(appendlines, expected_appendlines) 1395 self.assertExists(os.path.join(os.path.dirname(appendfn), testrecipe, '0001-Add-a-comment.patch')) 1396 # Try building 1397 bitbake('%s -c patch' % testrecipe) 1398 1399 1400class DevtoolExtractTests(DevtoolBase): 1401 1402 def test_devtool_extract(self): 1403 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1404 # Try devtool extract 1405 self.track_for_cleanup(tempdir) 1406 self.track_for_cleanup(self.workspacedir) 1407 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1408 result = runCmd('devtool extract matchbox-terminal %s' % tempdir) 1409 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') 1410 self._check_src_repo(tempdir) 1411 1412 def test_devtool_extract_virtual(self): 1413 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1414 # Try devtool extract 1415 self.track_for_cleanup(tempdir) 1416 self.track_for_cleanup(self.workspacedir) 1417 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1418 result = runCmd('devtool extract virtual/make %s' % tempdir) 1419 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') 1420 self._check_src_repo(tempdir) 1421 1422 def test_devtool_reset_all(self): 1423 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1424 self.track_for_cleanup(tempdir) 1425 self.track_for_cleanup(self.workspacedir) 1426 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1427 testrecipe1 = 'mdadm' 1428 testrecipe2 = 'cronie' 1429 result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1))) 1430 result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2))) 1431 result = runCmd('devtool build %s' % testrecipe1) 1432 result = runCmd('devtool build %s' % testrecipe2) 1433 stampprefix1 = get_bb_var('STAMP', testrecipe1) 1434 self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1) 1435 stampprefix2 = get_bb_var('STAMP', testrecipe2) 1436 self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2) 1437 result = runCmd('devtool reset -a') 1438 self.assertIn(testrecipe1, result.output) 1439 self.assertIn(testrecipe2, result.output) 1440 result = runCmd('devtool status') 1441 self.assertNotIn(testrecipe1, result.output) 1442 self.assertNotIn(testrecipe2, result.output) 1443 matches1 = glob.glob(stampprefix1 + '*') 1444 self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1) 1445 matches2 = glob.glob(stampprefix2 + '*') 1446 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2) 1447 1448 @OETestTag("runqemu") 1449 def test_devtool_deploy_target(self): 1450 # NOTE: Whilst this test would seemingly be better placed as a runtime test, 1451 # unfortunately the runtime tests run under bitbake and you can't run 1452 # devtool within bitbake (since devtool needs to run bitbake itself). 1453 # Additionally we are testing build-time functionality as well, so 1454 # really this has to be done as an oe-selftest test. 1455 # 1456 # Check preconditions 1457 machine = get_bb_var('MACHINE') 1458 if not machine.startswith('qemu'): 1459 self.skipTest('This test only works with qemu machines') 1460 if not os.path.exists('/etc/runqemu-nosudo'): 1461 self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test') 1462 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True) 1463 if result.status != 0: 1464 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True) 1465 if result.status != 0: 1466 self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output) 1467 for line in result.output.splitlines(): 1468 if line.startswith('tap'): 1469 break 1470 else: 1471 self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test') 1472 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1473 # Definitions 1474 testrecipe = 'mdadm' 1475 testfile = '/sbin/mdadm' 1476 testimage = 'oe-selftest-image' 1477 testcommand = '/sbin/mdadm --help' 1478 # Build an image to run 1479 bitbake("%s qemu-native qemu-helper-native" % testimage) 1480 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 1481 self.add_command_to_tearDown('bitbake -c clean %s' % testimage) 1482 self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage)) 1483 # Clean recipe so the first deploy will fail 1484 bitbake("%s -c clean" % testrecipe) 1485 # Try devtool modify 1486 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1487 self.track_for_cleanup(tempdir) 1488 self.track_for_cleanup(self.workspacedir) 1489 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) 1490 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1491 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) 1492 # Test that deploy-target at this point fails (properly) 1493 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe, ignore_status=True) 1494 self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output) 1495 self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output) 1496 result = runCmd('devtool build %s' % testrecipe) 1497 # First try a dry-run of deploy-target 1498 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe) 1499 self.assertIn(' %s' % testfile, result.output) 1500 # Boot the image 1501 with runqemu(testimage) as qemu: 1502 # Now really test deploy-target 1503 result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, qemu.ip)) 1504 # Run a test command to see if it was installed properly 1505 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' 1506 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand)) 1507 # Check if it deployed all of the files with the right ownership/perms 1508 # First look on the host - need to do this under pseudo to get the correct ownership/perms 1509 bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe) 1510 installdir = bb_vars['D'] 1511 fakerootenv = bb_vars['FAKEROOTENV'] 1512 fakerootcmd = bb_vars['FAKEROOTCMD'] 1513 result = runCmd('%s %s find . -type f -exec ls -l {} \\;' % (fakerootenv, fakerootcmd), cwd=installdir) 1514 filelist1 = self._process_ls_output(result.output) 1515 1516 # Now look on the target 1517 tempdir2 = tempfile.mkdtemp(prefix='devtoolqa') 1518 self.track_for_cleanup(tempdir2) 1519 tmpfilelist = os.path.join(tempdir2, 'files.txt') 1520 with open(tmpfilelist, 'w') as f: 1521 for line in filelist1: 1522 splitline = line.split() 1523 f.write(splitline[-1] + '\n') 1524 result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip)) 1525 filelist2 = self._process_ls_output(result.output) 1526 filelist1.sort(key=lambda item: item.split()[-1]) 1527 filelist2.sort(key=lambda item: item.split()[-1]) 1528 self.assertEqual(filelist1, filelist2) 1529 # Test undeploy-target 1530 result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip)) 1531 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True) 1532 self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') 1533 1534 def test_devtool_build_image(self): 1535 """Test devtool build-image plugin""" 1536 # Check preconditions 1537 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1538 image = 'core-image-minimal' 1539 self.track_for_cleanup(self.workspacedir) 1540 self.add_command_to_tearDown('bitbake -c clean %s' % image) 1541 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1542 bitbake('%s -c clean' % image) 1543 # Add target and native recipes to workspace 1544 recipes = ['mdadm', 'parted-native'] 1545 for recipe in recipes: 1546 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1547 self.track_for_cleanup(tempdir) 1548 self.add_command_to_tearDown('bitbake -c clean %s' % recipe) 1549 runCmd('devtool modify %s -x %s' % (recipe, tempdir)) 1550 # Try to build image 1551 result = runCmd('devtool build-image %s' % image) 1552 self.assertNotEqual(result, 0, 'devtool build-image failed') 1553 # Check if image contains expected packages 1554 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 1555 image_link_name = get_bb_var('IMAGE_LINK_NAME', image) 1556 reqpkgs = [item for item in recipes if not item.endswith('-native')] 1557 with open(os.path.join(deploy_dir_image, image_link_name + '.manifest'), 'r') as f: 1558 for line in f: 1559 splitval = line.split() 1560 if splitval: 1561 pkg = splitval[0] 1562 if pkg in reqpkgs: 1563 reqpkgs.remove(pkg) 1564 if reqpkgs: 1565 self.fail('The following packages were not present in the image as expected: %s' % ', '.join(reqpkgs)) 1566 1567class DevtoolUpgradeTests(DevtoolBase): 1568 1569 def setUp(self): 1570 super().setUp() 1571 try: 1572 runCmd("git config --global user.name") 1573 runCmd("git config --global user.email") 1574 except: 1575 self.skip("Git user.name and user.email must be set") 1576 1577 def test_devtool_upgrade(self): 1578 # Check preconditions 1579 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1580 self.track_for_cleanup(self.workspacedir) 1581 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1582 # Check parameters 1583 result = runCmd('devtool upgrade -h') 1584 for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split(): 1585 self.assertIn(param, result.output) 1586 # For the moment, we are using a real recipe. 1587 recipe = 'devtool-upgrade-test1' 1588 version = '1.6.0' 1589 oldrecipefile = get_bb_var('FILE', recipe) 1590 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1591 self.track_for_cleanup(tempdir) 1592 # Check that recipe is not already under devtool control 1593 result = runCmd('devtool status') 1594 self.assertNotIn(recipe, result.output) 1595 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that 1596 # we are downgrading instead of upgrading. 1597 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version)) 1598 # Check if srctree at least is populated 1599 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, version)) 1600 # Check new recipe subdirectory is present 1601 self.assertExists(os.path.join(self.workspacedir, 'recipes', recipe, '%s-%s' % (recipe, version)), 'Recipe folder should exist') 1602 # Check new recipe file is present 1603 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version)) 1604 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') 1605 # Check devtool status and make sure recipe is present 1606 result = runCmd('devtool status') 1607 self.assertIn(recipe, result.output) 1608 self.assertIn(tempdir, result.output) 1609 # Check recipe got changed as expected 1610 with open(oldrecipefile + '.upgraded', 'r') as f: 1611 desiredlines = f.readlines() 1612 with open(newrecipefile, 'r') as f: 1613 newlines = f.readlines() 1614 self.assertEqual(desiredlines, newlines) 1615 # Check devtool reset recipe 1616 result = runCmd('devtool reset %s -n' % recipe) 1617 result = runCmd('devtool status') 1618 self.assertNotIn(recipe, result.output) 1619 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') 1620 1621 def test_devtool_upgrade_git(self): 1622 # Check preconditions 1623 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1624 self.track_for_cleanup(self.workspacedir) 1625 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1626 recipe = 'devtool-upgrade-test2' 1627 commit = '6cc6077a36fe2648a5f993fe7c16c9632f946517' 1628 oldrecipefile = get_bb_var('FILE', recipe) 1629 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1630 self.track_for_cleanup(tempdir) 1631 # Check that recipe is not already under devtool control 1632 result = runCmd('devtool status') 1633 self.assertNotIn(recipe, result.output) 1634 # Check upgrade 1635 result = runCmd('devtool upgrade %s %s -S %s' % (recipe, tempdir, commit)) 1636 # Check if srctree at least is populated 1637 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit)) 1638 # Check new recipe file is present 1639 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile)) 1640 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') 1641 # Check devtool status and make sure recipe is present 1642 result = runCmd('devtool status') 1643 self.assertIn(recipe, result.output) 1644 self.assertIn(tempdir, result.output) 1645 # Check recipe got changed as expected 1646 with open(oldrecipefile + '.upgraded', 'r') as f: 1647 desiredlines = f.readlines() 1648 with open(newrecipefile, 'r') as f: 1649 newlines = f.readlines() 1650 self.assertEqual(desiredlines, newlines) 1651 # Check devtool reset recipe 1652 result = runCmd('devtool reset %s -n' % recipe) 1653 result = runCmd('devtool status') 1654 self.assertNotIn(recipe, result.output) 1655 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') 1656 1657 def test_devtool_layer_plugins(self): 1658 """Test that devtool can use plugins from other layers. 1659 1660 This test executes the selftest-reverse command from meta-selftest.""" 1661 1662 self.track_for_cleanup(self.workspacedir) 1663 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1664 1665 s = "Microsoft Made No Profit From Anyone's Zunes Yo" 1666 result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s) 1667 self.assertEqual(result.output, s[::-1]) 1668 1669 def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths): 1670 dstdir = basedstdir 1671 self.assertExists(dstdir) 1672 for p in paths: 1673 dstdir = os.path.join(dstdir, p) 1674 if not os.path.exists(dstdir): 1675 os.makedirs(dstdir) 1676 if p == "lib": 1677 # Can race with other tests 1678 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir) 1679 else: 1680 self.track_for_cleanup(dstdir) 1681 dstfile = os.path.join(dstdir, os.path.basename(srcfile)) 1682 if srcfile != dstfile: 1683 shutil.copy(srcfile, dstfile) 1684 self.track_for_cleanup(dstfile) 1685 1686 def test_devtool_load_plugin(self): 1687 """Test that devtool loads only the first found plugin in BBPATH.""" 1688 1689 self.track_for_cleanup(self.workspacedir) 1690 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1691 1692 devtool = runCmd("which devtool") 1693 fromname = runCmd("devtool --quiet pluginfile") 1694 srcfile = fromname.output 1695 bbpath = get_bb_var('BBPATH') 1696 searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)] 1697 plugincontent = [] 1698 with open(srcfile) as fh: 1699 plugincontent = fh.readlines() 1700 try: 1701 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found') 1702 for path in searchpath: 1703 self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool') 1704 result = runCmd("devtool --quiet count") 1705 self.assertEqual(result.output, '1') 1706 result = runCmd("devtool --quiet multiloaded") 1707 self.assertEqual(result.output, "no") 1708 for path in searchpath: 1709 result = runCmd("devtool --quiet bbdir") 1710 self.assertEqual(result.output, path) 1711 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py')) 1712 finally: 1713 with open(srcfile, 'w') as fh: 1714 fh.writelines(plugincontent) 1715 1716 def _setup_test_devtool_finish_upgrade(self): 1717 # Check preconditions 1718 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1719 self.track_for_cleanup(self.workspacedir) 1720 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1721 # Use a "real" recipe from meta-selftest 1722 recipe = 'devtool-upgrade-test1' 1723 oldversion = '1.5.3' 1724 newversion = '1.6.0' 1725 oldrecipefile = get_bb_var('FILE', recipe) 1726 recipedir = os.path.dirname(oldrecipefile) 1727 result = runCmd('git status --porcelain .', cwd=recipedir) 1728 if result.output.strip(): 1729 self.fail('Recipe directory for %s contains uncommitted changes' % recipe) 1730 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1731 self.track_for_cleanup(tempdir) 1732 # Check that recipe is not already under devtool control 1733 result = runCmd('devtool status') 1734 self.assertNotIn(recipe, result.output) 1735 # Do the upgrade 1736 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion)) 1737 # Check devtool status and make sure recipe is present 1738 result = runCmd('devtool status') 1739 self.assertIn(recipe, result.output) 1740 self.assertIn(tempdir, result.output) 1741 # Make a change to the source 1742 result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir) 1743 result = runCmd('git status --porcelain', cwd=tempdir) 1744 self.assertIn('M src/pv/number.c', result.output) 1745 result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir) 1746 # Check if patch is there 1747 recipedir = os.path.dirname(oldrecipefile) 1748 olddir = os.path.join(recipedir, recipe + '-' + oldversion) 1749 patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch' 1750 backportedpatchfn = 'backported.patch' 1751 self.assertExists(os.path.join(olddir, patchfn), 'Original patch file does not exist') 1752 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Backported patch file does not exist') 1753 return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn 1754 1755 def test_devtool_finish_upgrade_origlayer(self): 1756 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade() 1757 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 1758 self.assertIn('/meta-selftest/', recipedir) 1759 # Try finish to the original layer 1760 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 1761 result = runCmd('devtool finish %s meta-selftest' % recipe) 1762 result = runCmd('devtool status') 1763 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 1764 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 1765 self.assertNotExists(oldrecipefile, 'Old recipe file should have been deleted but wasn\'t') 1766 self.assertNotExists(os.path.join(olddir, patchfn), 'Old patch file should have been deleted but wasn\'t') 1767 self.assertNotExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should have been deleted but wasn\'t') 1768 newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion)) 1769 newdir = os.path.join(recipedir, recipe + '-' + newversion) 1770 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t') 1771 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t') 1772 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was') 1773 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t') 1774 with open(newrecipefile, 'r') as f: 1775 newcontent = f.read() 1776 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't") 1777 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was") 1778 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't") 1779 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI") 1780 1781 1782 def test_devtool_finish_upgrade_otherlayer(self): 1783 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade() 1784 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 1785 self.assertIn('/meta-selftest/', recipedir) 1786 # Try finish to a different layer - should create a bbappend 1787 # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here 1788 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 1789 oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta') 1790 newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool') 1791 newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion)) 1792 self.track_for_cleanup(newrecipedir) 1793 result = runCmd('devtool finish %s oe-core' % recipe) 1794 result = runCmd('devtool status') 1795 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 1796 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 1797 self.assertExists(oldrecipefile, 'Old recipe file should not have been deleted') 1798 self.assertExists(os.path.join(olddir, patchfn), 'Old patch file should not have been deleted') 1799 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should not have been deleted') 1800 newdir = os.path.join(newrecipedir, recipe + '-' + newversion) 1801 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t') 1802 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t') 1803 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was') 1804 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t') 1805 with open(newrecipefile, 'r') as f: 1806 newcontent = f.read() 1807 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't") 1808 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was") 1809 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't") 1810 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI") 1811 1812 def _setup_test_devtool_finish_modify(self): 1813 # Check preconditions 1814 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1815 # Try modifying a recipe 1816 self.track_for_cleanup(self.workspacedir) 1817 recipe = 'mdadm' 1818 oldrecipefile = get_bb_var('FILE', recipe) 1819 recipedir = os.path.dirname(oldrecipefile) 1820 result = runCmd('git status --porcelain .', cwd=recipedir) 1821 if result.output.strip(): 1822 self.fail('Recipe directory for %s contains uncommitted changes' % recipe) 1823 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1824 self.track_for_cleanup(tempdir) 1825 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1826 result = runCmd('devtool modify %s %s' % (recipe, tempdir)) 1827 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found') 1828 # Test devtool status 1829 result = runCmd('devtool status') 1830 self.assertIn(recipe, result.output) 1831 self.assertIn(tempdir, result.output) 1832 # Make a change to the source 1833 result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir) 1834 result = runCmd('git status --porcelain', cwd=tempdir) 1835 self.assertIn('M maps.c', result.output) 1836 result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir) 1837 for entry in os.listdir(recipedir): 1838 filesdir = os.path.join(recipedir, entry) 1839 if os.path.isdir(filesdir): 1840 break 1841 else: 1842 self.fail('Unable to find recipe files directory for %s' % recipe) 1843 return recipe, oldrecipefile, recipedir, filesdir 1844 1845 def test_devtool_finish_modify_origlayer(self): 1846 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify() 1847 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 1848 self.assertIn('/meta/', recipedir) 1849 # Try finish to the original layer 1850 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 1851 result = runCmd('devtool finish %s meta' % recipe) 1852 result = runCmd('devtool status') 1853 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 1854 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 1855 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)), 1856 ('??', '.*/.*-Add-a-comment-to-the-code.patch$')] 1857 self._check_repo_status(recipedir, expected_status) 1858 1859 def test_devtool_finish_modify_otherlayer(self): 1860 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify() 1861 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things) 1862 self.assertIn('/meta/', recipedir) 1863 relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta')) 1864 appenddir = os.path.join(get_test_layer(), relpth) 1865 self.track_for_cleanup(appenddir) 1866 # Try finish to the original layer 1867 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir)) 1868 result = runCmd('devtool finish %s meta-selftest' % recipe) 1869 result = runCmd('devtool status') 1870 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t') 1871 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish') 1872 result = runCmd('git status --porcelain .', cwd=recipedir) 1873 if result.output.strip(): 1874 self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip())) 1875 recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0] 1876 recipefn = recipefn.split('_')[0] + '_%' 1877 appendfile = os.path.join(appenddir, recipefn + '.bbappend') 1878 self.assertExists(appendfile, 'bbappend %s should have been created but wasn\'t' % appendfile) 1879 newdir = os.path.join(appenddir, recipe) 1880 files = os.listdir(newdir) 1881 foundpatch = None 1882 for fn in files: 1883 if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'): 1884 foundpatch = fn 1885 if not foundpatch: 1886 self.fail('No patch file created next to bbappend') 1887 files.remove(foundpatch) 1888 if files: 1889 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files)) 1890 1891 def test_devtool_rename(self): 1892 # Check preconditions 1893 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') 1894 self.track_for_cleanup(self.workspacedir) 1895 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1896 1897 # First run devtool add 1898 # We already have this recipe in OE-Core, but that doesn't matter 1899 recipename = 'i2c-tools' 1900 recipever = '3.1.2' 1901 recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever)) 1902 url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever 1903 def add_recipe(): 1904 result = runCmd('devtool add %s' % url) 1905 self.assertExists(recipefile, 'Expected recipe file not created') 1906 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory not created') 1907 checkvars = {} 1908 checkvars['S'] = None 1909 checkvars['SRC_URI'] = url.replace(recipever, '${PV}') 1910 self._test_recipe_contents(recipefile, checkvars, []) 1911 add_recipe() 1912 # Now rename it - change both name and version 1913 newrecipename = 'mynewrecipe' 1914 newrecipever = '456' 1915 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever)) 1916 result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever)) 1917 self.assertExists(newrecipefile, 'Recipe file not renamed') 1918 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists') 1919 newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename) 1920 self.assertExists(newsrctree, 'Source directory not renamed') 1921 checkvars = {} 1922 checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever) 1923 checkvars['SRC_URI'] = url 1924 self._test_recipe_contents(newrecipefile, checkvars, []) 1925 # Try again - change just name this time 1926 result = runCmd('devtool reset -n %s' % newrecipename) 1927 shutil.rmtree(newsrctree) 1928 add_recipe() 1929 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever)) 1930 result = runCmd('devtool rename %s %s' % (recipename, newrecipename)) 1931 self.assertExists(newrecipefile, 'Recipe file not renamed') 1932 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists') 1933 self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed') 1934 checkvars = {} 1935 checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename 1936 checkvars['SRC_URI'] = url.replace(recipever, '${PV}') 1937 self._test_recipe_contents(newrecipefile, checkvars, []) 1938 # Try again - change just version this time 1939 result = runCmd('devtool reset -n %s' % newrecipename) 1940 shutil.rmtree(newsrctree) 1941 add_recipe() 1942 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever)) 1943 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever)) 1944 self.assertExists(newrecipefile, 'Recipe file not renamed') 1945 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists') 1946 checkvars = {} 1947 checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever 1948 checkvars['SRC_URI'] = url 1949 self._test_recipe_contents(newrecipefile, checkvars, []) 1950 1951 def test_devtool_virtual_kernel_modify(self): 1952 """ 1953 Summary: The purpose of this test case is to verify that 1954 devtool modify works correctly when building 1955 the kernel. 1956 Dependencies: NA 1957 Steps: 1. Build kernel with bitbake. 1958 2. Save the config file generated. 1959 3. Clean the environment. 1960 4. Use `devtool modify virtual/kernel` to validate following: 1961 4.1 The source is checked out correctly. 1962 4.2 The resulting configuration is the same as 1963 what was get on step 2. 1964 4.3 The Kernel can be build correctly. 1965 4.4 Changes made on the source are reflected on the 1966 subsequent builds. 1967 4.5 Changes on the configuration are reflected on the 1968 subsequent builds 1969 Expected: devtool modify is able to checkout the source of the kernel 1970 and modification to the source and configurations are reflected 1971 when building the kernel. 1972 """ 1973 kernel_provider = self.td['PREFERRED_PROVIDER_virtual/kernel'] 1974 1975 # Clean up the environment 1976 bitbake('%s -c clean' % kernel_provider) 1977 tempdir = tempfile.mkdtemp(prefix='devtoolqa') 1978 tempdir_cfg = tempfile.mkdtemp(prefix='config_qa') 1979 self.track_for_cleanup(tempdir) 1980 self.track_for_cleanup(tempdir_cfg) 1981 self.track_for_cleanup(self.workspacedir) 1982 self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider) 1983 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') 1984 #Step 1 1985 #Here is just generated the config file instead of all the kernel to optimize the 1986 #time of executing this test case. 1987 bitbake('%s -c configure' % kernel_provider) 1988 bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config') 1989 #Step 2 1990 runCmd('cp %s %s' % (bbconfig, tempdir_cfg)) 1991 self.assertExists(os.path.join(tempdir_cfg, '.config'), 'Could not copy .config file from kernel') 1992 1993 tmpconfig = os.path.join(tempdir_cfg, '.config') 1994 #Step 3 1995 bitbake('%s -c clean' % kernel_provider) 1996 #Step 4.1 1997 runCmd('devtool modify virtual/kernel -x %s' % tempdir) 1998 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found') 1999 #Step 4.2 2000 configfile = os.path.join(tempdir,'.config') 2001 runCmd('diff %s %s' % (tmpconfig, configfile)) 2002 2003 #Step 4.3 2004 #NOTE: virtual/kernel is mapped to kernel_provider 2005 runCmd('devtool build %s' % kernel_provider) 2006 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux') 2007 self.assertExists(kernelfile, 'Kernel was not build correctly') 2008 2009 #Modify the kernel source 2010 modfile = os.path.join(tempdir, 'init/version.c') 2011 runCmd("sed -i 's/Linux/LiNuX/g' %s" % (modfile)) 2012 2013 #Modify the configuration 2014 codeconfigfile = os.path.join(tempdir, '.config.new') 2015 modconfopt = "CONFIG_SG_POOL=n" 2016 runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile)) 2017 2018 #Build again kernel with devtool 2019 runCmd('devtool build %s' % kernel_provider) 2020 2021 #Step 4.4 2022 runCmd("grep '%s' %s" % ('LiNuX', kernelfile)) 2023 2024 #Step 4.5 2025 runCmd("grep %s %s" % (modconfopt, codeconfigfile)) 2026