blob: ecff3a9ebdf316b6f880ae823a053ed6483ffad7 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Patrick Williams92b42cb2022-09-03 06:53:57 -05002# Copyright OpenEmbedded Contributors
3#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: MIT
5#
6
Patrick Williams169d7bc2024-01-05 11:33:25 -06007import errno
Brad Bishopd7bf8c12018-02-25 22:55:05 -05008import os
9import re
10import shutil
11import tempfile
12import glob
13import fnmatch
Patrick Williamse760df82023-05-26 11:10:49 -050014import unittest
Patrick Williams73bd93f2024-02-20 08:07:48 -060015import json
Brad Bishopd7bf8c12018-02-25 22:55:05 -050016
Brad Bishopd7bf8c12018-02-25 22:55:05 -050017from oeqa.selftest.case import OESelftestTestCase
18from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
19from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer
Patrick Williams45852732022-04-02 08:58:32 -050020from oeqa.core.decorator import OETestTag
Brad Bishopd7bf8c12018-02-25 22:55:05 -050021
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080022oldmetapath = None
23
24def setUpModule():
25 import bb.utils
26
27 global templayerdir
28 templayerdir = tempfile.mkdtemp(prefix='devtoolqa')
29 corecopydir = os.path.join(templayerdir, 'core-copy')
30 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
31 edited_layers = []
Patrick Williamsac13d5f2023-11-24 18:59:46 -060032 # make sure user doesn't have a local workspace
33 result = runCmd('bitbake-layers show-layers')
34 assert "workspacelayer" not in result.output, "Devtool test suite cannot be run with a local workspace directory"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080035
36 # We need to take a copy of the meta layer so we can modify it and not
37 # have any races against other tests that might be running in parallel
38 # however things like COREBASE mean that you can't just copy meta, you
39 # need the whole repository.
40 def bblayers_edit_cb(layerpath, canonical_layerpath):
41 global oldmetapath
42 if not canonical_layerpath.endswith('/'):
43 # This helps us match exactly when we're using this path later
44 canonical_layerpath += '/'
45 if not edited_layers and canonical_layerpath.endswith('/meta/'):
46 canonical_layerpath = os.path.realpath(canonical_layerpath) + '/'
47 edited_layers.append(layerpath)
48 oldmetapath = os.path.realpath(layerpath)
Patrick Williamse760df82023-05-26 11:10:49 -050049
50 # when downloading poky from tar.gz some tests will be skipped (BUG 12389)
51 try:
52 runCmd('git rev-parse --is-inside-work-tree', cwd=canonical_layerpath)
53 except:
54 raise unittest.SkipTest("devtool tests require folder to be a git repo")
55
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080056 result = runCmd('git rev-parse --show-toplevel', cwd=canonical_layerpath)
57 oldreporoot = result.output.rstrip()
58 newmetapath = os.path.join(corecopydir, os.path.relpath(oldmetapath, oldreporoot))
Patrick Williams169d7bc2024-01-05 11:33:25 -060059 runCmd('git clone file://%s %s' % (oldreporoot, corecopydir), cwd=templayerdir)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080060 # Now we need to copy any modified files
61 # You might ask "why not just copy the entire tree instead of
62 # cloning and doing this?" - well, the problem with that is
63 # TMPDIR or an equally large subdirectory might exist
64 # under COREBASE and we don't want to copy that, so we have
65 # to be selective.
66 result = runCmd('git status --porcelain', cwd=oldreporoot)
67 for line in result.output.splitlines():
68 if line.startswith(' M ') or line.startswith('?? '):
69 relpth = line.split()[1]
70 pth = os.path.join(oldreporoot, relpth)
71 if pth.startswith(canonical_layerpath):
72 if relpth.endswith('/'):
73 destdir = os.path.join(corecopydir, relpth)
Andrew Geisslerc3d88e42020-10-02 09:45:00 -050074 # avoid race condition by not copying .pyc files YPBZ#13421,13803
Andrew Geisslerd1e89492021-02-12 15:35:20 -060075 shutil.copytree(pth, destdir, ignore=shutil.ignore_patterns('*.pyc', '__pycache__'))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080076 else:
77 destdir = os.path.join(corecopydir, os.path.dirname(relpth))
78 bb.utils.mkdirhier(destdir)
79 shutil.copy2(pth, destdir)
80 return newmetapath
81 else:
82 return layerpath
83 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
84
85def tearDownModule():
86 if oldmetapath:
87 edited_layers = []
88 def bblayers_edit_cb(layerpath, canonical_layerpath):
89 if not edited_layers and canonical_layerpath.endswith('/meta'):
90 edited_layers.append(layerpath)
91 return oldmetapath
92 else:
93 return layerpath
94 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
95 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
96 shutil.rmtree(templayerdir)
97
Andrew Geissler595f6302022-01-24 19:11:47 +000098class DevtoolTestCase(OESelftestTestCase):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080099
100 def setUp(self):
101 """Test case setup function"""
Andrew Geissler595f6302022-01-24 19:11:47 +0000102 super(DevtoolTestCase, self).setUp()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800103 self.workspacedir = os.path.join(self.builddir, 'workspace')
104 self.assertTrue(not os.path.exists(self.workspacedir),
105 'This test cannot be run with a workspace directory '
106 'under the build directory')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800107
108 def _check_src_repo(self, repo_dir):
109 """Check srctree git repository"""
110 self.assertTrue(os.path.isdir(os.path.join(repo_dir, '.git')),
111 'git repository for external source tree not found')
112 result = runCmd('git status --porcelain', cwd=repo_dir)
113 self.assertEqual(result.output.strip(), "",
114 'Created git repo is not clean')
115 result = runCmd('git symbolic-ref HEAD', cwd=repo_dir)
116 self.assertEqual(result.output.strip(), "refs/heads/devtool",
117 'Wrong branch in git repo')
118
119 def _check_repo_status(self, repo_dir, expected_status):
120 """Check the worktree status of a repository"""
121 result = runCmd('git status . --porcelain',
122 cwd=repo_dir)
123 for line in result.output.splitlines():
124 for ind, (f_status, fn_re) in enumerate(expected_status):
125 if re.match(fn_re, line[3:]):
126 if f_status != line[:2]:
127 self.fail('Unexpected status in line: %s' % line)
128 expected_status.pop(ind)
129 break
130 else:
131 self.fail('Unexpected modified file in line: %s' % line)
132 if expected_status:
133 self.fail('Missing file changes: %s' % expected_status)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500134
135 def _test_recipe_contents(self, recipefile, checkvars, checkinherits):
136 with open(recipefile, 'r') as f:
137 invar = None
138 invalue = None
Brad Bishop6dbb3162019-11-25 09:41:34 -0500139 inherits = set()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500140 for line in f:
141 var = None
142 if invar:
143 value = line.strip().strip('"')
144 if value.endswith('\\'):
145 invalue += ' ' + value[:-1].strip()
146 continue
147 else:
148 invalue += ' ' + value.strip()
149 var = invar
150 value = invalue
151 invar = None
152 elif '=' in line:
153 splitline = line.split('=', 1)
154 var = splitline[0].rstrip()
155 value = splitline[1].strip().strip('"')
156 if value.endswith('\\'):
157 invalue = value[:-1].strip()
158 invar = var
159 continue
160 elif line.startswith('inherit '):
Brad Bishop6dbb3162019-11-25 09:41:34 -0500161 inherits.update(line.split()[1:])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500162
163 if var and var in checkvars:
164 needvalue = checkvars.pop(var)
165 if needvalue is None:
166 self.fail('Variable %s should not appear in recipe, but value is being set to "%s"' % (var, value))
167 if isinstance(needvalue, set):
168 if var == 'LICENSE':
169 value = set(value.split(' & '))
170 else:
171 value = set(value.split())
172 self.assertEqual(value, needvalue, 'values for %s do not match' % var)
173
174
175 missingvars = {}
176 for var, value in checkvars.items():
177 if value is not None:
178 missingvars[var] = value
179 self.assertEqual(missingvars, {}, 'Some expected variables not found in recipe: %s' % checkvars)
180
181 for inherit in checkinherits:
182 self.assertIn(inherit, inherits, 'Missing inherit of %s' % inherit)
183
184 def _check_bbappend(self, testrecipe, recipefile, appenddir):
185 result = runCmd('bitbake-layers show-appends', cwd=self.builddir)
186 resultlines = result.output.splitlines()
187 inrecipe = False
188 bbappends = []
189 bbappendfile = None
190 for line in resultlines:
191 if inrecipe:
192 if line.startswith(' '):
193 bbappends.append(line.strip())
194 else:
195 break
196 elif line == '%s:' % os.path.basename(recipefile):
197 inrecipe = True
198 self.assertLessEqual(len(bbappends), 2, '%s recipe is being bbappended by another layer - bbappends found:\n %s' % (testrecipe, '\n '.join(bbappends)))
199 for bbappend in bbappends:
200 if bbappend.startswith(appenddir):
201 bbappendfile = bbappend
202 break
203 else:
204 self.fail('bbappend for recipe %s does not seem to be created in test layer' % testrecipe)
205 return bbappendfile
206
207 def _create_temp_layer(self, templayerdir, addlayer, templayername, priority=999, recipepathspec='recipes-*/*'):
208 create_temp_layer(templayerdir, templayername, priority, recipepathspec)
209 if addlayer:
210 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
211 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
212
213 def _process_ls_output(self, output):
214 """
215 Convert ls -l output to a format we can reasonably compare from one context
216 to another (e.g. from host to target)
217 """
218 filelist = []
219 for line in output.splitlines():
220 splitline = line.split()
221 if len(splitline) < 8:
222 self.fail('_process_ls_output: invalid output line: %s' % line)
223 # Remove trailing . on perms
224 splitline[0] = splitline[0].rstrip('.')
225 # Remove leading . on paths
226 splitline[-1] = splitline[-1].lstrip('.')
227 # Drop fields we don't want to compare
228 del splitline[7]
229 del splitline[6]
230 del splitline[5]
231 del splitline[4]
232 del splitline[1]
233 filelist.append(' '.join(splitline))
234 return filelist
235
Andrew Geissler615f2f12022-07-15 14:00:58 -0500236 def _check_diff(self, diffoutput, addlines, removelines):
237 """Check output from 'git diff' matches expectation"""
238 remaining_addlines = addlines[:]
239 remaining_removelines = removelines[:]
240 for line in diffoutput.splitlines():
241 if line.startswith('+++') or line.startswith('---'):
242 continue
243 elif line.startswith('+'):
244 matched = False
245 for item in addlines:
246 if re.match(item, line[1:].strip()):
247 matched = True
248 remaining_addlines.remove(item)
249 break
250 self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
251 elif line.startswith('-'):
252 matched = False
253 for item in removelines:
254 if re.match(item, line[1:].strip()):
255 matched = True
256 remaining_removelines.remove(item)
257 break
258 self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
259 if remaining_addlines:
260 self.fail('Expected added lines not found: %s' % remaining_addlines)
261 if remaining_removelines:
262 self.fail('Expected removed lines not found: %s' % remaining_removelines)
263
Andrew Geissler220dafd2023-10-04 10:18:08 -0500264 def _check_runqemu_prerequisites(self):
265 """Check runqemu is available
266
267 Whilst some tests would seemingly be better placed as a runtime test,
268 unfortunately the runtime tests run under bitbake and you can't run
269 devtool within bitbake (since devtool needs to run bitbake itself).
270 Additionally we are testing build-time functionality as well, so
271 really this has to be done as an oe-selftest test.
272 """
273 machine = get_bb_var('MACHINE')
274 if not machine.startswith('qemu'):
275 self.skipTest('This test only works with qemu machines')
276 if not os.path.exists('/etc/runqemu-nosudo'):
277 self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
278 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True)
279 if result.status != 0:
280 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True)
281 if result.status != 0:
282 self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output)
283 for line in result.output.splitlines():
284 if line.startswith('tap'):
285 break
286 else:
287 self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
288
Patrick Williams92b42cb2022-09-03 06:53:57 -0500289 def _test_devtool_add_git_url(self, git_url, version, pn, resulting_src_uri):
290 self.track_for_cleanup(self.workspacedir)
291 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
292 result = runCmd('devtool add --version %s %s %s' % (version, pn, git_url))
293 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
294 # Check the recipe name is correct
295 recipefile = get_bb_var('FILE', pn)
296 self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
297 self.assertIn(recipefile, result.output)
298 # Test devtool status
299 result = runCmd('devtool status')
300 self.assertIn(pn, result.output)
301 self.assertIn(recipefile, result.output)
302 checkvars = {}
303 checkvars['SRC_URI'] = resulting_src_uri
304 self._test_recipe_contents(recipefile, checkvars, [])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500305
Andrew Geissler595f6302022-01-24 19:11:47 +0000306class DevtoolBase(DevtoolTestCase):
307
308 @classmethod
309 def setUpClass(cls):
310 super(DevtoolBase, cls).setUpClass()
311 bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
312 cls.original_sstate = bb_vars['SSTATE_DIR']
313 cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
314 cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
315 cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
316 % cls.original_sstate)
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500317 cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687"\n')
Andrew Geissler595f6302022-01-24 19:11:47 +0000318
319 @classmethod
320 def tearDownClass(cls):
321 cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
322 runCmd('rm -rf %s' % cls.devtool_sstate)
323 super(DevtoolBase, cls).tearDownClass()
324
325 def setUp(self):
326 """Test case setup function"""
327 super(DevtoolBase, self).setUp()
328 self.append_config(self.sstate_conf)
329
330
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500331class DevtoolTests(DevtoolBase):
332
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500333 def test_create_workspace(self):
334 # Check preconditions
335 result = runCmd('bitbake-layers show-layers')
Brad Bishop316dfdd2018-06-25 12:45:53 -0400336 self.assertTrue('\nworkspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf')
Brad Bishop96ff1982019-08-19 13:50:42 -0400337 # remove conf/devtool.conf to avoid it corrupting tests
338 devtoolconf = os.path.join(self.builddir, 'conf', 'devtool.conf')
339 self.track_for_cleanup(devtoolconf)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500340 # Try creating a workspace layer with a specific path
341 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
342 self.track_for_cleanup(tempdir)
343 result = runCmd('devtool create-workspace %s' % tempdir)
344 self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
345 result = runCmd('bitbake-layers show-layers')
346 self.assertIn(tempdir, result.output)
347 # Try creating a workspace layer with the default path
348 self.track_for_cleanup(self.workspacedir)
349 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
350 result = runCmd('devtool create-workspace')
351 self.assertTrue(os.path.isfile(os.path.join(self.workspacedir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
352 result = runCmd('bitbake-layers show-layers')
353 self.assertNotIn(tempdir, result.output)
354 self.assertIn(self.workspacedir, result.output)
355
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800356class DevtoolAddTests(DevtoolBase):
357
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500358 def test_devtool_add(self):
359 # Fetch source
360 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
361 self.track_for_cleanup(tempdir)
362 pn = 'pv'
363 pv = '1.5.3'
Andrew Geissler09209ee2020-12-13 08:44:15 -0600364 url = 'http://downloads.yoctoproject.org/mirror/sources/pv-1.5.3.tar.bz2'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500365 result = runCmd('wget %s' % url, cwd=tempdir)
366 result = runCmd('tar xfv %s' % os.path.basename(url), cwd=tempdir)
367 srcdir = os.path.join(tempdir, '%s-%s' % (pn, pv))
368 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
369 # Test devtool add
370 self.track_for_cleanup(self.workspacedir)
371 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
372 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
373 result = runCmd('devtool add %s %s' % (pn, srcdir))
374 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
375 # Test devtool status
376 result = runCmd('devtool status')
377 recipepath = '%s/recipes/%s/%s_%s.bb' % (self.workspacedir, pn, pn, pv)
378 self.assertIn(recipepath, result.output)
379 self.assertIn(srcdir, result.output)
380 # Test devtool find-recipe
381 result = runCmd('devtool -q find-recipe %s' % pn)
382 self.assertEqual(recipepath, result.output.strip())
383 # Test devtool edit-recipe
384 result = runCmd('VISUAL="echo 123" devtool -q edit-recipe %s' % pn)
385 self.assertEqual('123 %s' % recipepath, result.output.strip())
386 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
387 bitbake('%s -c cleansstate' % pn)
388 # Test devtool build
389 result = runCmd('devtool build %s' % pn)
390 bb_vars = get_bb_vars(['D', 'bindir'], pn)
391 installdir = bb_vars['D']
392 self.assertTrue(installdir, 'Could not query installdir variable')
393 bindir = bb_vars['bindir']
394 self.assertTrue(bindir, 'Could not query bindir variable')
395 if bindir[0] == '/':
396 bindir = bindir[1:]
397 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
398
Andrew Geissler8f840682023-07-21 09:09:43 -0500399 def test_devtool_add_binary(self):
400 # Create a binary package containing a known test file
401 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
402 self.track_for_cleanup(tempdir)
403 pn = 'tst-bin'
404 pv = '1.0'
405 test_file_dir = "var/lib/%s/" % pn
406 test_file_name = "test_file"
407 test_file_content = "TEST CONTENT"
408 test_file_package_root = os.path.join(tempdir, pn)
409 test_file_dir_full = os.path.join(test_file_package_root, test_file_dir)
410 bb.utils.mkdirhier(test_file_dir_full)
411 with open(os.path.join(test_file_dir_full, test_file_name), "w") as f:
412 f.write(test_file_content)
413 bin_package_path = os.path.join(tempdir, "%s.tar.gz" % pn)
414 runCmd("tar czf %s -C %s ." % (bin_package_path, test_file_package_root))
415
416 # Test devtool add -b on the binary package
417 self.track_for_cleanup(self.workspacedir)
418 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
419 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
420 result = runCmd('devtool add -b %s %s' % (pn, bin_package_path))
421 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
422
423 # Build the resulting recipe
424 result = runCmd('devtool build %s' % pn)
425 installdir = get_bb_var('D', pn)
426 self.assertTrue(installdir, 'Could not query installdir variable')
427
428 # Check that a known file from the binary package has indeed been installed
429 self.assertTrue(os.path.isfile(os.path.join(installdir, test_file_dir, test_file_name)), '%s not found in D' % test_file_name)
430
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500431 def test_devtool_add_git_local(self):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800432 # We need dbus built so that DEPENDS recognition works
433 bitbake('dbus')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500434 # Fetch source from a remote URL, but do it outside of devtool
435 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
436 self.track_for_cleanup(tempdir)
437 pn = 'dbus-wait'
438 srcrev = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
439 # We choose an https:// git URL here to check rewriting the URL works
440 url = 'https://git.yoctoproject.org/git/dbus-wait'
441 # Force fetching to "noname" subdir so we verify we're picking up the name from autoconf
442 # instead of the directory name
443 result = runCmd('git clone %s noname' % url, cwd=tempdir)
444 srcdir = os.path.join(tempdir, 'noname')
445 result = runCmd('git reset --hard %s' % srcrev, cwd=srcdir)
446 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory')
447 # Test devtool add
448 self.track_for_cleanup(self.workspacedir)
449 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
450 # Don't specify a name since we should be able to auto-detect it
451 result = runCmd('devtool add %s' % srcdir)
452 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
453 # Check the recipe name is correct
454 recipefile = get_bb_var('FILE', pn)
455 self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
456 self.assertIn(recipefile, result.output)
457 # Test devtool status
458 result = runCmd('devtool status')
459 self.assertIn(pn, result.output)
460 self.assertIn(srcdir, result.output)
461 self.assertIn(recipefile, result.output)
462 checkvars = {}
Andrew Geissler9aee5002022-03-30 16:27:02 +0000463 checkvars['LICENSE'] = 'GPL-2.0-only'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500464 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
465 checkvars['S'] = '${WORKDIR}/git'
Andrew Geissler5082cc72023-09-11 08:41:39 -0400466 checkvars['PV'] = '0.1+git'
Andrew Geissler595f6302022-01-24 19:11:47 +0000467 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https;branch=master'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500468 checkvars['SRCREV'] = srcrev
469 checkvars['DEPENDS'] = set(['dbus'])
470 self._test_recipe_contents(recipefile, checkvars, [])
471
Patrick Williams92b42cb2022-09-03 06:53:57 -0500472 def test_devtool_add_git_style1(self):
473 version = 'v3.1.0'
474 pn = 'mbedtls'
475 # this will trigger reformat_git_uri with branch parameter in url
476 git_url = "'git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https'"
477 resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https"
478 self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri)
479
480 def test_devtool_add_git_style2(self):
481 version = 'v3.1.0'
482 pn = 'mbedtls'
483 # this will trigger reformat_git_uri with branch parameter in url
484 git_url = "'git://git@github.com/ARMmbed/mbedtls.git;protocol=https'"
485 resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;protocol=https;branch=master"
486 self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri)
487
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500488 def test_devtool_add_library(self):
489 # Fetch source
490 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
491 self.track_for_cleanup(tempdir)
492 version = '1.1'
493 url = 'https://www.intra2net.com/en/developer/libftdi/download/libftdi1-%s.tar.bz2' % version
494 result = runCmd('wget %s' % url, cwd=tempdir)
495 result = runCmd('tar xfv libftdi1-%s.tar.bz2' % version, cwd=tempdir)
496 srcdir = os.path.join(tempdir, 'libftdi1-%s' % version)
497 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory')
498 # Test devtool add (and use -V so we test that too)
499 self.track_for_cleanup(self.workspacedir)
500 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
501 result = runCmd('devtool add libftdi %s -V %s' % (srcdir, version))
502 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
503 # Test devtool status
504 result = runCmd('devtool status')
505 self.assertIn('libftdi', result.output)
506 self.assertIn(srcdir, result.output)
507 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
508 bitbake('libftdi -c cleansstate')
509 # libftdi's python/CMakeLists.txt is a bit broken, so let's just disable it
510 # There's also the matter of it installing cmake files to a path we don't
511 # normally cover, which triggers the installed-vs-shipped QA test we have
512 # within do_package
513 recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version)
514 result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile)
515 with open(recipefile, 'a') as f:
Patrick Williams213cb262021-08-07 19:21:33 -0500516 f.write('\nFILES:${PN}-dev += "${datadir}/cmake/Modules"\n')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500517 # We don't have the ability to pick up this dependency automatically yet...
518 f.write('\nDEPENDS += "libusb1"\n')
519 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
520 # Test devtool build
521 result = runCmd('devtool build libftdi')
522 bb_vars = get_bb_vars(['TESTLIBOUTPUT', 'STAMP'], 'libftdi')
523 staging_libdir = bb_vars['TESTLIBOUTPUT']
524 self.assertTrue(staging_libdir, 'Could not query TESTLIBOUTPUT variable')
525 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)
526 # Test devtool reset
527 stampprefix = bb_vars['STAMP']
528 result = runCmd('devtool reset libftdi')
529 result = runCmd('devtool status')
530 self.assertNotIn('libftdi', result.output)
531 self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi')
532 matches = glob.glob(stampprefix + '*')
533 self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned')
534 self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning')
535
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500536 def test_devtool_add_fetch(self):
537 # Fetch source
538 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
539 self.track_for_cleanup(tempdir)
540 testver = '0.23'
Brad Bishop15ae2502019-06-18 21:44:24 -0400541 url = 'https://files.pythonhosted.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-%s.tar.gz' % testver
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500542 testrecipe = 'python-markupsafe'
543 srcdir = os.path.join(tempdir, testrecipe)
544 # Test devtool add
545 self.track_for_cleanup(self.workspacedir)
546 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
547 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Patrick Williams169d7bc2024-01-05 11:33:25 -0600548 result = runCmd('devtool add --no-pypi %s %s -f %s' % (testrecipe, srcdir, url))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500549 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output)
550 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
551 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
552 # Test devtool status
553 result = runCmd('devtool status')
554 self.assertIn(testrecipe, result.output)
555 self.assertIn(srcdir, result.output)
556 # Check recipe
557 recipefile = get_bb_var('FILE', testrecipe)
558 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
559 checkvars = {}
560 checkvars['S'] = '${WORKDIR}/MarkupSafe-${PV}'
561 checkvars['SRC_URI'] = url.replace(testver, '${PV}')
562 self._test_recipe_contents(recipefile, checkvars, [])
563 # Try with version specified
564 result = runCmd('devtool reset -n %s' % testrecipe)
565 shutil.rmtree(srcdir)
566 fakever = '1.9'
Patrick Williams169d7bc2024-01-05 11:33:25 -0600567 result = runCmd('devtool add --no-pypi %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500568 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
569 # Test devtool status
570 result = runCmd('devtool status')
571 self.assertIn(testrecipe, result.output)
572 self.assertIn(srcdir, result.output)
573 # Check recipe
574 recipefile = get_bb_var('FILE', testrecipe)
575 self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named')
576 checkvars = {}
577 checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver
578 checkvars['SRC_URI'] = url
579 self._test_recipe_contents(recipefile, checkvars, [])
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600580
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500581 def test_devtool_add_fetch_git(self):
582 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
583 self.track_for_cleanup(tempdir)
584 url = 'gitsm://git.yoctoproject.org/mraa'
Andrew Geissler595f6302022-01-24 19:11:47 +0000585 url_branch = '%s;branch=master' % url
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500586 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
587 testrecipe = 'mraa'
588 srcdir = os.path.join(tempdir, testrecipe)
589 # Test devtool add
590 self.track_for_cleanup(self.workspacedir)
591 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
592 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
593 result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url))
594 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created: %s' % result.output)
595 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
596 # Test devtool status
597 result = runCmd('devtool status')
598 self.assertIn(testrecipe, result.output)
599 self.assertIn(srcdir, result.output)
600 # Check recipe
601 recipefile = get_bb_var('FILE', testrecipe)
602 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
603 checkvars = {}
604 checkvars['S'] = '${WORKDIR}/git'
Andrew Geissler5082cc72023-09-11 08:41:39 -0400605 checkvars['PV'] = '1.0+git'
Andrew Geissler595f6302022-01-24 19:11:47 +0000606 checkvars['SRC_URI'] = url_branch
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500607 checkvars['SRCREV'] = '${AUTOREV}'
608 self._test_recipe_contents(recipefile, checkvars, [])
609 # Try with revision and version specified
610 result = runCmd('devtool reset -n %s' % testrecipe)
611 shutil.rmtree(srcdir)
612 url_rev = '%s;rev=%s' % (url, checkrev)
613 result = runCmd('devtool add %s %s -f "%s" -V 1.5' % (testrecipe, srcdir, url_rev))
614 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
615 # Test devtool status
616 result = runCmd('devtool status')
617 self.assertIn(testrecipe, result.output)
618 self.assertIn(srcdir, result.output)
619 # Check recipe
620 recipefile = get_bb_var('FILE', testrecipe)
621 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
622 checkvars = {}
623 checkvars['S'] = '${WORKDIR}/git'
Andrew Geissler5082cc72023-09-11 08:41:39 -0400624 checkvars['PV'] = '1.5+git'
Andrew Geissler595f6302022-01-24 19:11:47 +0000625 checkvars['SRC_URI'] = url_branch
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500626 checkvars['SRCREV'] = checkrev
627 self._test_recipe_contents(recipefile, checkvars, [])
628
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500629 def test_devtool_add_fetch_simple(self):
630 # Fetch source from a remote URL, auto-detecting name
631 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
632 self.track_for_cleanup(tempdir)
633 testver = '1.6.0'
634 url = 'http://www.ivarch.com/programs/sources/pv-%s.tar.bz2' % testver
635 testrecipe = 'pv'
636 srcdir = os.path.join(self.workspacedir, 'sources', testrecipe)
637 # Test devtool add
638 self.track_for_cleanup(self.workspacedir)
639 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
640 result = runCmd('devtool add %s' % url)
641 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output)
642 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
643 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
644 # Test devtool status
645 result = runCmd('devtool status')
646 self.assertIn(testrecipe, result.output)
647 self.assertIn(srcdir, result.output)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500648 # Check recipedevtool add
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500649 recipefile = get_bb_var('FILE', testrecipe)
650 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
651 checkvars = {}
652 checkvars['S'] = None
653 checkvars['SRC_URI'] = url.replace(testver, '${PV}')
654 self._test_recipe_contents(recipefile, checkvars, [])
655
Andrew Geissler82c905d2020-04-13 13:39:40 -0500656 def test_devtool_add_npm(self):
Andrew Geisslerf0343792020-11-18 10:42:21 -0600657 collections = get_bb_var('BBFILE_COLLECTIONS').split()
658 if "openembedded-layer" not in collections:
659 self.skipTest("Test needs meta-oe for nodejs")
660
Andrew Geissler82c905d2020-04-13 13:39:40 -0500661 pn = 'savoirfairelinux-node-server-example'
662 pv = '1.0.0'
663 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=' + pv
664 # Test devtool add
665 self.track_for_cleanup(self.workspacedir)
666 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
667 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
668 result = runCmd('devtool add \'%s\'' % url)
669 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
670 self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, '%s_%s.bb' % (pn, pv)), 'Recipe not created')
671 self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, pn, 'npm-shrinkwrap.json'), 'Shrinkwrap not created')
672 # Test devtool status
673 result = runCmd('devtool status')
674 self.assertIn(pn, result.output)
675 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
676 bitbake('%s -c cleansstate' % pn)
677 # Test devtool build
678 result = runCmd('devtool build %s' % pn)
679
Andrew Geissler615f2f12022-07-15 14:00:58 -0500680 def test_devtool_add_python_egg_requires(self):
681 # Fetch source
682 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
683 self.track_for_cleanup(tempdir)
684 testver = '0.14.0'
685 url = 'https://files.pythonhosted.org/packages/e9/9e/25d59f5043cf763833b2581c8027fa92342c4cf8ee523b498ecdf460c16d/uvicorn-%s.tar.gz' % testver
686 testrecipe = 'python3-uvicorn'
687 srcdir = os.path.join(tempdir, testrecipe)
688 # Test devtool add
689 self.track_for_cleanup(self.workspacedir)
690 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
691 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
692
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800693class DevtoolModifyTests(DevtoolBase):
694
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500695 def test_devtool_modify(self):
696 import oe.path
697
698 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
699 self.track_for_cleanup(tempdir)
700 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500701 self.add_command_to_tearDown('bitbake -c clean mdadm')
Brad Bishop00e122a2019-10-05 11:10:57 -0400702 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500703 result = runCmd('devtool modify mdadm -x %s' % tempdir)
704 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
705 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
706 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mdadm_*.bbappend'))
707 self.assertTrue(matches, 'bbappend not created %s' % result.output)
708
709 # Test devtool status
710 result = runCmd('devtool status')
711 self.assertIn('mdadm', result.output)
712 self.assertIn(tempdir, result.output)
713 self._check_src_repo(tempdir)
714
715 bitbake('mdadm -C unpack')
716
717 def check_line(checkfile, expected, message, present=True):
718 # Check for $expected, on a line on its own, in checkfile.
719 with open(checkfile, 'r') as f:
720 if present:
721 self.assertIn(expected + '\n', f, message)
722 else:
723 self.assertNotIn(expected + '\n', f, message)
724
725 modfile = os.path.join(tempdir, 'mdadm.8.in')
726 bb_vars = get_bb_vars(['PKGD', 'mandir'], 'mdadm')
727 pkgd = bb_vars['PKGD']
728 self.assertTrue(pkgd, 'Could not query PKGD variable')
729 mandir = bb_vars['mandir']
730 self.assertTrue(mandir, 'Could not query mandir variable')
731 manfile = oe.path.join(pkgd, mandir, 'man8', 'mdadm.8')
732
733 check_line(modfile, 'Linux Software RAID', 'Could not find initial string')
734 check_line(modfile, 'antique pin sardine', 'Unexpectedly found replacement string', present=False)
735
736 result = runCmd("sed -i 's!^Linux Software RAID$!antique pin sardine!' %s" % modfile)
737 check_line(modfile, 'antique pin sardine', 'mdadm.8.in file not modified (sed failed)')
738
739 bitbake('mdadm -c package')
740 check_line(manfile, 'antique pin sardine', 'man file not modified. man searched file path: %s' % manfile)
741
742 result = runCmd('git checkout -- %s' % modfile, cwd=tempdir)
743 check_line(modfile, 'Linux Software RAID', 'man .in file not restored (git failed)')
744
745 bitbake('mdadm -c package')
746 check_line(manfile, 'Linux Software RAID', 'man file not updated. man searched file path: %s' % manfile)
747
748 result = runCmd('devtool reset mdadm')
749 result = runCmd('devtool status')
750 self.assertNotIn('mdadm', result.output)
751
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500752 def test_devtool_buildclean(self):
753 def assertFile(path, *paths):
754 f = os.path.join(path, *paths)
755 self.assertExists(f)
756 def assertNoFile(path, *paths):
757 f = os.path.join(path, *paths)
758 self.assertNotExists(f)
759
760 # Clean up anything in the workdir/sysroot/sstate cache
761 bitbake('mdadm m4 -c cleansstate')
762 # Try modifying a recipe
763 tempdir_mdadm = tempfile.mkdtemp(prefix='devtoolqa')
764 tempdir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
765 builddir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
766 self.track_for_cleanup(tempdir_mdadm)
767 self.track_for_cleanup(tempdir_m4)
768 self.track_for_cleanup(builddir_m4)
769 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500770 self.add_command_to_tearDown('bitbake -c clean mdadm m4')
Brad Bishop00e122a2019-10-05 11:10:57 -0400771 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500772 self.write_recipeinc('m4', 'EXTERNALSRC_BUILD = "%s"\ndo_clean() {\n\t:\n}\n' % builddir_m4)
773 try:
774 runCmd('devtool modify mdadm -x %s' % tempdir_mdadm)
775 runCmd('devtool modify m4 -x %s' % tempdir_m4)
776 assertNoFile(tempdir_mdadm, 'mdadm')
777 assertNoFile(builddir_m4, 'src/m4')
778 result = bitbake('m4 -e')
779 result = bitbake('mdadm m4 -c compile')
780 self.assertEqual(result.status, 0)
781 assertFile(tempdir_mdadm, 'mdadm')
782 assertFile(builddir_m4, 'src/m4')
783 # Check that buildclean task exists and does call make clean
784 bitbake('mdadm m4 -c buildclean')
785 assertNoFile(tempdir_mdadm, 'mdadm')
786 assertNoFile(builddir_m4, 'src/m4')
Brad Bishop00e122a2019-10-05 11:10:57 -0400787 runCmd('echo "#Trigger rebuild" >> %s/Makefile' % tempdir_mdadm)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500788 bitbake('mdadm m4 -c compile')
789 assertFile(tempdir_mdadm, 'mdadm')
790 assertFile(builddir_m4, 'src/m4')
791 bitbake('mdadm m4 -c clean')
792 # Check that buildclean task is run before clean for B == S
793 assertNoFile(tempdir_mdadm, 'mdadm')
794 # Check that buildclean task is not run before clean for B != S
795 assertFile(builddir_m4, 'src/m4')
796 finally:
797 self.delete_recipeinc('m4')
798
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500799 def test_devtool_modify_invalid(self):
800 # Try modifying some recipes
801 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
802 self.track_for_cleanup(tempdir)
803 self.track_for_cleanup(self.workspacedir)
804 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
805
Andrew Geissler5199d832021-09-24 16:47:35 -0500806 testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk'.split()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500807 # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose
808 result = runCmd('bitbake-layers show-recipes gcc-source*')
809 for line in result.output.splitlines():
810 # just match those lines that contain a real target
811 m = re.match('(?P<recipe>^[a-zA-Z0-9.-]+)(?P<colon>:$)', line)
812 if m:
813 testrecipes.append(m.group('recipe'))
814 for testrecipe in testrecipes:
815 # Check it's a valid recipe
816 bitbake('%s -e' % testrecipe)
817 # devtool extract should fail
818 result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
819 self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed. devtool output: %s' % (testrecipe, result.output))
820 self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe)
821 self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe)
822 # devtool modify should fail
823 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
824 self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed. devtool output: %s' % (testrecipe, result.output))
825 self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe)
826
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500827 def test_devtool_modify_native(self):
828 # Check preconditions
829 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
830 # Try modifying some recipes
831 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
832 self.track_for_cleanup(tempdir)
833 self.track_for_cleanup(self.workspacedir)
834 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
835
836 bbclassextended = False
837 inheritnative = False
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500838 testrecipes = 'cdrtools-native mtools-native apt-native desktop-file-utils-native'.split()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500839 for testrecipe in testrecipes:
840 checkextend = 'native' in (get_bb_var('BBCLASSEXTEND', testrecipe) or '').split()
841 if not bbclassextended:
842 bbclassextended = checkextend
843 if not inheritnative:
844 inheritnative = not checkextend
845 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)))
846 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool modify output: %s' % result.output)
847 result = runCmd('devtool build %s' % testrecipe)
848 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool build output: %s' % result.output)
849 result = runCmd('devtool reset %s' % testrecipe)
850 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool reset output: %s' % result.output)
851
852 self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
853 self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
Andrew Geissler615f2f12022-07-15 14:00:58 -0500854
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600855 def test_devtool_modify_localfiles_only(self):
856 # Check preconditions
857 testrecipe = 'base-files'
858 src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
859 foundlocalonly = False
860 correct_symlink = False
861 for item in src_uri:
862 if item.startswith('file://'):
863 if '.patch' not in item:
864 foundlocalonly = True
865 else:
866 foundlocalonly = False
867 break
868 self.assertTrue(foundlocalonly, 'This test expects the %s recipe to fetch local files only and it seems that it no longer does' % testrecipe)
869 # Clean up anything in the workdir/sysroot/sstate cache
870 bitbake('%s -c cleansstate' % testrecipe)
871 # Try modifying a recipe
872 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
873 self.track_for_cleanup(tempdir)
874 self.track_for_cleanup(self.workspacedir)
875 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
876 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
877 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
878 srcfile = os.path.join(tempdir, 'oe-local-files/share/dot.bashrc')
879 srclink = os.path.join(tempdir, 'share/dot.bashrc')
880 self.assertExists(srcfile, 'Extracted source could not be found')
881 if os.path.islink(srclink) and os.path.exists(srclink) and os.path.samefile(srcfile, srclink):
882 correct_symlink = True
883 self.assertTrue(correct_symlink, 'Source symlink to oe-local-files is broken')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500884
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600885 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
886 self.assertTrue(matches, 'bbappend not created')
887 # Test devtool status
888 result = runCmd('devtool status')
889 self.assertIn(testrecipe, result.output)
890 self.assertIn(tempdir, result.output)
891 # Try building
892 bitbake(testrecipe)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500893
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500894 def test_devtool_modify_git(self):
895 # Check preconditions
Brad Bishop316dfdd2018-06-25 12:45:53 -0400896 testrecipe = 'psplash'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500897 src_uri = get_bb_var('SRC_URI', testrecipe)
898 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
899 # Clean up anything in the workdir/sysroot/sstate cache
900 bitbake('%s -c cleansstate' % testrecipe)
901 # Try modifying a recipe
902 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
903 self.track_for_cleanup(tempdir)
904 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500905 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
Brad Bishop00e122a2019-10-05 11:10:57 -0400906 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500907 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400908 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500909 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400910 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend'))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500911 self.assertTrue(matches, 'bbappend not created')
912 # Test devtool status
913 result = runCmd('devtool status')
914 self.assertIn(testrecipe, result.output)
915 self.assertIn(tempdir, result.output)
916 # Check git repo
917 self._check_src_repo(tempdir)
918 # Try building
919 bitbake(testrecipe)
920
Patrick Williamsf52e3dd2024-01-26 13:04:43 -0600921 def test_devtool_modify_git_no_extract(self):
922 # Check preconditions
923 testrecipe = 'psplash'
924 src_uri = get_bb_var('SRC_URI', testrecipe)
925 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
926 # Clean up anything in the workdir/sysroot/sstate cache
927 bitbake('%s -c cleansstate' % testrecipe)
928 # Try modifying a recipe
929 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
930 self.track_for_cleanup(tempdir)
931 self.track_for_cleanup(self.workspacedir)
932 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
933 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
934 result = runCmd('git clone https://git.yoctoproject.org/psplash %s && devtool modify -n %s %s' % (tempdir, testrecipe, tempdir))
935 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
936 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend'))
937 self.assertTrue(matches, 'bbappend not created')
938 # Test devtool status
939 result = runCmd('devtool status')
940 self.assertIn(testrecipe, result.output)
941 self.assertIn(tempdir, result.output)
942
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500943 def test_devtool_modify_git_crates_subpath(self):
944 # This tests two things in devtool context:
945 # - that we support local git dependencies for cargo based recipe
946 # - that we support patches in SRC_URI when git url contains subpath parameter
947
948 # Check preconditions:
949 # recipe inherits cargo
950 # git:// uri with a subpath as the main package
951 # some crate:// in SRC_URI
952 # others git:// in SRC_URI
953 # cointains a patch
Patrick Williams169d7bc2024-01-05 11:33:25 -0600954 testrecipe = 'hello-rs'
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500955 bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'WORKDIR', 'CARGO_HOME'], testrecipe)
956 recipefile = bb_vars['FILE']
957 workdir = bb_vars['WORKDIR']
958 cargo_home = bb_vars['CARGO_HOME']
959 src_uri = bb_vars['SRC_URI'].split()
960 self.assertTrue(src_uri[0].startswith('git://'),
961 'This test expects the %s recipe to have a git repo has its main uri' % testrecipe)
962 self.assertIn(';subpath=', src_uri[0],
963 'This test expects the %s recipe to have a git uri with subpath' % testrecipe)
964 self.assertTrue(any([uri.startswith('crate://') for uri in src_uri]),
965 'This test expects the %s recipe to have some crates in its src uris' % testrecipe)
Patrick Williams169d7bc2024-01-05 11:33:25 -0600966 self.assertGreaterEqual(sum(map(lambda x:x.startswith('git://'), src_uri)), 2,
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500967 'This test expects the %s recipe to have several git:// uris' % testrecipe)
968 self.assertTrue(any([uri.startswith('file://') and '.patch' in uri for uri in src_uri]),
969 'This test expects the %s recipe to have a patch in its src uris' % testrecipe)
970
Andrew Geissler028142b2023-05-05 11:29:21 -0500971 self._test_recipe_contents(recipefile, {}, ['ptest-cargo'])
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500972
973 # Clean up anything in the workdir/sysroot/sstate cache
974 bitbake('%s -c cleansstate' % testrecipe)
975 # Try modifying a recipe
976 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
977 self.track_for_cleanup(tempdir)
978 self.track_for_cleanup(self.workspacedir)
979 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
980 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
981 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
982 self.assertExists(os.path.join(tempdir, 'Cargo.toml'), 'Extracted source could not be found')
983 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
Patrick Williams169d7bc2024-01-05 11:33:25 -0600984 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500985 self.assertTrue(matches, 'bbappend not created')
986 # Test devtool status
987 result = runCmd('devtool status')
988 self.assertIn(testrecipe, result.output)
989 self.assertIn(tempdir, result.output)
990 # Check git repo
991 self._check_src_repo(tempdir)
Patrick Williams73bd93f2024-02-20 08:07:48 -0600992 # Check that the patch is correctly applied.
993 # The last commit message in the tree must contain the following note:
994 # Notes (devtool):
995 # original patch: <patchname>
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500996 # ..
997 patchname = None
998 for uri in src_uri:
999 if uri.startswith('file://') and '.patch' in uri:
1000 patchname = uri.replace("file://", "").partition('.patch')[0] + '.patch'
1001 self.assertIsNotNone(patchname)
1002 result = runCmd('git -C %s log -1' % tempdir)
Patrick Williams73bd93f2024-02-20 08:07:48 -06001003 self.assertIn("Notes (devtool):\n original patch: %s" % patchname, result.output)
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001004
1005 # Configure the recipe to check that the git dependencies are correctly patched in cargo config
1006 bitbake('-c configure %s' % testrecipe)
1007
1008 cargo_config_path = os.path.join(cargo_home, 'config')
1009 with open(cargo_config_path, "r") as f:
1010 cargo_config_contents = [line.strip('\n') for line in f.readlines()]
1011
1012 # Get back git dependencies of the recipe (ignoring the main one)
1013 # and check that they are all correctly patched to be fetched locally
1014 git_deps = [uri for uri in src_uri if uri.startswith("git://")][1:]
1015 for git_dep in git_deps:
1016 raw_url, _, raw_parms = git_dep.partition(";")
1017 parms = {}
1018 for parm in raw_parms.split(";"):
1019 name_parm, _, value_parm = parm.partition('=')
1020 parms[name_parm]=value_parm
1021 self.assertIn('protocol', parms, 'git dependencies uri should contain the "protocol" parameter')
1022 self.assertIn('name', parms, 'git dependencies uri should contain the "name" parameter')
1023 self.assertIn('destsuffix', parms, 'git dependencies uri should contain the "destsuffix" parameter')
1024 self.assertIn('type', parms, 'git dependencies uri should contain the "type" parameter')
1025 self.assertEqual(parms['type'], 'git-dependency', 'git dependencies uri should have "type=git-dependency"')
1026 raw_url = raw_url.replace("git://", '%s://' % parms['protocol'])
1027 patch_line = '[patch."%s"]' % raw_url
1028 path_patched = os.path.join(workdir, parms['destsuffix'])
1029 path_override_line = '%s = { path = "%s" }' % (parms['name'], path_patched)
1030 # Would have been better to use tomllib to read this file :/
1031 self.assertIn(patch_line, cargo_config_contents)
1032 self.assertIn(path_override_line, cargo_config_contents)
1033
1034 # Try to package the recipe
1035 bitbake('-c package_qa %s' % testrecipe)
1036
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001037 def test_devtool_modify_localfiles(self):
1038 # Check preconditions
1039 testrecipe = 'lighttpd'
1040 src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
1041 foundlocal = False
1042 for item in src_uri:
1043 if item.startswith('file://') and '.patch' not in item:
1044 foundlocal = True
1045 break
1046 self.assertTrue(foundlocal, 'This test expects the %s recipe to fetch local files and it seems that it no longer does' % testrecipe)
1047 # Clean up anything in the workdir/sysroot/sstate cache
1048 bitbake('%s -c cleansstate' % testrecipe)
1049 # Try modifying a recipe
1050 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1051 self.track_for_cleanup(tempdir)
1052 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001053 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
Brad Bishop00e122a2019-10-05 11:10:57 -04001054 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001055 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1056 self.assertExists(os.path.join(tempdir, 'configure.ac'), 'Extracted source could not be found')
1057 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
1058 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
1059 self.assertTrue(matches, 'bbappend not created')
1060 # Test devtool status
1061 result = runCmd('devtool status')
1062 self.assertIn(testrecipe, result.output)
1063 self.assertIn(tempdir, result.output)
1064 # Try building
1065 bitbake(testrecipe)
1066
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001067 def test_devtool_modify_virtual(self):
1068 # Try modifying a virtual recipe
1069 virtrecipe = 'virtual/make'
1070 realrecipe = 'make'
1071 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1072 self.track_for_cleanup(tempdir)
1073 self.track_for_cleanup(self.workspacedir)
1074 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1075 result = runCmd('devtool modify %s -x %s' % (virtrecipe, tempdir))
1076 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
1077 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
1078 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % realrecipe))
1079 self.assertTrue(matches, 'bbappend not created %s' % result.output)
1080 # Test devtool status
1081 result = runCmd('devtool status')
1082 self.assertNotIn(virtrecipe, result.output)
1083 self.assertIn(realrecipe, result.output)
1084 # Check git repo
1085 self._check_src_repo(tempdir)
1086 # This is probably sufficient
1087
Andrew Geisslerf0343792020-11-18 10:42:21 -06001088 def test_devtool_modify_overrides(self):
1089 # Try modifying a recipe with patches in overrides
1090 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1091 self.track_for_cleanup(tempdir)
1092 self.track_for_cleanup(self.workspacedir)
1093 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1094 result = runCmd('devtool modify devtool-patch-overrides -x %s' % (tempdir))
1095
1096 self._check_src_repo(tempdir)
1097 source = os.path.join(tempdir, "source")
1098 def check(branch, expected):
1099 runCmd('git -C %s checkout %s' % (tempdir, branch))
1100 with open(source, "rt") as f:
1101 content = f.read()
Patrick Williams73bd93f2024-02-20 08:07:48 -06001102 self.assertEqual(content, expected)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001103 if self.td["MACHINE"] == "qemux86":
1104 check('devtool', 'This is a test for qemux86\n')
1105 elif self.td["MACHINE"] == "qemuarm":
1106 check('devtool', 'This is a test for qemuarm\n')
1107 else:
1108 check('devtool', 'This is a test for something\n')
Andrew Geisslerf0343792020-11-18 10:42:21 -06001109 check('devtool-no-overrides', 'This is a test for something\n')
1110 check('devtool-override-qemuarm', 'This is a test for qemuarm\n')
1111 check('devtool-override-qemux86', 'This is a test for qemux86\n')
1112
Patrick Williamsf52e3dd2024-01-26 13:04:43 -06001113 def test_devtool_modify_multiple_sources(self):
1114 # This test check that recipes fetching several sources can be used with devtool modify/build
1115 # Check preconditions
1116 testrecipe = 'bzip2'
1117 src_uri = get_bb_var('SRC_URI', testrecipe)
1118 src1 = 'https://' in src_uri
1119 src2 = 'git://' in src_uri
1120 self.assertTrue(src1 and src2, 'This test expects the %s recipe to fetch both a git source and a tarball and it seems that it no longer does' % testrecipe)
1121 # Clean up anything in the workdir/sysroot/sstate cache
1122 bitbake('%s -c cleansstate' % testrecipe)
1123 # Try modifying a recipe
1124 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1125 self.track_for_cleanup(tempdir)
1126 self.track_for_cleanup(self.workspacedir)
1127 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1128 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1129 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1130 self.assertEqual(result.status, 0, "Could not modify recipe %s. Output: %s" % (testrecipe, result.output))
1131 # Test devtool status
1132 result = runCmd('devtool status')
1133 self.assertIn(testrecipe, result.output)
1134 self.assertIn(tempdir, result.output)
1135 # Try building
1136 result = bitbake(testrecipe)
1137 self.assertEqual(result.status, 0, "Bitbake failed, exit code %s, output %s" % (result.status, result.output))
1138
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001139class DevtoolUpdateTests(DevtoolBase):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001140
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001141 def test_devtool_update_recipe(self):
1142 # Check preconditions
1143 testrecipe = 'minicom'
1144 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1145 recipefile = bb_vars['FILE']
1146 src_uri = bb_vars['SRC_URI']
1147 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
1148 self._check_repo_status(os.path.dirname(recipefile), [])
1149 # First, modify a recipe
1150 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1151 self.track_for_cleanup(tempdir)
1152 self.track_for_cleanup(self.workspacedir)
1153 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1154 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1155 # We don't use -x here so that we test the behaviour of devtool modify without it
1156 result = runCmd('devtool modify %s %s' % (testrecipe, tempdir))
1157 # Check git repo
1158 self._check_src_repo(tempdir)
1159 # Add a couple of commits
1160 # FIXME: this only tests adding, need to also test update and remove
1161 result = runCmd('echo "Additional line" >> README', cwd=tempdir)
1162 result = runCmd('git commit -a -m "Change the README"', cwd=tempdir)
1163 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
1164 result = runCmd('git add devtool-new-file', cwd=tempdir)
1165 result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
1166 self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1167 result = runCmd('devtool update-recipe %s' % testrecipe)
1168 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1169 ('??', '.*/0001-Change-the-README.patch$'),
1170 ('??', '.*/0002-Add-a-new-file.patch$')]
1171 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1172
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001173 def test_devtool_update_recipe_git(self):
1174 # Check preconditions
Patrick Williams7784c422022-11-17 07:29:11 -06001175 testrecipe = 'mtd-utils-selftest'
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001176 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1177 recipefile = bb_vars['FILE']
1178 src_uri = bb_vars['SRC_URI']
1179 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
1180 patches = []
1181 for entry in src_uri.split():
1182 if entry.startswith('file://') and entry.endswith('.patch'):
1183 patches.append(entry[7:].split(';')[0])
1184 self.assertGreater(len(patches), 0, 'The %s recipe does not appear to contain any patches, so this test will not be effective' % testrecipe)
1185 self._check_repo_status(os.path.dirname(recipefile), [])
1186 # First, modify a recipe
1187 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1188 self.track_for_cleanup(tempdir)
1189 self.track_for_cleanup(self.workspacedir)
1190 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1191 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1192 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1193 # Check git repo
1194 self._check_src_repo(tempdir)
1195 # Add a couple of commits
1196 # FIXME: this only tests adding, need to also test update and remove
1197 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir)
1198 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
1199 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
1200 result = runCmd('git add devtool-new-file', cwd=tempdir)
1201 result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
1202 self.add_command_to_tearDown('cd %s; rm -rf %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1203 result = runCmd('devtool update-recipe -m srcrev %s' % testrecipe)
1204 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile))] + \
1205 [(' D', '.*/%s$' % patch) for patch in patches]
1206 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1207
1208 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
Andrew Geissler595f6302022-01-24 19:11:47 +00001209 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git;branch=master"']
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001210 srcurilines = src_uri.split()
1211 srcurilines[0] = 'SRC_URI = "' + srcurilines[0]
1212 srcurilines.append('"')
1213 removelines = ['SRCREV = ".*"'] + srcurilines
Andrew Geissler615f2f12022-07-15 14:00:58 -05001214 self._check_diff(result.output, addlines, removelines)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001215 # Now try with auto mode
1216 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
1217 result = runCmd('devtool update-recipe %s' % testrecipe)
1218 result = runCmd('git rev-parse --show-toplevel', cwd=os.path.dirname(recipefile))
1219 topleveldir = result.output.strip()
1220 relpatchpath = os.path.join(os.path.relpath(os.path.dirname(recipefile), topleveldir), testrecipe)
1221 expected_status = [(' M', os.path.relpath(recipefile, topleveldir)),
1222 ('??', '%s/0001-Change-the-Makefile.patch' % relpatchpath),
1223 ('??', '%s/0002-Add-a-new-file.patch' % relpatchpath)]
1224 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1225
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001226 def test_devtool_update_recipe_append(self):
1227 # Check preconditions
1228 testrecipe = 'mdadm'
1229 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1230 recipefile = bb_vars['FILE']
1231 src_uri = bb_vars['SRC_URI']
1232 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
1233 self._check_repo_status(os.path.dirname(recipefile), [])
1234 # First, modify a recipe
1235 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1236 tempsrcdir = os.path.join(tempdir, 'source')
1237 templayerdir = os.path.join(tempdir, 'layer')
1238 self.track_for_cleanup(tempdir)
1239 self.track_for_cleanup(self.workspacedir)
1240 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1241 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1242 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
1243 # Check git repo
1244 self._check_src_repo(tempsrcdir)
1245 # Add a commit
1246 result = runCmd("sed 's!\\(#define VERSION\\W*\"[^\"]*\\)\"!\\1-custom\"!' -i ReadMe.c", cwd=tempsrcdir)
1247 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
1248 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
1249 # Create a temporary layer and add it to bblayers.conf
1250 self._create_temp_layer(templayerdir, True, 'selftestupdaterecipe')
1251 # Create the bbappend
1252 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
1253 self.assertNotIn('WARNING:', result.output)
1254 # Check recipe is still clean
1255 self._check_repo_status(os.path.dirname(recipefile), [])
1256 # Check bbappend was created
1257 splitpath = os.path.dirname(recipefile).split(os.sep)
1258 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
1259 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
1260 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-our-custom-version.patch')
1261 self.assertExists(patchfile, 'Patch file not created')
1262
1263 # Check bbappend contents
Patrick Williams213cb262021-08-07 19:21:33 -05001264 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001265 '\n',
1266 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n',
1267 '\n']
1268 with open(bbappendfile, 'r') as f:
1269 self.assertEqual(expectedlines, f.readlines())
1270
1271 # Check we can run it again and bbappend isn't modified
1272 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
1273 with open(bbappendfile, 'r') as f:
1274 self.assertEqual(expectedlines, f.readlines())
1275 # Drop new commit and check patch gets deleted
1276 result = runCmd('git reset HEAD^', cwd=tempsrcdir)
1277 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
1278 self.assertNotExists(patchfile, 'Patch file not deleted')
Patrick Williams213cb262021-08-07 19:21:33 -05001279 expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001280 '\n']
1281 with open(bbappendfile, 'r') as f:
1282 self.assertEqual(expectedlines2, f.readlines())
1283 # Put commit back and check we can run it if layer isn't in bblayers.conf
1284 os.remove(bbappendfile)
1285 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
1286 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
1287 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
1288 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
1289 self.assertExists(patchfile, 'Patch file not created (with disabled layer)')
1290 with open(bbappendfile, 'r') as f:
1291 self.assertEqual(expectedlines, f.readlines())
1292 # Deleting isn't expected to work under these circumstances
1293
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001294 def test_devtool_update_recipe_append_git(self):
1295 # Check preconditions
Patrick Williams7784c422022-11-17 07:29:11 -06001296 testrecipe = 'mtd-utils-selftest'
Andrew Geissler517393d2023-01-13 08:55:19 -06001297 bb_vars = get_bb_vars(['FILE', 'SRC_URI', 'LAYERSERIES_CORENAMES'], testrecipe)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001298 recipefile = bb_vars['FILE']
1299 src_uri = bb_vars['SRC_URI']
Andrew Geissler517393d2023-01-13 08:55:19 -06001300 corenames = bb_vars['LAYERSERIES_CORENAMES']
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001301 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
1302 for entry in src_uri.split():
1303 if entry.startswith('git://'):
1304 git_uri = entry
1305 break
1306 self._check_repo_status(os.path.dirname(recipefile), [])
1307 # First, modify a recipe
1308 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1309 tempsrcdir = os.path.join(tempdir, 'source')
1310 templayerdir = os.path.join(tempdir, 'layer')
1311 self.track_for_cleanup(tempdir)
1312 self.track_for_cleanup(self.workspacedir)
1313 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1314 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1315 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
1316 # Check git repo
1317 self._check_src_repo(tempsrcdir)
1318 # Add a commit
1319 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
1320 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
1321 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
1322 # Create a temporary layer
1323 os.makedirs(os.path.join(templayerdir, 'conf'))
1324 with open(os.path.join(templayerdir, 'conf', 'layer.conf'), 'w') as f:
1325 f.write('BBPATH .= ":${LAYERDIR}"\n')
1326 f.write('BBFILES += "${LAYERDIR}/recipes-*/*/*.bbappend"\n')
1327 f.write('BBFILE_COLLECTIONS += "oeselftesttemplayer"\n')
1328 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n')
1329 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n')
1330 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n')
Andrew Geissler517393d2023-01-13 08:55:19 -06001331 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "%s"\n' % corenames)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001332 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
1333 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
1334 # Create the bbappend
1335 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1336 self.assertNotIn('WARNING:', result.output)
1337 # Check recipe is still clean
1338 self._check_repo_status(os.path.dirname(recipefile), [])
1339 # Check bbappend was created
1340 splitpath = os.path.dirname(recipefile).split(os.sep)
1341 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
1342 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
1343 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
1344
1345 # Check bbappend contents
1346 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
1347 expectedlines = set(['SRCREV = "%s"\n' % result.output,
1348 '\n',
1349 'SRC_URI = "%s"\n' % git_uri,
1350 '\n'])
1351 with open(bbappendfile, 'r') as f:
1352 self.assertEqual(expectedlines, set(f.readlines()))
1353
1354 # Check we can run it again and bbappend isn't modified
1355 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1356 with open(bbappendfile, 'r') as f:
1357 self.assertEqual(expectedlines, set(f.readlines()))
1358 # Drop new commit and check SRCREV changes
1359 result = runCmd('git reset HEAD^', cwd=tempsrcdir)
1360 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1361 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
1362 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
1363 expectedlines = set(['SRCREV = "%s"\n' % result.output,
1364 '\n',
1365 'SRC_URI = "%s"\n' % git_uri,
1366 '\n'])
1367 with open(bbappendfile, 'r') as f:
1368 self.assertEqual(expectedlines, set(f.readlines()))
1369 # Put commit back and check we can run it if layer isn't in bblayers.conf
1370 os.remove(bbappendfile)
1371 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
1372 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
1373 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1374 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
1375 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
1376 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
1377 expectedlines = set(['SRCREV = "%s"\n' % result.output,
1378 '\n',
1379 'SRC_URI = "%s"\n' % git_uri,
1380 '\n'])
1381 with open(bbappendfile, 'r') as f:
1382 self.assertEqual(expectedlines, set(f.readlines()))
1383 # Deleting isn't expected to work under these circumstances
1384
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001385 def test_devtool_update_recipe_local_files(self):
1386 """Check that local source files are copied over instead of patched"""
1387 testrecipe = 'makedevs'
1388 recipefile = get_bb_var('FILE', testrecipe)
1389 # Setup srctree for modifying the recipe
1390 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1391 self.track_for_cleanup(tempdir)
1392 self.track_for_cleanup(self.workspacedir)
1393 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1394 # (don't bother with cleaning the recipe on teardown, we won't be
1395 # building it)
1396 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1397 # Check git repo
1398 self._check_src_repo(tempdir)
1399 # Try building just to ensure we haven't broken that
1400 bitbake("%s" % testrecipe)
1401 # Edit / commit local source
1402 runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir)
1403 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
1404 runCmd('echo "Bar" > new-file', cwd=tempdir)
1405 runCmd('git add new-file', cwd=tempdir)
1406 runCmd('git commit -m "Add new file"', cwd=tempdir)
1407 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
1408 os.path.dirname(recipefile))
1409 runCmd('devtool update-recipe %s' % testrecipe)
1410 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1411 (' M', '.*/makedevs/makedevs.c$'),
1412 ('??', '.*/makedevs/new-local$'),
1413 ('??', '.*/makedevs/0001-Add-new-file.patch$')]
1414 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1415
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001416 def test_devtool_update_recipe_local_files_2(self):
1417 """Check local source files support when oe-local-files is in Git"""
Brad Bishop316dfdd2018-06-25 12:45:53 -04001418 testrecipe = 'devtool-test-local'
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001419 recipefile = get_bb_var('FILE', testrecipe)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001420 recipedir = os.path.dirname(recipefile)
1421 result = runCmd('git status --porcelain .', cwd=recipedir)
1422 if result.output.strip():
1423 self.fail('Recipe directory for %s contains uncommitted changes' % testrecipe)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001424 # Setup srctree for modifying the recipe
1425 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1426 self.track_for_cleanup(tempdir)
1427 self.track_for_cleanup(self.workspacedir)
1428 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1429 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1430 # Check git repo
1431 self._check_src_repo(tempdir)
1432 # Add oe-local-files to Git
1433 runCmd('rm oe-local-files/.gitignore', cwd=tempdir)
1434 runCmd('git add oe-local-files', cwd=tempdir)
1435 runCmd('git commit -m "Add local sources"', cwd=tempdir)
1436 # Edit / commit local sources
Brad Bishop316dfdd2018-06-25 12:45:53 -04001437 runCmd('echo "# Foobar" >> oe-local-files/file1', cwd=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001438 runCmd('git commit -am "Edit existing file"', cwd=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001439 runCmd('git rm oe-local-files/file2', cwd=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001440 runCmd('git commit -m"Remove file"', cwd=tempdir)
1441 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
1442 runCmd('git add oe-local-files/new-local', cwd=tempdir)
1443 runCmd('git commit -m "Add new local file"', cwd=tempdir)
1444 runCmd('echo "Gar" > new-file', cwd=tempdir)
1445 runCmd('git add new-file', cwd=tempdir)
1446 runCmd('git commit -m "Add new file"', cwd=tempdir)
1447 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
1448 os.path.dirname(recipefile))
1449 # Checkout unmodified file to working copy -> devtool should still pick
1450 # the modified version from HEAD
Brad Bishop316dfdd2018-06-25 12:45:53 -04001451 runCmd('git checkout HEAD^ -- oe-local-files/file1', cwd=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001452 runCmd('devtool update-recipe %s' % testrecipe)
1453 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001454 (' M', '.*/file1$'),
1455 (' D', '.*/file2$'),
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001456 ('??', '.*/new-local$'),
1457 ('??', '.*/0001-Add-new-file.patch$')]
1458 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1459
Andrew Geissler4ed12e12020-06-05 18:00:41 -05001460 def test_devtool_update_recipe_with_gitignore(self):
1461 # First, modify the recipe
1462 testrecipe = 'devtool-test-ignored'
1463 bb_vars = get_bb_vars(['FILE'], testrecipe)
1464 recipefile = bb_vars['FILE']
1465 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch')
1466 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch.expected')
1467 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1468 self.track_for_cleanup(tempdir)
1469 self.track_for_cleanup(self.workspacedir)
1470 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1471 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1472 result = runCmd('devtool modify %s' % testrecipe)
1473 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1474 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe)
1475 # Check recipe got changed as expected
1476 with open(newpatchfile, 'r') as f:
1477 desiredlines = f.readlines()
1478 with open(patchfile, 'r') as f:
1479 newlines = f.readlines()
1480 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo
1481 # which changes the metadata subject which is added into the patch, but keep
1482 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh
1483 # devtool-test-ignored manually, then it should generate exactly the same .patch file
1484 self.assertEqual(desiredlines[5:], newlines[5:])
1485
1486 def test_devtool_update_recipe_long_filename(self):
1487 # First, modify the recipe
1488 testrecipe = 'devtool-test-long-filename'
1489 bb_vars = get_bb_vars(['FILE'], testrecipe)
1490 recipefile = bb_vars['FILE']
1491 patchfilename = '0001-I-ll-patch-you-only-if-devtool-lets-me-to-do-it-corr.patch'
1492 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename)
1493 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename + '.expected')
1494 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1495 self.track_for_cleanup(tempdir)
1496 self.track_for_cleanup(self.workspacedir)
1497 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1498 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1499 result = runCmd('devtool modify %s' % testrecipe)
1500 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1501 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe)
1502 # Check recipe got changed as expected
1503 with open(newpatchfile, 'r') as f:
1504 desiredlines = f.readlines()
1505 with open(patchfile, 'r') as f:
1506 newlines = f.readlines()
1507 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo
1508 # which changes the metadata subject which is added into the patch, but keep
1509 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh
1510 # devtool-test-ignored manually, then it should generate exactly the same .patch file
1511 self.assertEqual(desiredlines[5:], newlines[5:])
1512
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001513 def test_devtool_update_recipe_local_files_3(self):
1514 # First, modify the recipe
1515 testrecipe = 'devtool-test-localonly'
1516 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1517 recipefile = bb_vars['FILE']
1518 src_uri = bb_vars['SRC_URI']
1519 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1520 self.track_for_cleanup(tempdir)
1521 self.track_for_cleanup(self.workspacedir)
1522 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1523 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1524 result = runCmd('devtool modify %s' % testrecipe)
1525 # Modify one file
1526 runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files'))
1527 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1528 result = runCmd('devtool update-recipe %s' % testrecipe)
1529 expected_status = [(' M', '.*/%s/file2$' % testrecipe)]
1530 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1531
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001532 def test_devtool_update_recipe_local_patch_gz(self):
1533 # First, modify the recipe
1534 testrecipe = 'devtool-test-patch-gz'
1535 if get_bb_var('DISTRO') == 'poky-tiny':
1536 self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe)
1537 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1538 recipefile = bb_vars['FILE']
1539 src_uri = bb_vars['SRC_URI']
1540 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1541 self.track_for_cleanup(tempdir)
1542 self.track_for_cleanup(self.workspacedir)
1543 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1544 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1545 result = runCmd('devtool modify %s' % testrecipe)
1546 # Modify one file
1547 srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
1548 runCmd('echo "Another line" >> README', cwd=srctree)
Patrick Williams169d7bc2024-01-05 11:33:25 -06001549 runCmd('git commit -a --amend --no-edit --no-verify', cwd=srctree)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001550 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1551 result = runCmd('devtool update-recipe %s' % testrecipe)
1552 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
1553 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1554 patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz')
1555 result = runCmd('file %s' % patch_gz)
1556 if 'gzip compressed data' not in result.output:
1557 self.fail('New patch file is not gzipped - file reports:\n%s' % result.output)
1558
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001559 def test_devtool_update_recipe_local_files_subdir(self):
1560 # Try devtool update-recipe on a recipe that has a file with subdir= set in
1561 # SRC_URI such that it overwrites a file that was in an archive that
1562 # was also in SRC_URI
1563 # First, modify the recipe
1564 testrecipe = 'devtool-test-subdir'
1565 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1566 recipefile = bb_vars['FILE']
1567 src_uri = bb_vars['SRC_URI']
1568 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1569 self.track_for_cleanup(tempdir)
1570 self.track_for_cleanup(self.workspacedir)
1571 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1572 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1573 result = runCmd('devtool modify %s' % testrecipe)
1574 testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile')
1575 self.assertExists(testfile, 'Extracted source could not be found')
1576 with open(testfile, 'r') as f:
1577 contents = f.read().rstrip()
1578 self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been')
1579 # Test devtool update-recipe without modifying any files
1580 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1581 result = runCmd('devtool update-recipe %s' % testrecipe)
1582 expected_status = []
1583 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1584
Andrew Geissler615f2f12022-07-15 14:00:58 -05001585 def test_devtool_finish_modify_git_subdir(self):
1586 # Check preconditions
1587 testrecipe = 'dos2unix'
Patrick Williams520786c2023-06-25 16:20:36 -05001588 self.append_config('ERROR_QA:remove:pn-dos2unix = "patch-status"\n')
Andrew Geissler615f2f12022-07-15 14:00:58 -05001589 bb_vars = get_bb_vars(['SRC_URI', 'S', 'WORKDIR', 'FILE'], testrecipe)
1590 self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe)
1591 workdir_git = '%s/git/' % bb_vars['WORKDIR']
1592 if not bb_vars['S'].startswith(workdir_git):
1593 self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe)
1594 subdir = bb_vars['S'].split(workdir_git, 1)[1]
1595 # Clean up anything in the workdir/sysroot/sstate cache
1596 bitbake('%s -c cleansstate' % testrecipe)
1597 # Try modifying a recipe
1598 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1599 self.track_for_cleanup(tempdir)
1600 self.track_for_cleanup(self.workspacedir)
1601 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1602 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1603 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1604 testsrcfile = os.path.join(tempdir, subdir, 'dos2unix.c')
1605 self.assertExists(testsrcfile, 'Extracted source could not be found')
1606 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
1607 self.assertNotExists(os.path.join(tempdir, subdir, '.git'), 'Subdirectory has been initialised as a git repo')
1608 # Check git repo
1609 self._check_src_repo(tempdir)
1610 # Modify file
1611 runCmd("sed -i '1s:^:/* Add a comment */\\n:' %s" % testsrcfile)
1612 result = runCmd('git commit -a -m "Add a comment"', cwd=tempdir)
1613 # Now try updating original recipe
1614 recipefile = bb_vars['FILE']
1615 recipedir = os.path.dirname(recipefile)
1616 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
1617 result = runCmd('devtool update-recipe %s' % testrecipe)
1618 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1619 ('??', '.*/%s/%s/$' % (testrecipe, testrecipe))]
1620 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1621 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
1622 removelines = ['SRC_URI = "git://.*"']
1623 addlines = [
1624 'SRC_URI = "git://.* \\\\',
1625 'file://0001-Add-a-comment.patch;patchdir=.. \\\\',
1626 '"'
1627 ]
1628 self._check_diff(result.output, addlines, removelines)
1629 # Put things back so we can run devtool finish on a different layer
1630 runCmd('cd %s; rm -f %s/*.patch; git checkout .' % (recipedir, testrecipe))
1631 # Run devtool finish
1632 res = re.search('recipes-.*', recipedir)
1633 self.assertTrue(res, 'Unable to find recipe subdirectory')
1634 recipesubdir = res[0]
1635 self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, recipesubdir))
1636 result = runCmd('devtool finish %s meta-selftest' % testrecipe)
1637 # Check bbappend file contents
1638 appendfn = os.path.join(self.testlayer_path, recipesubdir, '%s_%%.bbappend' % testrecipe)
1639 with open(appendfn, 'r') as f:
1640 appendlines = f.readlines()
1641 expected_appendlines = [
1642 'FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n',
1643 '\n',
1644 'SRC_URI += "file://0001-Add-a-comment.patch;patchdir=.."\n',
1645 '\n'
1646 ]
1647 self.assertEqual(appendlines, expected_appendlines)
1648 self.assertExists(os.path.join(os.path.dirname(appendfn), testrecipe, '0001-Add-a-comment.patch'))
1649 # Try building
1650 bitbake('%s -c patch' % testrecipe)
1651
Patrick Williamsda295312023-12-05 16:48:56 -06001652 def test_devtool_git_submodules(self):
1653 # This tests if we can add a patch in a git submodule and extract it properly using devtool finish
1654 # Check preconditions
1655 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1656 self.track_for_cleanup(self.workspacedir)
1657 recipe = 'vulkan-samples'
1658 src_uri = get_bb_var('SRC_URI', recipe)
1659 self.assertIn('gitsm://', src_uri, 'This test expects the %s recipe to be a git recipe with submodules' % recipe)
1660 oldrecipefile = get_bb_var('FILE', recipe)
1661 recipedir = os.path.dirname(oldrecipefile)
1662 result = runCmd('git status --porcelain .', cwd=recipedir)
1663 if result.output.strip():
1664 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
1665 self.assertIn('/meta/', recipedir)
1666 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1667 self.track_for_cleanup(tempdir)
1668 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1669 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
1670 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
1671 # Test devtool status
1672 result = runCmd('devtool status')
1673 self.assertIn(recipe, result.output)
1674 self.assertIn(tempdir, result.output)
1675 # Modify a source file in a submodule, (grab the first one)
1676 result = runCmd('git submodule --quiet foreach \'echo $sm_path\'', cwd=tempdir)
1677 submodule = result.output.splitlines()[0]
1678 submodule_path = os.path.join(tempdir, submodule)
1679 runCmd('echo "#This is a first comment" >> testfile', cwd=submodule_path)
1680 result = runCmd('git status --porcelain . ', cwd=submodule_path)
1681 self.assertIn("testfile", result.output)
1682 runCmd('git add testfile; git commit -m "Adding a new file"', cwd=submodule_path)
1683
1684 # Try finish to the original layer
1685 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1686 runCmd('devtool finish -f %s meta' % recipe)
1687 result = runCmd('devtool status')
1688 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1689 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
1690 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
1691 ('??', '.*/.*-Adding-a-new-file.patch$')]
1692 self._check_repo_status(recipedir, expected_status)
1693 # Make sure the patch is added to the recipe with the correct "patchdir" option
1694 result = runCmd('git diff .', cwd=recipedir)
1695 addlines = [
1696 'file://0001-Adding-a-new-file.patch;patchdir=%s \\\\' % submodule
1697 ]
1698 self._check_diff(result.output, addlines, [])
Andrew Geissler615f2f12022-07-15 14:00:58 -05001699
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001700class DevtoolExtractTests(DevtoolBase):
1701
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001702 def test_devtool_extract(self):
1703 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1704 # Try devtool extract
1705 self.track_for_cleanup(tempdir)
1706 self.track_for_cleanup(self.workspacedir)
1707 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1708 result = runCmd('devtool extract matchbox-terminal %s' % tempdir)
1709 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
1710 self._check_src_repo(tempdir)
1711
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001712 def test_devtool_extract_virtual(self):
1713 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1714 # Try devtool extract
1715 self.track_for_cleanup(tempdir)
1716 self.track_for_cleanup(self.workspacedir)
1717 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1718 result = runCmd('devtool extract virtual/make %s' % tempdir)
1719 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
1720 self._check_src_repo(tempdir)
1721
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001722 def test_devtool_reset_all(self):
1723 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1724 self.track_for_cleanup(tempdir)
1725 self.track_for_cleanup(self.workspacedir)
1726 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1727 testrecipe1 = 'mdadm'
1728 testrecipe2 = 'cronie'
1729 result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1)))
1730 result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2)))
1731 result = runCmd('devtool build %s' % testrecipe1)
1732 result = runCmd('devtool build %s' % testrecipe2)
1733 stampprefix1 = get_bb_var('STAMP', testrecipe1)
1734 self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1)
1735 stampprefix2 = get_bb_var('STAMP', testrecipe2)
1736 self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2)
1737 result = runCmd('devtool reset -a')
1738 self.assertIn(testrecipe1, result.output)
1739 self.assertIn(testrecipe2, result.output)
1740 result = runCmd('devtool status')
1741 self.assertNotIn(testrecipe1, result.output)
1742 self.assertNotIn(testrecipe2, result.output)
1743 matches1 = glob.glob(stampprefix1 + '*')
1744 self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1)
1745 matches2 = glob.glob(stampprefix2 + '*')
1746 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
1747
Patrick Williams45852732022-04-02 08:58:32 -05001748 @OETestTag("runqemu")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001749 def test_devtool_deploy_target(self):
Andrew Geissler220dafd2023-10-04 10:18:08 -05001750 self._check_runqemu_prerequisites()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001751 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1752 # Definitions
1753 testrecipe = 'mdadm'
1754 testfile = '/sbin/mdadm'
1755 testimage = 'oe-selftest-image'
1756 testcommand = '/sbin/mdadm --help'
1757 # Build an image to run
1758 bitbake("%s qemu-native qemu-helper-native" % testimage)
1759 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
1760 self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
1761 self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage))
1762 # Clean recipe so the first deploy will fail
1763 bitbake("%s -c clean" % testrecipe)
1764 # Try devtool modify
1765 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1766 self.track_for_cleanup(tempdir)
1767 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001768 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
Brad Bishop00e122a2019-10-05 11:10:57 -04001769 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001770 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1771 # Test that deploy-target at this point fails (properly)
1772 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe, ignore_status=True)
1773 self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output)
1774 self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output)
1775 result = runCmd('devtool build %s' % testrecipe)
1776 # First try a dry-run of deploy-target
1777 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe)
1778 self.assertIn(' %s' % testfile, result.output)
1779 # Boot the image
1780 with runqemu(testimage) as qemu:
1781 # Now really test deploy-target
1782 result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, qemu.ip))
1783 # Run a test command to see if it was installed properly
1784 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
1785 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
1786 # Check if it deployed all of the files with the right ownership/perms
1787 # First look on the host - need to do this under pseudo to get the correct ownership/perms
1788 bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe)
1789 installdir = bb_vars['D']
1790 fakerootenv = bb_vars['FAKEROOTENV']
1791 fakerootcmd = bb_vars['FAKEROOTCMD']
Brad Bishop19323692019-04-05 15:28:33 -04001792 result = runCmd('%s %s find . -type f -exec ls -l {} \\;' % (fakerootenv, fakerootcmd), cwd=installdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001793 filelist1 = self._process_ls_output(result.output)
1794
1795 # Now look on the target
1796 tempdir2 = tempfile.mkdtemp(prefix='devtoolqa')
1797 self.track_for_cleanup(tempdir2)
1798 tmpfilelist = os.path.join(tempdir2, 'files.txt')
1799 with open(tmpfilelist, 'w') as f:
1800 for line in filelist1:
1801 splitline = line.split()
1802 f.write(splitline[-1] + '\n')
1803 result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip))
1804 filelist2 = self._process_ls_output(result.output)
1805 filelist1.sort(key=lambda item: item.split()[-1])
1806 filelist2.sort(key=lambda item: item.split()[-1])
1807 self.assertEqual(filelist1, filelist2)
1808 # Test undeploy-target
1809 result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip))
1810 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
1811 self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
1812
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001813 def test_devtool_build_image(self):
1814 """Test devtool build-image plugin"""
1815 # Check preconditions
1816 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1817 image = 'core-image-minimal'
1818 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001819 self.add_command_to_tearDown('bitbake -c clean %s' % image)
Brad Bishop00e122a2019-10-05 11:10:57 -04001820 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001821 bitbake('%s -c clean' % image)
1822 # Add target and native recipes to workspace
1823 recipes = ['mdadm', 'parted-native']
1824 for recipe in recipes:
1825 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1826 self.track_for_cleanup(tempdir)
1827 self.add_command_to_tearDown('bitbake -c clean %s' % recipe)
1828 runCmd('devtool modify %s -x %s' % (recipe, tempdir))
1829 # Try to build image
1830 result = runCmd('devtool build-image %s' % image)
1831 self.assertNotEqual(result, 0, 'devtool build-image failed')
1832 # Check if image contains expected packages
1833 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
1834 image_link_name = get_bb_var('IMAGE_LINK_NAME', image)
1835 reqpkgs = [item for item in recipes if not item.endswith('-native')]
1836 with open(os.path.join(deploy_dir_image, image_link_name + '.manifest'), 'r') as f:
1837 for line in f:
1838 splitval = line.split()
1839 if splitval:
1840 pkg = splitval[0]
1841 if pkg in reqpkgs:
1842 reqpkgs.remove(pkg)
1843 if reqpkgs:
1844 self.fail('The following packages were not present in the image as expected: %s' % ', '.join(reqpkgs))
1845
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001846class DevtoolUpgradeTests(DevtoolBase):
1847
Patrick Williams45852732022-04-02 08:58:32 -05001848 def setUp(self):
1849 super().setUp()
1850 try:
1851 runCmd("git config --global user.name")
1852 runCmd("git config --global user.email")
1853 except:
1854 self.skip("Git user.name and user.email must be set")
1855
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001856 def test_devtool_upgrade(self):
1857 # Check preconditions
1858 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1859 self.track_for_cleanup(self.workspacedir)
1860 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1861 # Check parameters
1862 result = runCmd('devtool upgrade -h')
1863 for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split():
1864 self.assertIn(param, result.output)
1865 # For the moment, we are using a real recipe.
1866 recipe = 'devtool-upgrade-test1'
1867 version = '1.6.0'
1868 oldrecipefile = get_bb_var('FILE', recipe)
1869 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1870 self.track_for_cleanup(tempdir)
1871 # Check that recipe is not already under devtool control
1872 result = runCmd('devtool status')
1873 self.assertNotIn(recipe, result.output)
1874 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
1875 # we are downgrading instead of upgrading.
1876 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
1877 # Check if srctree at least is populated
1878 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, version))
1879 # Check new recipe subdirectory is present
1880 self.assertExists(os.path.join(self.workspacedir, 'recipes', recipe, '%s-%s' % (recipe, version)), 'Recipe folder should exist')
1881 # Check new recipe file is present
1882 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
1883 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
1884 # Check devtool status and make sure recipe is present
1885 result = runCmd('devtool status')
1886 self.assertIn(recipe, result.output)
1887 self.assertIn(tempdir, result.output)
1888 # Check recipe got changed as expected
1889 with open(oldrecipefile + '.upgraded', 'r') as f:
1890 desiredlines = f.readlines()
1891 with open(newrecipefile, 'r') as f:
1892 newlines = f.readlines()
1893 self.assertEqual(desiredlines, newlines)
1894 # Check devtool reset recipe
1895 result = runCmd('devtool reset %s -n' % recipe)
1896 result = runCmd('devtool status')
1897 self.assertNotIn(recipe, result.output)
1898 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
1899
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001900 def test_devtool_upgrade_git(self):
1901 # Check preconditions
1902 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1903 self.track_for_cleanup(self.workspacedir)
1904 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1905 recipe = 'devtool-upgrade-test2'
1906 commit = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
1907 oldrecipefile = get_bb_var('FILE', recipe)
1908 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1909 self.track_for_cleanup(tempdir)
1910 # Check that recipe is not already under devtool control
1911 result = runCmd('devtool status')
1912 self.assertNotIn(recipe, result.output)
1913 # Check upgrade
1914 result = runCmd('devtool upgrade %s %s -S %s' % (recipe, tempdir, commit))
1915 # Check if srctree at least is populated
1916 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit))
1917 # Check new recipe file is present
1918 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile))
1919 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
1920 # Check devtool status and make sure recipe is present
1921 result = runCmd('devtool status')
1922 self.assertIn(recipe, result.output)
1923 self.assertIn(tempdir, result.output)
1924 # Check recipe got changed as expected
1925 with open(oldrecipefile + '.upgraded', 'r') as f:
1926 desiredlines = f.readlines()
1927 with open(newrecipefile, 'r') as f:
1928 newlines = f.readlines()
1929 self.assertEqual(desiredlines, newlines)
1930 # Check devtool reset recipe
1931 result = runCmd('devtool reset %s -n' % recipe)
1932 result = runCmd('devtool status')
1933 self.assertNotIn(recipe, result.output)
1934 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
1935
Patrick Williams169d7bc2024-01-05 11:33:25 -06001936 def test_devtool_upgrade_drop_md5sum(self):
1937 # Check preconditions
1938 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1939 self.track_for_cleanup(self.workspacedir)
1940 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1941 # For the moment, we are using a real recipe.
1942 recipe = 'devtool-upgrade-test3'
1943 version = '1.6.0'
1944 oldrecipefile = get_bb_var('FILE', recipe)
1945 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1946 self.track_for_cleanup(tempdir)
1947 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
1948 # we are downgrading instead of upgrading.
1949 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
1950 # Check new recipe file is present
1951 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
1952 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
1953 # Check recipe got changed as expected
1954 with open(oldrecipefile + '.upgraded', 'r') as f:
1955 desiredlines = f.readlines()
1956 with open(newrecipefile, 'r') as f:
1957 newlines = f.readlines()
1958 self.assertEqual(desiredlines, newlines)
1959
1960 def test_devtool_upgrade_all_checksums(self):
1961 # Check preconditions
1962 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1963 self.track_for_cleanup(self.workspacedir)
1964 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1965 # For the moment, we are using a real recipe.
1966 recipe = 'devtool-upgrade-test4'
1967 version = '1.6.0'
1968 oldrecipefile = get_bb_var('FILE', recipe)
1969 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1970 self.track_for_cleanup(tempdir)
1971 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
1972 # we are downgrading instead of upgrading.
1973 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
1974 # Check new recipe file is present
1975 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
1976 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
1977 # Check recipe got changed as expected
1978 with open(oldrecipefile + '.upgraded', 'r') as f:
1979 desiredlines = f.readlines()
1980 with open(newrecipefile, 'r') as f:
1981 newlines = f.readlines()
1982 self.assertEqual(desiredlines, newlines)
1983
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001984 def test_devtool_layer_plugins(self):
1985 """Test that devtool can use plugins from other layers.
1986
1987 This test executes the selftest-reverse command from meta-selftest."""
1988
1989 self.track_for_cleanup(self.workspacedir)
1990 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1991
1992 s = "Microsoft Made No Profit From Anyone's Zunes Yo"
1993 result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s)
1994 self.assertEqual(result.output, s[::-1])
1995
1996 def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
1997 dstdir = basedstdir
1998 self.assertExists(dstdir)
1999 for p in paths:
2000 dstdir = os.path.join(dstdir, p)
2001 if not os.path.exists(dstdir):
Patrick Williams169d7bc2024-01-05 11:33:25 -06002002 try:
2003 os.makedirs(dstdir)
2004 except PermissionError:
2005 return False
2006 except OSError as e:
2007 if e.errno == errno.EROFS:
2008 return False
2009 else:
2010 raise e
Andrew Geissler475cb722020-07-10 16:00:51 -05002011 if p == "lib":
2012 # Can race with other tests
2013 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir)
2014 else:
2015 self.track_for_cleanup(dstdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002016 dstfile = os.path.join(dstdir, os.path.basename(srcfile))
2017 if srcfile != dstfile:
Patrick Williams169d7bc2024-01-05 11:33:25 -06002018 try:
2019 shutil.copy(srcfile, dstfile)
2020 except PermissionError:
2021 return False
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002022 self.track_for_cleanup(dstfile)
Patrick Williams169d7bc2024-01-05 11:33:25 -06002023 return True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002024
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002025 def test_devtool_load_plugin(self):
2026 """Test that devtool loads only the first found plugin in BBPATH."""
2027
2028 self.track_for_cleanup(self.workspacedir)
2029 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2030
2031 devtool = runCmd("which devtool")
2032 fromname = runCmd("devtool --quiet pluginfile")
2033 srcfile = fromname.output
2034 bbpath = get_bb_var('BBPATH')
2035 searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)]
2036 plugincontent = []
2037 with open(srcfile) as fh:
2038 plugincontent = fh.readlines()
2039 try:
2040 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
Patrick Williams169d7bc2024-01-05 11:33:25 -06002041 searchpath = [
2042 path for path in searchpath
2043 if self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
2044 ]
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002045 result = runCmd("devtool --quiet count")
2046 self.assertEqual(result.output, '1')
2047 result = runCmd("devtool --quiet multiloaded")
2048 self.assertEqual(result.output, "no")
2049 for path in searchpath:
2050 result = runCmd("devtool --quiet bbdir")
Patrick Williams169d7bc2024-01-05 11:33:25 -06002051 self.assertEqual(os.path.realpath(result.output), os.path.realpath(path))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002052 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
2053 finally:
2054 with open(srcfile, 'w') as fh:
2055 fh.writelines(plugincontent)
2056
2057 def _setup_test_devtool_finish_upgrade(self):
2058 # Check preconditions
2059 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2060 self.track_for_cleanup(self.workspacedir)
2061 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2062 # Use a "real" recipe from meta-selftest
2063 recipe = 'devtool-upgrade-test1'
2064 oldversion = '1.5.3'
2065 newversion = '1.6.0'
2066 oldrecipefile = get_bb_var('FILE', recipe)
2067 recipedir = os.path.dirname(oldrecipefile)
2068 result = runCmd('git status --porcelain .', cwd=recipedir)
2069 if result.output.strip():
2070 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
2071 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2072 self.track_for_cleanup(tempdir)
2073 # Check that recipe is not already under devtool control
2074 result = runCmd('devtool status')
2075 self.assertNotIn(recipe, result.output)
2076 # Do the upgrade
2077 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion))
2078 # Check devtool status and make sure recipe is present
2079 result = runCmd('devtool status')
2080 self.assertIn(recipe, result.output)
2081 self.assertIn(tempdir, result.output)
2082 # Make a change to the source
2083 result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir)
2084 result = runCmd('git status --porcelain', cwd=tempdir)
2085 self.assertIn('M src/pv/number.c', result.output)
2086 result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir)
2087 # Check if patch is there
2088 recipedir = os.path.dirname(oldrecipefile)
2089 olddir = os.path.join(recipedir, recipe + '-' + oldversion)
2090 patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch'
Brad Bishop6dbb3162019-11-25 09:41:34 -05002091 backportedpatchfn = 'backported.patch'
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002092 self.assertExists(os.path.join(olddir, patchfn), 'Original patch file does not exist')
Brad Bishop6dbb3162019-11-25 09:41:34 -05002093 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Backported patch file does not exist')
2094 return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002095
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002096 def test_devtool_finish_upgrade_origlayer(self):
Brad Bishop6dbb3162019-11-25 09:41:34 -05002097 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002098 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
2099 self.assertIn('/meta-selftest/', recipedir)
2100 # Try finish to the original layer
2101 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
2102 result = runCmd('devtool finish %s meta-selftest' % recipe)
2103 result = runCmd('devtool status')
2104 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
2105 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
2106 self.assertNotExists(oldrecipefile, 'Old recipe file should have been deleted but wasn\'t')
2107 self.assertNotExists(os.path.join(olddir, patchfn), 'Old patch file should have been deleted but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05002108 self.assertNotExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should have been deleted but wasn\'t')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002109 newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion))
2110 newdir = os.path.join(recipedir, recipe + '-' + newversion)
2111 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t')
2112 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05002113 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002114 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05002115 with open(newrecipefile, 'r') as f:
2116 newcontent = f.read()
2117 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't")
2118 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was")
2119 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't")
2120 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI")
2121
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002122
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002123 def test_devtool_finish_upgrade_otherlayer(self):
Brad Bishop6dbb3162019-11-25 09:41:34 -05002124 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002125 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
2126 self.assertIn('/meta-selftest/', recipedir)
2127 # Try finish to a different layer - should create a bbappend
2128 # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here
2129 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
2130 oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta')
2131 newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool')
2132 newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion))
2133 self.track_for_cleanup(newrecipedir)
2134 result = runCmd('devtool finish %s oe-core' % recipe)
2135 result = runCmd('devtool status')
2136 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
2137 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
2138 self.assertExists(oldrecipefile, 'Old recipe file should not have been deleted')
2139 self.assertExists(os.path.join(olddir, patchfn), 'Old patch file should not have been deleted')
Brad Bishop6dbb3162019-11-25 09:41:34 -05002140 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should not have been deleted')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002141 newdir = os.path.join(newrecipedir, recipe + '-' + newversion)
2142 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t')
2143 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05002144 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002145 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05002146 with open(newrecipefile, 'r') as f:
2147 newcontent = f.read()
2148 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't")
2149 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was")
2150 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't")
2151 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002152
2153 def _setup_test_devtool_finish_modify(self):
2154 # Check preconditions
2155 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2156 # Try modifying a recipe
2157 self.track_for_cleanup(self.workspacedir)
2158 recipe = 'mdadm'
2159 oldrecipefile = get_bb_var('FILE', recipe)
2160 recipedir = os.path.dirname(oldrecipefile)
2161 result = runCmd('git status --porcelain .', cwd=recipedir)
2162 if result.output.strip():
2163 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
2164 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2165 self.track_for_cleanup(tempdir)
2166 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2167 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
2168 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
2169 # Test devtool status
2170 result = runCmd('devtool status')
2171 self.assertIn(recipe, result.output)
2172 self.assertIn(tempdir, result.output)
2173 # Make a change to the source
2174 result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir)
2175 result = runCmd('git status --porcelain', cwd=tempdir)
2176 self.assertIn('M maps.c', result.output)
2177 result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir)
2178 for entry in os.listdir(recipedir):
2179 filesdir = os.path.join(recipedir, entry)
2180 if os.path.isdir(filesdir):
2181 break
2182 else:
2183 self.fail('Unable to find recipe files directory for %s' % recipe)
2184 return recipe, oldrecipefile, recipedir, filesdir
2185
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002186 def test_devtool_finish_modify_origlayer(self):
2187 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
2188 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
2189 self.assertIn('/meta/', recipedir)
2190 # Try finish to the original layer
2191 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
2192 result = runCmd('devtool finish %s meta' % recipe)
2193 result = runCmd('devtool status')
2194 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
2195 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
2196 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
2197 ('??', '.*/.*-Add-a-comment-to-the-code.patch$')]
2198 self._check_repo_status(recipedir, expected_status)
2199
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002200 def test_devtool_finish_modify_otherlayer(self):
2201 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
2202 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
2203 self.assertIn('/meta/', recipedir)
2204 relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta'))
2205 appenddir = os.path.join(get_test_layer(), relpth)
2206 self.track_for_cleanup(appenddir)
2207 # Try finish to the original layer
2208 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
2209 result = runCmd('devtool finish %s meta-selftest' % recipe)
2210 result = runCmd('devtool status')
2211 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
2212 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
2213 result = runCmd('git status --porcelain .', cwd=recipedir)
2214 if result.output.strip():
2215 self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip()))
2216 recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0]
2217 recipefn = recipefn.split('_')[0] + '_%'
2218 appendfile = os.path.join(appenddir, recipefn + '.bbappend')
2219 self.assertExists(appendfile, 'bbappend %s should have been created but wasn\'t' % appendfile)
2220 newdir = os.path.join(appenddir, recipe)
2221 files = os.listdir(newdir)
2222 foundpatch = None
2223 for fn in files:
2224 if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'):
2225 foundpatch = fn
2226 if not foundpatch:
2227 self.fail('No patch file created next to bbappend')
2228 files.remove(foundpatch)
2229 if files:
2230 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
2231
Patrick Williams73bd93f2024-02-20 08:07:48 -06002232 def test_devtool_finish_update_patch(self):
2233 # This test uses a modified version of the sysdig recipe from meta-oe.
2234 # - The patches have been renamed.
2235 # - The dependencies are commented out since the recipe is not being
2236 # built.
2237 #
2238 # The sysdig recipe is interesting in that it fetches two different Git
2239 # repositories, and there are patches for both. This leads to that
2240 # devtool will create ignore commits as it uses Git submodules to keep
2241 # track of the second repository.
2242 #
2243 # This test will verify that the ignored commits actually are ignored
2244 # when a commit in between is modified. It will also verify that the
2245 # updated patch keeps its original name.
2246
2247 # Check preconditions
2248 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2249 # Try modifying a recipe
2250 self.track_for_cleanup(self.workspacedir)
2251 recipe = 'sysdig-selftest'
2252 recipefile = get_bb_var('FILE', recipe)
2253 recipedir = os.path.dirname(recipefile)
2254 result = runCmd('git status --porcelain .', cwd=recipedir)
2255 if result.output.strip():
2256 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
2257 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2258 self.track_for_cleanup(tempdir)
2259 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2260 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
2261 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (recipedir, recipe, recipe, os.path.basename(recipefile)))
2262 self.assertExists(os.path.join(tempdir, 'CMakeLists.txt'), 'Extracted source could not be found')
2263 # Make a change to one of the existing commits
2264 result = runCmd('echo "# A comment " >> CMakeLists.txt', cwd=tempdir)
2265 result = runCmd('git status --porcelain', cwd=tempdir)
2266 self.assertIn('M CMakeLists.txt', result.output)
2267 result = runCmd('git commit --fixup HEAD^ CMakeLists.txt', cwd=tempdir)
2268 result = runCmd('git show -s --format=%s', cwd=tempdir)
2269 self.assertIn('fixup! cmake: Pass PROBE_NAME via CFLAGS', result.output)
2270 result = runCmd('GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash devtool-base', cwd=tempdir)
2271 result = runCmd('devtool finish %s meta-selftest' % recipe)
2272 result = runCmd('devtool status')
2273 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
2274 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
2275 expected_status = [(' M', '.*/0099-cmake-Pass-PROBE_NAME-via-CFLAGS.patch$')]
2276 self._check_repo_status(recipedir, expected_status)
2277
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002278 def test_devtool_rename(self):
2279 # Check preconditions
2280 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
2281 self.track_for_cleanup(self.workspacedir)
2282 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2283
2284 # First run devtool add
2285 # We already have this recipe in OE-Core, but that doesn't matter
2286 recipename = 'i2c-tools'
2287 recipever = '3.1.2'
2288 recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever))
2289 url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever
2290 def add_recipe():
2291 result = runCmd('devtool add %s' % url)
2292 self.assertExists(recipefile, 'Expected recipe file not created')
2293 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory not created')
2294 checkvars = {}
2295 checkvars['S'] = None
2296 checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
2297 self._test_recipe_contents(recipefile, checkvars, [])
2298 add_recipe()
2299 # Now rename it - change both name and version
2300 newrecipename = 'mynewrecipe'
2301 newrecipever = '456'
2302 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever))
2303 result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever))
2304 self.assertExists(newrecipefile, 'Recipe file not renamed')
2305 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists')
2306 newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename)
2307 self.assertExists(newsrctree, 'Source directory not renamed')
2308 checkvars = {}
2309 checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever)
2310 checkvars['SRC_URI'] = url
2311 self._test_recipe_contents(newrecipefile, checkvars, [])
2312 # Try again - change just name this time
2313 result = runCmd('devtool reset -n %s' % newrecipename)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002314 add_recipe()
2315 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
2316 result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
2317 self.assertExists(newrecipefile, 'Recipe file not renamed')
2318 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists')
2319 self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed')
2320 checkvars = {}
2321 checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename
2322 checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
2323 self._test_recipe_contents(newrecipefile, checkvars, [])
2324 # Try again - change just version this time
2325 result = runCmd('devtool reset -n %s' % newrecipename)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002326 add_recipe()
2327 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
2328 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
2329 self.assertExists(newrecipefile, 'Recipe file not renamed')
2330 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists')
2331 checkvars = {}
2332 checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever
2333 checkvars['SRC_URI'] = url
2334 self._test_recipe_contents(newrecipefile, checkvars, [])
2335
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002336 def test_devtool_virtual_kernel_modify(self):
2337 """
2338 Summary: The purpose of this test case is to verify that
2339 devtool modify works correctly when building
2340 the kernel.
2341 Dependencies: NA
2342 Steps: 1. Build kernel with bitbake.
2343 2. Save the config file generated.
2344 3. Clean the environment.
2345 4. Use `devtool modify virtual/kernel` to validate following:
2346 4.1 The source is checked out correctly.
2347 4.2 The resulting configuration is the same as
2348 what was get on step 2.
2349 4.3 The Kernel can be build correctly.
2350 4.4 Changes made on the source are reflected on the
2351 subsequent builds.
2352 4.5 Changes on the configuration are reflected on the
2353 subsequent builds
2354 Expected: devtool modify is able to checkout the source of the kernel
2355 and modification to the source and configurations are reflected
2356 when building the kernel.
Patrick Williams45852732022-04-02 08:58:32 -05002357 """
2358 kernel_provider = self.td['PREFERRED_PROVIDER_virtual/kernel']
2359
Andrew Geissler82c905d2020-04-13 13:39:40 -05002360 # Clean up the environment
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002361 bitbake('%s -c clean' % kernel_provider)
2362 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2363 tempdir_cfg = tempfile.mkdtemp(prefix='config_qa')
2364 self.track_for_cleanup(tempdir)
2365 self.track_for_cleanup(tempdir_cfg)
2366 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002367 self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider)
Brad Bishop00e122a2019-10-05 11:10:57 -04002368 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002369 #Step 1
2370 #Here is just generated the config file instead of all the kernel to optimize the
2371 #time of executing this test case.
2372 bitbake('%s -c configure' % kernel_provider)
2373 bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config')
2374 #Step 2
2375 runCmd('cp %s %s' % (bbconfig, tempdir_cfg))
2376 self.assertExists(os.path.join(tempdir_cfg, '.config'), 'Could not copy .config file from kernel')
2377
2378 tmpconfig = os.path.join(tempdir_cfg, '.config')
2379 #Step 3
2380 bitbake('%s -c clean' % kernel_provider)
2381 #Step 4.1
2382 runCmd('devtool modify virtual/kernel -x %s' % tempdir)
2383 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
2384 #Step 4.2
2385 configfile = os.path.join(tempdir,'.config')
Patrick Williams45852732022-04-02 08:58:32 -05002386 runCmd('diff %s %s' % (tmpconfig, configfile))
2387
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002388 #Step 4.3
2389 #NOTE: virtual/kernel is mapped to kernel_provider
Patrick Williams45852732022-04-02 08:58:32 -05002390 runCmd('devtool build %s' % kernel_provider)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002391 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
2392 self.assertExists(kernelfile, 'Kernel was not build correctly')
2393
2394 #Modify the kernel source
Patrick Williams45852732022-04-02 08:58:32 -05002395 modfile = os.path.join(tempdir, 'init/version.c')
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002396 # Moved to uts.h in 6.1 onwards
2397 modfile2 = os.path.join(tempdir, 'include/linux/uts.h')
2398 runCmd("sed -i 's/Linux/LiNuX/g' %s %s" % (modfile, modfile2))
Patrick Williams45852732022-04-02 08:58:32 -05002399
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002400 #Modify the configuration
Patrick Williams45852732022-04-02 08:58:32 -05002401 codeconfigfile = os.path.join(tempdir, '.config.new')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002402 modconfopt = "CONFIG_SG_POOL=n"
Patrick Williams45852732022-04-02 08:58:32 -05002403 runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
2404
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002405 #Build again kernel with devtool
Patrick Williams45852732022-04-02 08:58:32 -05002406 runCmd('devtool build %s' % kernel_provider)
2407
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002408 #Step 4.4
Patrick Williams45852732022-04-02 08:58:32 -05002409 runCmd("grep '%s' %s" % ('LiNuX', kernelfile))
2410
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002411 #Step 4.5
Patrick Williams45852732022-04-02 08:58:32 -05002412 runCmd("grep %s %s" % (modconfopt, codeconfigfile))
Patrick Williams73bd93f2024-02-20 08:07:48 -06002413
2414
2415class DevtoolIdeSdkTests(DevtoolBase):
2416 def _write_bb_config(self, recipe_names):
2417 """Helper to write the bitbake local.conf file"""
2418 conf_lines = [
2419 'IMAGE_CLASSES += "image-combined-dbg"',
2420 'IMAGE_GEN_DEBUGFS = "1"',
2421 'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join(
2422 [r + '-ptest' for r in recipe_names])
2423 ]
2424 self.write_config("\n".join(conf_lines))
2425
2426 def _check_workspace(self):
2427 """Check if a workspace directory is available and setup the cleanup"""
2428 self.assertTrue(not os.path.exists(self.workspacedir),
2429 'This test cannot be run with a workspace directory under the build directory')
2430 self.track_for_cleanup(self.workspacedir)
2431 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
2432
2433 def _workspace_scripts_dir(self, recipe_name):
2434 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts'))
2435
2436 def _sources_scripts_dir(self, src_dir):
2437 return os.path.realpath(os.path.join(src_dir, 'oe-scripts'))
2438
2439 def _workspace_gdbinit_dir(self, recipe_name):
2440 return os.path.realpath(os.path.join(self.builddir, 'workspace', 'ide-sdk', recipe_name, 'scripts', 'gdbinit'))
2441
2442 def _sources_gdbinit_dir(self, src_dir):
2443 return os.path.realpath(os.path.join(src_dir, 'oe-gdbinit'))
2444
2445 def _devtool_ide_sdk_recipe(self, recipe_name, build_file, testimage):
2446 """Setup a recipe for working with devtool ide-sdk
2447
2448 Basically devtool modify -x followed by some tests
2449 """
2450 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
2451 self.track_for_cleanup(tempdir)
2452 self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name)
2453
2454 result = runCmd('devtool modify %s -x %s' % (recipe_name, tempdir))
2455 self.assertExists(os.path.join(tempdir, build_file),
2456 'Extracted source could not be found')
2457 self.assertExists(os.path.join(self.workspacedir, 'conf',
2458 'layer.conf'), 'Workspace directory not created')
2459 matches = glob.glob(os.path.join(self.workspacedir,
2460 'appends', recipe_name + '.bbappend'))
2461 self.assertTrue(matches, 'bbappend not created %s' % result.output)
2462
2463 # Test devtool status
2464 result = runCmd('devtool status')
2465 self.assertIn(recipe_name, result.output)
2466 self.assertIn(tempdir, result.output)
2467 self._check_src_repo(tempdir)
2468
2469 # Usually devtool ide-sdk would initiate the build of the SDK.
2470 # But there is a circular dependency with starting Qemu and passing the IP of runqemu to devtool ide-sdk.
2471 if testimage:
2472 bitbake("%s qemu-native qemu-helper-native" % testimage)
2473 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
2474 self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
2475 self.add_command_to_tearDown(
2476 'rm -f %s/%s*' % (deploy_dir_image, testimage))
2477
2478 return tempdir
2479
2480 def _get_recipe_ids(self, recipe_name):
2481 """IDs needed to write recipe specific config entries into IDE config files"""
2482 package_arch = get_bb_var('PACKAGE_ARCH', recipe_name)
2483 recipe_id = recipe_name + "-" + package_arch
2484 recipe_id_pretty = recipe_name + ": " + package_arch
2485 return (recipe_id, recipe_id_pretty)
2486
2487 def _verify_install_script_code(self, tempdir, recipe_name):
2488 """Verify the scripts referred by the tasks.json file are fine.
2489
2490 This function does not depend on Qemu. Therefore it verifies the scripts
2491 exists and the delete step works as expected. But it does not try to
2492 deploy to Qemu.
2493 """
2494 recipe_id, recipe_id_pretty = self._get_recipe_ids(recipe_name)
2495 with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j:
2496 tasks_d = json.load(tasks_j)
2497 tasks = tasks_d["tasks"]
2498 task_install = next(
2499 (task for task in tasks if task["label"] == "install && deploy-target %s" % recipe_id_pretty), None)
2500 self.assertIsNot(task_install, None)
2501 # execute only the bb_run_do_install script since the deploy would require e.g. Qemu running.
2502 i_and_d_script = "install_and_deploy_" + recipe_id
2503 i_and_d_script_path = os.path.join(
2504 self._workspace_scripts_dir(recipe_name), i_and_d_script)
2505 self.assertExists(i_and_d_script_path)
2506 del_script = "delete_package_dirs_" + recipe_id
2507 del_script_path = os.path.join(
2508 self._workspace_scripts_dir(recipe_name), del_script)
2509 self.assertExists(del_script_path)
2510 runCmd(del_script_path, cwd=tempdir)
2511
2512 def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe):
2513 """Verify deployment and execution in Qemu system work for one recipe.
2514
2515 This function checks the entire SDK workflow: changing the code, recompiling
2516 it and deploying it back to Qemu, and checking that the changes have been
2517 incorporated into the provided binaries. It also runs the tests of the recipe.
2518 """
2519 recipe_id, _ = self._get_recipe_ids(recipe_name)
2520 i_and_d_script = "install_and_deploy_" + recipe_id
2521 install_deploy_cmd = os.path.join(
2522 self._workspace_scripts_dir(recipe_name), i_and_d_script)
2523 self.assertExists(install_deploy_cmd,
2524 '%s script not found' % install_deploy_cmd)
2525 runCmd(install_deploy_cmd)
2526
2527 MAGIC_STRING_ORIG = "Magic: 123456789"
2528 MAGIC_STRING_NEW = "Magic: 987654321"
2529 ptest_cmd = "ptest-runner " + recipe_name
2530
2531 # validate that SSH is working
2532 status, _ = qemu.run("uname")
2533 self.assertEqual(
2534 status, 0, msg="Failed to connect to the SSH server on Qemu")
2535
2536 # Verify the unmodified example prints the magic string
2537 status, output = qemu.run(example_exe)
2538 self.assertEqual(status, 0, msg="%s failed: %s" %
2539 (example_exe, output))
2540 self.assertIn(MAGIC_STRING_ORIG, output)
2541
2542 # Verify the unmodified ptests work
2543 status, output = qemu.run(ptest_cmd)
2544 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
2545 self.assertIn("PASS: cpp-example-lib", output)
2546
Patrick Williams39653562024-03-01 08:54:02 -06002547 # Verify remote debugging works
2548 self._gdb_cross_debugging(
2549 qemu, recipe_name, example_exe, MAGIC_STRING_ORIG)
2550
Patrick Williams73bd93f2024-02-20 08:07:48 -06002551 # Replace the Magic String in the code, compile and deploy to Qemu
2552 cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp')
2553 with open(cpp_example_lib_hpp, 'r') as file:
2554 cpp_code = file.read()
2555 cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW)
2556 with open(cpp_example_lib_hpp, 'w') as file:
2557 file.write(cpp_code)
2558 runCmd(install_deploy_cmd, cwd=tempdir)
2559
2560 # Verify the modified example prints the modified magic string
2561 status, output = qemu.run(example_exe)
2562 self.assertEqual(status, 0, msg="%s failed: %s" %
2563 (example_exe, output))
2564 self.assertNotIn(MAGIC_STRING_ORIG, output)
2565 self.assertIn(MAGIC_STRING_NEW, output)
2566
2567 # Verify the modified example ptests work
2568 status, output = qemu.run(ptest_cmd)
2569 self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output))
2570 self.assertIn("PASS: cpp-example-lib", output)
2571
Patrick Williams39653562024-03-01 08:54:02 -06002572 # Verify remote debugging works wit the modified magic string
2573 self._gdb_cross_debugging(
2574 qemu, recipe_name, example_exe, MAGIC_STRING_NEW)
2575
Patrick Williams73bd93f2024-02-20 08:07:48 -06002576 def _gdb_cross(self):
2577 """Verify gdb-cross is provided by devtool ide-sdk"""
2578 target_arch = self.td["TARGET_ARCH"]
2579 target_sys = self.td["TARGET_SYS"]
2580 gdb_recipe = "gdb-cross-" + target_arch
2581 gdb_binary = target_sys + "-gdb"
2582
2583 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe)
2584 r = runCmd("%s --version" % gdb_binary,
2585 native_sysroot=native_sysroot, target_sys=target_sys)
2586 self.assertEqual(r.status, 0)
2587 self.assertIn("GNU gdb", r.output)
2588
Patrick Williams39653562024-03-01 08:54:02 -06002589 def _gdb_cross_debugging(self, qemu, recipe_name, example_exe, magic_string):
Patrick Williams73bd93f2024-02-20 08:07:48 -06002590 """Verify gdb-cross is working
2591
2592 Test remote debugging:
2593 break main
2594 run
2595 continue
Patrick Williams39653562024-03-01 08:54:02 -06002596 break CppExample::print_json()
2597 continue
2598 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789")
2599 $1 = 0
2600 print CppExample::test_string.compare("cpp-example-lib Magic: 123456789aaa")
2601 $2 = -3
2602 list cpp-example-lib.hpp:13,13
2603 13 inline static const std::string test_string = "cpp-example-lib Magic: 123456789";
2604 continue
Patrick Williams73bd93f2024-02-20 08:07:48 -06002605 """
2606 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
2607 gdbserver_script = os.path.join(self._workspace_scripts_dir(
2608 recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_m')
2609 gdb_script = os.path.join(self._workspace_scripts_dir(
2610 recipe_name), 'gdb_1234_usr-bin-' + example_exe)
2611
2612 # Start a gdbserver
2613 r = runCmd(gdbserver_script)
2614 self.assertEqual(r.status, 0)
2615
2616 # Check there is a gdbserver running
2617 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
2618 self.assertEqual(r.status, 0)
2619 self.assertIn("gdbserver ", r.output)
2620
2621 # Check the pid file is correct
2622 test_cmd = "cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \
2623 example_exe + "/pid)/cmdline"
2624 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd))
2625 self.assertEqual(r.status, 0)
2626 self.assertIn("gdbserver", r.output)
2627
2628 # Test remote debugging works
Patrick Williams39653562024-03-01 08:54:02 -06002629 gdb_batch_cmd = " --batch -ex 'break main' -ex 'run'"
2630 gdb_batch_cmd += " -ex 'break CppExample::print_json()' -ex 'continue'"
2631 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %s\")'" % magic_string
2632 gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %saaa\")'" % magic_string
2633 gdb_batch_cmd += " -ex 'list cpp-example-lib.hpp:13,13'"
2634 gdb_batch_cmd += " -ex 'continue'"
2635 r = runCmd(gdb_script + gdb_batch_cmd)
2636 self.logger.debug("%s %s returned: %s", gdb_script,
2637 gdb_batch_cmd, r.output)
Patrick Williams73bd93f2024-02-20 08:07:48 -06002638 self.assertEqual(r.status, 0)
2639 self.assertIn("Breakpoint 1, main", r.output)
Patrick Williams39653562024-03-01 08:54:02 -06002640 self.assertIn("$1 = 0", r.output) # test.string.compare equal
2641 self.assertIn("$2 = -3", r.output) # test.string.compare longer
2642 self.assertIn(
2643 'inline static const std::string test_string = "cpp-example-lib %s";' % magic_string, r.output)
Patrick Williams73bd93f2024-02-20 08:07:48 -06002644 self.assertIn("exited normally", r.output)
2645
2646 # Stop the gdbserver
2647 r = runCmd(gdbserver_script + ' stop')
2648 self.assertEqual(r.status, 0)
2649
2650 # Check there is no gdbserver running
2651 r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'))
2652 self.assertEqual(r.status, 0)
2653 self.assertNotIn("gdbserver ", r.output)
2654
2655 def _verify_cmake_preset(self, tempdir):
2656 """Verify the generated cmake preset works as expected
2657
2658 Check if compiling works
2659 Check if unit tests can be executed in qemu (not qemu-system)
2660 """
2661 with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j:
2662 cmake_preset_d = json.load(cmake_preset_j)
2663 config_presets = cmake_preset_d["configurePresets"]
2664 self.assertEqual(len(config_presets), 1)
2665 cmake_exe = config_presets[0]["cmakeExecutable"]
2666 preset_name = config_presets[0]["name"]
2667
2668 # Verify the wrapper for cmake native is available
2669 self.assertExists(cmake_exe)
2670
2671 # Verify the cmake preset generated by devtool ide-sdk is available
2672 result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir)
2673 self.assertIn(preset_name, result.output)
2674
2675 # Verify cmake re-uses the o files compiled by bitbake
2676 result = runCmd('%s --build --preset %s' %
2677 (cmake_exe, preset_name), cwd=tempdir)
2678 self.assertIn("ninja: no work to do.", result.output)
2679
2680 # Verify the unit tests work (in Qemu user mode)
2681 result = runCmd('%s --build --preset %s --target test' %
2682 (cmake_exe, preset_name), cwd=tempdir)
2683 self.assertIn("100% tests passed", result.output)
2684
2685 # Verify re-building and testing works again
2686 result = runCmd('%s --build --preset %s --target clean' %
2687 (cmake_exe, preset_name), cwd=tempdir)
2688 self.assertIn("Cleaning", result.output)
2689 result = runCmd('%s --build --preset %s' %
2690 (cmake_exe, preset_name), cwd=tempdir)
2691 self.assertIn("Building", result.output)
2692 self.assertIn("Linking", result.output)
2693 result = runCmd('%s --build --preset %s --target test' %
2694 (cmake_exe, preset_name), cwd=tempdir)
2695 self.assertIn("Running tests...", result.output)
2696 self.assertIn("100% tests passed", result.output)
2697
2698 @OETestTag("runqemu")
2699 def test_devtool_ide_sdk_none_qemu(self):
2700 """Start qemu-system and run tests for multiple recipes. ide=none is used."""
2701 recipe_names = ["cmake-example", "meson-example"]
2702 testimage = "oe-selftest-image"
2703
2704 self._check_workspace()
2705 self._write_bb_config(recipe_names)
2706 self._check_runqemu_prerequisites()
2707
2708 # Verify deployment to Qemu (system mode) works
2709 bitbake(testimage)
2710 with runqemu(testimage, runqemuparams="nographic") as qemu:
2711 # cmake-example recipe
2712 recipe_name = "cmake-example"
2713 example_exe = "cmake-example"
2714 build_file = "CMakeLists.txt"
2715 tempdir = self._devtool_ide_sdk_recipe(
2716 recipe_name, build_file, testimage)
2717 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
2718 recipe_name, testimage, qemu.ip)
2719 runCmd(bitbake_sdk_cmd)
Patrick Williams39653562024-03-01 08:54:02 -06002720 self._gdb_cross()
Patrick Williams73bd93f2024-02-20 08:07:48 -06002721 self._verify_cmake_preset(tempdir)
2722 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
2723 # Verify the oe-scripts sym-link is valid
2724 self.assertEqual(self._workspace_scripts_dir(
2725 recipe_name), self._sources_scripts_dir(tempdir))
Patrick Williams73bd93f2024-02-20 08:07:48 -06002726
2727 # meson-example recipe
2728 recipe_name = "meson-example"
2729 example_exe = "mesonex"
2730 build_file = "meson.build"
2731 tempdir = self._devtool_ide_sdk_recipe(
2732 recipe_name, build_file, testimage)
2733 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
2734 recipe_name, testimage, qemu.ip)
2735 runCmd(bitbake_sdk_cmd)
Patrick Williams39653562024-03-01 08:54:02 -06002736 self._gdb_cross()
Patrick Williams73bd93f2024-02-20 08:07:48 -06002737 self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
2738 # Verify the oe-scripts sym-link is valid
2739 self.assertEqual(self._workspace_scripts_dir(
2740 recipe_name), self._sources_scripts_dir(tempdir))
Patrick Williams73bd93f2024-02-20 08:07:48 -06002741
2742 def test_devtool_ide_sdk_code_cmake(self):
2743 """Verify a cmake recipe works with ide=code mode"""
2744 recipe_name = "cmake-example"
2745 build_file = "CMakeLists.txt"
2746 testimage = "oe-selftest-image"
2747
2748 self._check_workspace()
2749 self._write_bb_config([recipe_name])
2750 tempdir = self._devtool_ide_sdk_recipe(
2751 recipe_name, build_file, testimage)
2752 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
2753 recipe_name, testimage)
2754 runCmd(bitbake_sdk_cmd)
2755 self._verify_cmake_preset(tempdir)
2756 self._verify_install_script_code(tempdir, recipe_name)
2757 self._gdb_cross()
2758
2759 def test_devtool_ide_sdk_code_meson(self):
2760 """Verify a meson recipe works with ide=code mode"""
2761 recipe_name = "meson-example"
2762 build_file = "meson.build"
2763 testimage = "oe-selftest-image"
2764
2765 self._check_workspace()
2766 self._write_bb_config([recipe_name])
2767 tempdir = self._devtool_ide_sdk_recipe(
2768 recipe_name, build_file, testimage)
2769 bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
2770 recipe_name, testimage)
2771 runCmd(bitbake_sdk_cmd)
2772
2773 with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j:
2774 settings_d = json.load(settings_j)
2775 meson_exe = settings_d["mesonbuild.mesonPath"]
2776 meson_build_folder = settings_d["mesonbuild.buildFolder"]
2777
2778 # Verify the wrapper for meson native is available
2779 self.assertExists(meson_exe)
2780
2781 # Verify meson re-uses the o files compiled by bitbake
2782 result = runCmd('%s compile -C %s' %
2783 (meson_exe, meson_build_folder), cwd=tempdir)
2784 self.assertIn("ninja: no work to do.", result.output)
2785
2786 # Verify the unit tests work (in Qemu)
2787 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
2788
2789 # Verify re-building and testing works again
2790 result = runCmd('%s compile -C %s --clean' %
2791 (meson_exe, meson_build_folder), cwd=tempdir)
2792 self.assertIn("Cleaning...", result.output)
2793 result = runCmd('%s compile -C %s' %
2794 (meson_exe, meson_build_folder), cwd=tempdir)
2795 self.assertIn("Linking target", result.output)
2796 runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir)
2797
2798 self._verify_install_script_code(tempdir, recipe_name)
2799 self._gdb_cross()
2800
2801 def test_devtool_ide_sdk_shared_sysroots(self):
2802 """Verify the shared sysroot SDK"""
2803
2804 # Handle the workspace (which is not needed by this test case)
2805 self._check_workspace()
2806
2807 result_init = runCmd(
2808 'devtool ide-sdk -m shared oe-selftest-image cmake-example meson-example --ide=code')
2809 bb_vars = get_bb_vars(
2810 ['REAL_MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE'], "meta-ide-support")
2811 environment_script = 'environment-setup-%s' % bb_vars['REAL_MULTIMACH_TARGET_SYS']
2812 deploydir = bb_vars['DEPLOY_DIR_IMAGE']
2813 environment_script_path = os.path.join(deploydir, environment_script)
2814 cpp_example_src = os.path.join(
2815 bb_vars['COREBASE'], 'meta-selftest', 'recipes-test', 'cpp', 'files')
2816
2817 # Verify the cross environment script is available
2818 self.assertExists(environment_script_path)
2819
2820 def runCmdEnv(cmd, cwd):
2821 cmd = '/bin/sh -c ". %s > /dev/null && %s"' % (
2822 environment_script_path, cmd)
2823 return runCmd(cmd, cwd)
2824
2825 # Verify building the C++ example works with CMake
2826 tempdir_cmake = tempfile.mkdtemp(prefix='devtoolqa')
2827 self.track_for_cleanup(tempdir_cmake)
2828
2829 result_cmake = runCmdEnv("which cmake", cwd=tempdir_cmake)
2830 cmake_native = os.path.normpath(result_cmake.output.strip())
2831 self.assertExists(cmake_native)
2832
2833 runCmdEnv('cmake %s' % cpp_example_src, cwd=tempdir_cmake)
2834 runCmdEnv('cmake --build %s' % tempdir_cmake, cwd=tempdir_cmake)
2835
2836 # Verify the printed note really referres to a cmake executable
2837 cmake_native_code = ""
2838 for line in result_init.output.splitlines():
2839 m = re.search(r'"cmake.cmakePath": "(.*)"', line)
2840 if m:
2841 cmake_native_code = m.group(1)
2842 break
2843 self.assertExists(cmake_native_code)
2844 self.assertEqual(cmake_native, cmake_native_code)
2845
2846 # Verify building the C++ example works with Meson
2847 tempdir_meson = tempfile.mkdtemp(prefix='devtoolqa')
2848 self.track_for_cleanup(tempdir_meson)
2849
2850 result_cmake = runCmdEnv("which meson", cwd=tempdir_meson)
2851 meson_native = os.path.normpath(result_cmake.output.strip())
2852 self.assertExists(meson_native)
2853
2854 runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src)
2855 runCmdEnv('meson compile', cwd=tempdir_meson)
2856
2857 def test_devtool_ide_sdk_plugins(self):
2858 """Test that devtool ide-sdk can use plugins from other layers."""
2859
2860 # We need a workspace layer and a modified recipe (but no image)
2861 modified_recipe_name = "meson-example"
2862 modified_build_file = "meson.build"
2863 testimage = "oe-selftest-image"
2864 shared_recipe_name = "cmake-example"
2865
2866 self._check_workspace()
2867 self._write_bb_config([modified_recipe_name])
2868 tempdir = self._devtool_ide_sdk_recipe(
2869 modified_recipe_name, modified_build_file, None)
2870
2871 IDE_RE = re.compile(r'.*--ide \{(.*)\}.*')
2872
2873 def get_ides_from_help(help_str):
2874 m = IDE_RE.search(help_str)
2875 return m.group(1).split(',')
2876
2877 # verify the default plugins are available but the foo plugin is not
2878 result = runCmd('devtool ide-sdk -h')
2879 found_ides = get_ides_from_help(result.output)
2880 self.assertIn('code', found_ides)
2881 self.assertIn('none', found_ides)
2882 self.assertNotIn('foo', found_ides)
2883
2884 shared_config_file = os.path.join(tempdir, 'shared-config.txt')
2885 shared_config_str = 'Dummy shared IDE config'
2886 modified_config_file = os.path.join(tempdir, 'modified-config.txt')
2887 modified_config_str = 'Dummy modified IDE config'
2888
2889 # Generate a foo plugin in the workspace layer
2890 plugin_dir = os.path.join(
2891 self.workspacedir, 'lib', 'devtool', 'ide_plugins')
2892 os.makedirs(plugin_dir)
2893 plugin_code = 'from devtool.ide_plugins import IdeBase\n\n'
2894 plugin_code += 'class IdeFoo(IdeBase):\n'
2895 plugin_code += ' def setup_shared_sysroots(self, shared_env):\n'
2896 plugin_code += ' with open("%s", "w") as config_file:\n' % shared_config_file
2897 plugin_code += ' config_file.write("%s")\n\n' % shared_config_str
2898 plugin_code += ' def setup_modified_recipe(self, args, image_recipe, modified_recipe):\n'
2899 plugin_code += ' with open("%s", "w") as config_file:\n' % modified_config_file
2900 plugin_code += ' config_file.write("%s")\n\n' % modified_config_str
2901 plugin_code += 'def register_ide_plugin(ide_plugins):\n'
2902 plugin_code += ' ide_plugins["foo"] = IdeFoo\n'
2903
2904 plugin_py = os.path.join(plugin_dir, 'ide_foo.py')
2905 with open(plugin_py, 'w') as plugin_file:
2906 plugin_file.write(plugin_code)
2907
2908 # Verify the foo plugin is available as well
2909 result = runCmd('devtool ide-sdk -h')
2910 found_ides = get_ides_from_help(result.output)
2911 self.assertIn('code', found_ides)
2912 self.assertIn('none', found_ides)
2913 self.assertIn('foo', found_ides)
2914
2915 # Verify the foo plugin generates a shared config
2916 result = runCmd(
2917 'devtool ide-sdk -m shared --skip-bitbake --ide foo %s' % shared_recipe_name)
2918 with open(shared_config_file) as shared_config:
2919 shared_config_new = shared_config.read()
2920 self.assertEqual(shared_config_str, shared_config_new)
2921
2922 # Verify the foo plugin generates a modified config
2923 result = runCmd('devtool ide-sdk --skip-bitbake --ide foo %s %s' %
2924 (modified_recipe_name, testimage))
2925 with open(modified_config_file) as modified_config:
2926 modified_config_new = modified_config.read()
2927 self.assertEqual(modified_config_str, modified_config_new)