1# 2# Copyright OpenEmbedded Contributors 3# 4# SPDX-License-Identifier: MIT 5# 6 7import errno 8import os 9import shutil 10import tempfile 11import urllib.parse 12 13from oeqa.utils.commands import runCmd, bitbake, get_bb_var 14from oeqa.utils.commands import get_bb_vars, create_temp_layer 15from oeqa.selftest.cases import devtool 16 17templayerdir = None 18 19def setUpModule(): 20 global templayerdir 21 templayerdir = tempfile.mkdtemp(prefix='recipetoolqa') 22 create_temp_layer(templayerdir, 'selftestrecipetool') 23 runCmd('bitbake-layers add-layer %s' % templayerdir) 24 25 26def tearDownModule(): 27 runCmd('bitbake-layers remove-layer %s' % templayerdir, ignore_status=True) 28 runCmd('rm -rf %s' % templayerdir) 29 30 31def needTomllib(test): 32 # This test require python 3.11 or above for the tomllib module or tomli module to be installed 33 try: 34 import tomllib 35 except ImportError: 36 try: 37 import tomli 38 except ImportError: 39 test.skipTest('Test requires python 3.11 or above for tomllib module or tomli module') 40 41class RecipetoolBase(devtool.DevtoolTestCase): 42 43 def setUpLocal(self): 44 super(RecipetoolBase, self).setUpLocal() 45 self.templayerdir = templayerdir 46 self.tempdir = tempfile.mkdtemp(prefix='recipetoolqa') 47 self.track_for_cleanup(self.tempdir) 48 self.testfile = os.path.join(self.tempdir, 'testfile') 49 with open(self.testfile, 'w') as f: 50 f.write('Test file\n') 51 config = 'BBMASK += "meta-poky/recipes-core/base-files/base-files_%.bbappend"\n' 52 self.append_config(config) 53 54 def tearDownLocal(self): 55 runCmd('rm -rf %s/recipes-*' % self.templayerdir) 56 super(RecipetoolBase, self).tearDownLocal() 57 58 def _try_recipetool_appendcmd(self, cmd, testrecipe, expectedfiles, expectedlines=None): 59 result = runCmd(cmd) 60 self.assertNotIn('Traceback', result.output) 61 62 # Check the bbappend was created and applies properly 63 recipefile = get_bb_var('FILE', testrecipe) 64 bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir) 65 66 # Check the bbappend contents 67 if expectedlines is not None: 68 with open(bbappendfile, 'r') as f: 69 self.assertEqual(expectedlines, f.readlines(), "Expected lines are not present in %s" % bbappendfile) 70 71 # Check file was copied 72 filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe) 73 for expectedfile in expectedfiles: 74 self.assertTrue(os.path.isfile(os.path.join(filesdir, expectedfile)), 'Expected file %s to be copied next to bbappend, but it wasn\'t' % expectedfile) 75 76 # Check no other files created 77 createdfiles = [] 78 for root, _, files in os.walk(filesdir): 79 for f in files: 80 createdfiles.append(os.path.relpath(os.path.join(root, f), filesdir)) 81 self.assertTrue(sorted(createdfiles), sorted(expectedfiles)) 82 83 return bbappendfile, result.output 84 85 86class RecipetoolAppendTests(RecipetoolBase): 87 88 @classmethod 89 def setUpClass(cls): 90 super(RecipetoolAppendTests, cls).setUpClass() 91 # Ensure we have the right data in shlibs/pkgdata 92 cls.logger.info('Running bitbake to generate pkgdata') 93 bitbake('-c packagedata base-files coreutils busybox selftest-recipetool-appendfile') 94 bb_vars = get_bb_vars(['COREBASE']) 95 cls.corebase = bb_vars['COREBASE'] 96 97 def _try_recipetool_appendfile(self, testrecipe, destfile, newfile, options, expectedlines, expectedfiles): 98 cmd = 'recipetool appendfile %s %s %s %s' % (self.templayerdir, destfile, newfile, options) 99 return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines) 100 101 def _try_recipetool_appendfile_fail(self, destfile, newfile, checkerror): 102 cmd = 'recipetool appendfile %s %s %s' % (self.templayerdir, destfile, newfile) 103 result = runCmd(cmd, ignore_status=True) 104 self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd) 105 self.assertNotIn('Traceback', result.output) 106 for errorstr in checkerror: 107 self.assertIn(errorstr, result.output) 108 109 def test_recipetool_appendfile_basic(self): 110 # Basic test 111 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 112 '\n'] 113 _, output = self._try_recipetool_appendfile('base-files', '/etc/motd', self.testfile, '', expectedlines, ['motd']) 114 self.assertNotIn('WARNING: ', output) 115 116 def test_recipetool_appendfile_invalid(self): 117 # Test some commands that should error 118 self._try_recipetool_appendfile_fail('/etc/passwd', self.testfile, ['ERROR: /etc/passwd cannot be handled by this tool', 'useradd', 'extrausers']) 119 self._try_recipetool_appendfile_fail('/etc/timestamp', self.testfile, ['ERROR: /etc/timestamp cannot be handled by this tool']) 120 self._try_recipetool_appendfile_fail('/dev/console', self.testfile, ['ERROR: /dev/console cannot be handled by this tool']) 121 122 def test_recipetool_appendfile_alternatives(self): 123 # Now try with a file we know should be an alternative 124 # (this is very much a fake example, but one we know is reliably an alternative) 125 self._try_recipetool_appendfile_fail('/bin/ls', self.testfile, ['ERROR: File /bin/ls is an alternative possibly provided by the following recipes:', 'coreutils', 'busybox']) 126 # Need a test file - should be executable 127 testfile2 = os.path.join(self.corebase, 'oe-init-build-env') 128 testfile2name = os.path.basename(testfile2) 129 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 130 '\n', 131 'SRC_URI += "file://%s"\n' % testfile2name, 132 '\n', 133 'do_install:append() {\n', 134 ' install -d ${D}${base_bindir}\n', 135 ' install -m 0755 ${WORKDIR}/%s ${D}${base_bindir}/ls\n' % testfile2name, 136 '}\n'] 137 self._try_recipetool_appendfile('coreutils', '/bin/ls', testfile2, '-r coreutils', expectedlines, [testfile2name]) 138 # Now try bbappending the same file again, contents should not change 139 bbappendfile, _ = self._try_recipetool_appendfile('coreutils', '/bin/ls', self.testfile, '-r coreutils', expectedlines, [testfile2name]) 140 # But file should have 141 copiedfile = os.path.join(os.path.dirname(bbappendfile), 'coreutils', testfile2name) 142 result = runCmd('diff -q %s %s' % (testfile2, copiedfile), ignore_status=True) 143 self.assertNotEqual(result.status, 0, 'New file should have been copied but was not %s' % result.output) 144 145 def test_recipetool_appendfile_binary(self): 146 # Try appending a binary file 147 # /bin/ls can be a symlink to /usr/bin/ls 148 ls = os.path.realpath("/bin/ls") 149 result = runCmd('recipetool appendfile %s /bin/ls %s -r coreutils' % (self.templayerdir, ls)) 150 self.assertIn('WARNING: ', result.output) 151 self.assertIn('is a binary', result.output) 152 153 def test_recipetool_appendfile_add(self): 154 # Try arbitrary file add to a recipe 155 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 156 '\n', 157 'SRC_URI += "file://testfile"\n', 158 '\n', 159 'do_install:append() {\n', 160 ' install -d ${D}${datadir}\n', 161 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', 162 '}\n'] 163 self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase', expectedlines, ['testfile']) 164 # Try adding another file, this time where the source file is executable 165 # (so we're testing that, plus modifying an existing bbappend) 166 testfile2 = os.path.join(self.corebase, 'oe-init-build-env') 167 testfile2name = os.path.basename(testfile2) 168 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 169 '\n', 170 'SRC_URI += "file://testfile \\\n', 171 ' file://%s \\\n' % testfile2name, 172 ' "\n', 173 '\n', 174 'do_install:append() {\n', 175 ' install -d ${D}${datadir}\n', 176 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', 177 ' install -m 0755 ${WORKDIR}/%s ${D}${datadir}/scriptname\n' % testfile2name, 178 '}\n'] 179 self._try_recipetool_appendfile('netbase', '/usr/share/scriptname', testfile2, '-r netbase', expectedlines, ['testfile', testfile2name]) 180 181 def test_recipetool_appendfile_add_bindir(self): 182 # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable 183 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 184 '\n', 185 'SRC_URI += "file://testfile"\n', 186 '\n', 187 'do_install:append() {\n', 188 ' install -d ${D}${bindir}\n', 189 ' install -m 0755 ${WORKDIR}/testfile ${D}${bindir}/selftest-recipetool-testbin\n', 190 '}\n'] 191 _, output = self._try_recipetool_appendfile('netbase', '/usr/bin/selftest-recipetool-testbin', self.testfile, '-r netbase', expectedlines, ['testfile']) 192 self.assertNotIn('WARNING: ', output) 193 194 def test_recipetool_appendfile_add_machine(self): 195 # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable 196 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 197 '\n', 198 'PACKAGE_ARCH = "${MACHINE_ARCH}"\n', 199 '\n', 200 'SRC_URI:append:mymachine = " file://testfile"\n', 201 '\n', 202 'do_install:append:mymachine() {\n', 203 ' install -d ${D}${datadir}\n', 204 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', 205 '}\n'] 206 _, output = self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase -m mymachine', expectedlines, ['mymachine/testfile']) 207 self.assertNotIn('WARNING: ', output) 208 209 def test_recipetool_appendfile_orig(self): 210 # A file that's in SRC_URI and in do_install with the same name 211 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 212 '\n'] 213 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-orig', self.testfile, '', expectedlines, ['selftest-replaceme-orig']) 214 self.assertNotIn('WARNING: ', output) 215 216 def test_recipetool_appendfile_todir(self): 217 # A file that's in SRC_URI and in do_install with destination directory rather than file 218 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 219 '\n'] 220 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-todir', self.testfile, '', expectedlines, ['selftest-replaceme-todir']) 221 self.assertNotIn('WARNING: ', output) 222 223 def test_recipetool_appendfile_renamed(self): 224 # A file that's in SRC_URI with a different name to the destination file 225 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 226 '\n'] 227 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-renamed', self.testfile, '', expectedlines, ['file1']) 228 self.assertNotIn('WARNING: ', output) 229 230 def test_recipetool_appendfile_subdir(self): 231 # A file that's in SRC_URI in a subdir 232 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 233 '\n', 234 'SRC_URI += "file://testfile"\n', 235 '\n', 236 'do_install:append() {\n', 237 ' install -d ${D}${datadir}\n', 238 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-subdir\n', 239 '}\n'] 240 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-subdir', self.testfile, '', expectedlines, ['testfile']) 241 self.assertNotIn('WARNING: ', output) 242 243 def test_recipetool_appendfile_inst_glob(self): 244 # A file that's in do_install as a glob 245 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 246 '\n'] 247 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-globfile']) 248 self.assertNotIn('WARNING: ', output) 249 250 def test_recipetool_appendfile_inst_todir_glob(self): 251 # A file that's in do_install as a glob with destination as a directory 252 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 253 '\n'] 254 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-todir-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-todir-globfile']) 255 self.assertNotIn('WARNING: ', output) 256 257 def test_recipetool_appendfile_patch(self): 258 # A file that's added by a patch in SRC_URI 259 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 260 '\n', 261 'SRC_URI += "file://testfile"\n', 262 '\n', 263 'do_install:append() {\n', 264 ' install -d ${D}${sysconfdir}\n', 265 ' install -m 0644 ${WORKDIR}/testfile ${D}${sysconfdir}/selftest-replaceme-patched\n', 266 '}\n'] 267 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/etc/selftest-replaceme-patched', self.testfile, '', expectedlines, ['testfile']) 268 for line in output.splitlines(): 269 if 'WARNING: ' in line: 270 self.assertIn('add-file.patch', line, 'Unexpected warning found in output:\n%s' % line) 271 break 272 else: 273 self.fail('Patch warning not found in output:\n%s' % output) 274 275 def test_recipetool_appendfile_script(self): 276 # Now, a file that's in SRC_URI but installed by a script (so no mention in do_install) 277 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 278 '\n', 279 'SRC_URI += "file://testfile"\n', 280 '\n', 281 'do_install:append() {\n', 282 ' install -d ${D}${datadir}\n', 283 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-scripted\n', 284 '}\n'] 285 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-scripted', self.testfile, '', expectedlines, ['testfile']) 286 self.assertNotIn('WARNING: ', output) 287 288 def test_recipetool_appendfile_inst_func(self): 289 # A file that's installed from a function called by do_install 290 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 291 '\n'] 292 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-func', self.testfile, '', expectedlines, ['selftest-replaceme-inst-func']) 293 self.assertNotIn('WARNING: ', output) 294 295 def test_recipetool_appendfile_postinstall(self): 296 # A file that's created by a postinstall script (and explicitly mentioned in it) 297 # First try without specifying recipe 298 self._try_recipetool_appendfile_fail('/usr/share/selftest-replaceme-postinst', self.testfile, ['File /usr/share/selftest-replaceme-postinst may be written out in a pre/postinstall script of the following recipes:', 'selftest-recipetool-appendfile']) 299 # Now specify recipe 300 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 301 '\n', 302 'SRC_URI += "file://testfile"\n', 303 '\n', 304 'do_install:append() {\n', 305 ' install -d ${D}${datadir}\n', 306 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-postinst\n', 307 '}\n'] 308 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-postinst', self.testfile, '-r selftest-recipetool-appendfile', expectedlines, ['testfile']) 309 310 def test_recipetool_appendfile_extlayer(self): 311 # Try creating a bbappend in a layer that's not in bblayers.conf and has a different structure 312 exttemplayerdir = os.path.join(self.tempdir, 'extlayer') 313 self._create_temp_layer(exttemplayerdir, False, 'oeselftestextlayer', recipepathspec='metadata/recipes/recipes-*/*') 314 result = runCmd('recipetool appendfile %s /usr/share/selftest-replaceme-orig %s' % (exttemplayerdir, self.testfile)) 315 self.assertNotIn('Traceback', result.output) 316 createdfiles = [] 317 for root, _, files in os.walk(exttemplayerdir): 318 for f in files: 319 createdfiles.append(os.path.relpath(os.path.join(root, f), exttemplayerdir)) 320 createdfiles.remove('conf/layer.conf') 321 expectedfiles = ['metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile.bbappend', 322 'metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile/selftest-replaceme-orig'] 323 self.assertEqual(sorted(createdfiles), sorted(expectedfiles)) 324 325 def test_recipetool_appendfile_wildcard(self): 326 327 def try_appendfile_wc(options): 328 result = runCmd('recipetool appendfile %s /etc/profile %s %s' % (self.templayerdir, self.testfile, options)) 329 self.assertNotIn('Traceback', result.output) 330 bbappendfile = None 331 for root, _, files in os.walk(self.templayerdir): 332 for f in files: 333 if f.endswith('.bbappend'): 334 bbappendfile = f 335 break 336 if not bbappendfile: 337 self.fail('No bbappend file created') 338 runCmd('rm -rf %s/recipes-*' % self.templayerdir) 339 return bbappendfile 340 341 # Check without wildcard option 342 recipefn = os.path.basename(get_bb_var('FILE', 'base-files')) 343 filename = try_appendfile_wc('') 344 self.assertEqual(filename, recipefn.replace('.bb', '.bbappend')) 345 # Now check with wildcard option 346 filename = try_appendfile_wc('-w') 347 self.assertEqual(filename, recipefn.split('_')[0] + '_%.bbappend') 348 349 350class RecipetoolCreateTests(RecipetoolBase): 351 352 def test_recipetool_create(self): 353 # Try adding a recipe 354 tempsrc = os.path.join(self.tempdir, 'srctree') 355 os.makedirs(tempsrc) 356 recipefile = os.path.join(self.tempdir, 'logrotate_3.12.3.bb') 357 srcuri = 'https://github.com/logrotate/logrotate/releases/download/3.12.3/logrotate-3.12.3.tar.xz' 358 result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc)) 359 self.assertTrue(os.path.isfile(recipefile)) 360 checkvars = {} 361 checkvars['LICENSE'] = 'GPL-2.0-only' 362 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' 363 checkvars['SRC_URI'] = 'https://github.com/logrotate/logrotate/releases/download/${PV}/logrotate-${PV}.tar.xz' 364 checkvars['SRC_URI[sha256sum]'] = '2e6a401cac9024db2288297e3be1a8ab60e7401ba8e91225218aaf4a27e82a07' 365 self._test_recipe_contents(recipefile, checkvars, []) 366 367 def test_recipetool_create_autotools(self): 368 if 'x11' not in get_bb_var('DISTRO_FEATURES'): 369 self.skipTest('Test requires x11 as distro feature') 370 # Ensure we have the right data in shlibs/pkgdata 371 bitbake('libpng pango libx11 libxext jpeg libcheck') 372 # Try adding a recipe 373 tempsrc = os.path.join(self.tempdir, 'srctree') 374 os.makedirs(tempsrc) 375 recipefile = os.path.join(self.tempdir, 'libmatchbox.bb') 376 srcuri = 'git://git.yoctoproject.org/libmatchbox;protocol=https' 377 result = runCmd(['recipetool', 'create', '-o', recipefile, srcuri + ";rev=9f7cf8895ae2d39c465c04cc78e918c157420269", '-x', tempsrc]) 378 self.assertTrue(os.path.isfile(recipefile), 'recipetool did not create recipe file; output:\n%s' % result.output) 379 checkvars = {} 380 checkvars['LICENSE'] = 'LGPL-2.1-only' 381 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34' 382 checkvars['S'] = '${WORKDIR}/git' 383 checkvars['PV'] = '1.11+git' 384 checkvars['SRC_URI'] = srcuri + ';branch=master' 385 checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxext', 'pango']) 386 inherits = ['autotools', 'pkgconfig'] 387 self._test_recipe_contents(recipefile, checkvars, inherits) 388 389 def test_recipetool_create_simple(self): 390 # Try adding a recipe 391 temprecipe = os.path.join(self.tempdir, 'recipe') 392 os.makedirs(temprecipe) 393 pv = '1.7.4.1' 394 srcuri = 'http://www.dest-unreach.org/socat/download/Archive/socat-%s.tar.bz2' % pv 395 result = runCmd('recipetool create %s -o %s' % (srcuri, temprecipe)) 396 dirlist = os.listdir(temprecipe) 397 if len(dirlist) > 1: 398 self.fail('recipetool created more than just one file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist))) 399 if len(dirlist) < 1 or not os.path.isfile(os.path.join(temprecipe, dirlist[0])): 400 self.fail('recipetool did not create recipe file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist))) 401 self.assertEqual(dirlist[0], 'socat_%s.bb' % pv, 'Recipe file incorrectly named') 402 checkvars = {} 403 checkvars['LICENSE'] = set(['Unknown', 'GPL-2.0-only']) 404 checkvars['LIC_FILES_CHKSUM'] = set(['file://COPYING.OpenSSL;md5=5c9bccc77f67a8328ef4ebaf468116f4', 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263']) 405 # We don't check DEPENDS since they are variable for this recipe depending on what's in the sysroot 406 checkvars['S'] = None 407 checkvars['SRC_URI'] = srcuri.replace(pv, '${PV}') 408 inherits = ['autotools'] 409 self._test_recipe_contents(os.path.join(temprecipe, dirlist[0]), checkvars, inherits) 410 411 def test_recipetool_create_cmake(self): 412 temprecipe = os.path.join(self.tempdir, 'recipe') 413 os.makedirs(temprecipe) 414 recipefile = os.path.join(temprecipe, 'taglib_1.11.1.bb') 415 srcuri = 'http://taglib.github.io/releases/taglib-1.11.1.tar.gz' 416 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 417 self.assertTrue(os.path.isfile(recipefile)) 418 checkvars = {} 419 checkvars['LICENSE'] = set(['LGPL-2.1-only', 'MPL-1.1-only']) 420 checkvars['SRC_URI'] = 'http://taglib.github.io/releases/taglib-${PV}.tar.gz' 421 checkvars['SRC_URI[sha256sum]'] = 'b6d1a5a610aae6ff39d93de5efd0fdc787aa9e9dc1e7026fa4c961b26563526b' 422 checkvars['DEPENDS'] = set(['boost', 'zlib']) 423 inherits = ['cmake'] 424 self._test_recipe_contents(recipefile, checkvars, inherits) 425 426 def test_recipetool_create_npm(self): 427 collections = get_bb_var('BBFILE_COLLECTIONS').split() 428 if "openembedded-layer" not in collections: 429 self.skipTest("Test needs meta-oe for nodejs") 430 431 temprecipe = os.path.join(self.tempdir, 'recipe') 432 os.makedirs(temprecipe) 433 recipefile = os.path.join(temprecipe, 'savoirfairelinux-node-server-example_1.0.0.bb') 434 shrinkwrap = os.path.join(temprecipe, 'savoirfairelinux-node-server-example', 'npm-shrinkwrap.json') 435 srcuri = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 436 result = runCmd('recipetool create -o %s \'%s\'' % (temprecipe, srcuri)) 437 self.assertTrue(os.path.isfile(recipefile)) 438 self.assertTrue(os.path.isfile(shrinkwrap)) 439 checkvars = {} 440 checkvars['SUMMARY'] = 'Node Server Example' 441 checkvars['HOMEPAGE'] = 'https://github.com/savoirfairelinux/node-server-example#readme' 442 checkvars['LICENSE'] = 'BSD-3-Clause & ISC & MIT & Unknown' 443 urls = [] 444 urls.append('npm://registry.npmjs.org/;package=@savoirfairelinux/node-server-example;version=${PV}') 445 urls.append('npmsw://${THISDIR}/${BPN}/npm-shrinkwrap.json') 446 checkvars['SRC_URI'] = set(urls) 447 checkvars['S'] = '${WORKDIR}/npm' 448 checkvars['LICENSE:${PN}'] = 'MIT' 449 checkvars['LICENSE:${PN}-base64'] = 'Unknown' 450 checkvars['LICENSE:${PN}-accepts'] = 'MIT' 451 checkvars['LICENSE:${PN}-inherits'] = 'ISC' 452 inherits = ['npm'] 453 self._test_recipe_contents(recipefile, checkvars, inherits) 454 455 def test_recipetool_create_github(self): 456 # Basic test to see if github URL mangling works. Deliberately use an 457 # older release of Meson at present so we don't need a toml parser. 458 temprecipe = os.path.join(self.tempdir, 'recipe') 459 os.makedirs(temprecipe) 460 recipefile = os.path.join(temprecipe, 'python3-meson_git.bb') 461 srcuri = 'https://github.com/mesonbuild/meson;rev=0.52.1' 462 cmd = ['recipetool', 'create', '-o', temprecipe, srcuri] 463 result = runCmd(cmd) 464 self.assertTrue(os.path.isfile(recipefile), msg="recipe %s not created for command %s, output %s" % (recipefile, " ".join(cmd), result.output)) 465 checkvars = {} 466 checkvars['LICENSE'] = set(['Apache-2.0', "Unknown"]) 467 checkvars['SRC_URI'] = 'git://github.com/mesonbuild/meson;protocol=https;branch=0.52' 468 inherits = ['setuptools3'] 469 self._test_recipe_contents(recipefile, checkvars, inherits) 470 471 def test_recipetool_create_python3_setuptools(self): 472 # Test creating python3 package from tarball (using setuptools3 class) 473 # Use the --no-pypi switch to avoid creating a pypi enabled recipe and 474 # and check the created recipe as if it was a more general tarball 475 temprecipe = os.path.join(self.tempdir, 'recipe') 476 os.makedirs(temprecipe) 477 pn = 'python-magic' 478 pv = '0.4.15' 479 recipefile = os.path.join(temprecipe, '%s_%s.bb' % (pn, pv)) 480 srcuri = 'https://files.pythonhosted.org/packages/84/30/80932401906eaf787f2e9bd86dc458f1d2e75b064b4c187341f29516945c/python-magic-%s.tar.gz' % pv 481 result = runCmd('recipetool create --no-pypi -o %s %s' % (temprecipe, srcuri)) 482 self.assertTrue(os.path.isfile(recipefile)) 483 checkvars = {} 484 checkvars['LICENSE'] = set(['MIT']) 485 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=16a934f165e8c3245f241e77d401bb88' 486 checkvars['SRC_URI'] = 'https://files.pythonhosted.org/packages/84/30/80932401906eaf787f2e9bd86dc458f1d2e75b064b4c187341f29516945c/python-magic-${PV}.tar.gz' 487 checkvars['SRC_URI[sha256sum]'] = 'f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5' 488 inherits = ['setuptools3'] 489 self._test_recipe_contents(recipefile, checkvars, inherits) 490 491 def test_recipetool_create_python3_setuptools_pypi_tarball(self): 492 # Test creating python3 package from tarball (using setuptools3 and pypi classes) 493 temprecipe = os.path.join(self.tempdir, 'recipe') 494 os.makedirs(temprecipe) 495 pn = 'python-magic' 496 pv = '0.4.15' 497 recipefile = os.path.join(temprecipe, '%s_%s.bb' % (pn, pv)) 498 srcuri = 'https://files.pythonhosted.org/packages/84/30/80932401906eaf787f2e9bd86dc458f1d2e75b064b4c187341f29516945c/python-magic-%s.tar.gz' % pv 499 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 500 self.assertTrue(os.path.isfile(recipefile)) 501 checkvars = {} 502 checkvars['LICENSE'] = set(['MIT']) 503 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=16a934f165e8c3245f241e77d401bb88' 504 checkvars['SRC_URI[sha256sum]'] = 'f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5' 505 checkvars['PYPI_PACKAGE'] = pn 506 inherits = ['setuptools3', 'pypi'] 507 self._test_recipe_contents(recipefile, checkvars, inherits) 508 509 def test_recipetool_create_python3_setuptools_pypi(self): 510 # Test creating python3 package from pypi url (using setuptools3 and pypi classes) 511 # Intentionnaly using setuptools3 class here instead of any of the pep517 class 512 # to avoid the toml dependency and allows this test to run on host autobuilders 513 # with older version of python 514 temprecipe = os.path.join(self.tempdir, 'recipe') 515 os.makedirs(temprecipe) 516 pn = 'python-magic' 517 pv = '0.4.15' 518 recipefile = os.path.join(temprecipe, '%s_%s.bb' % (pn, pv)) 519 # First specify the required version in the url 520 srcuri = 'https://pypi.org/project/%s/%s' % (pn, pv) 521 runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 522 self.assertTrue(os.path.isfile(recipefile)) 523 checkvars = {} 524 checkvars['LICENSE'] = set(['MIT']) 525 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=16a934f165e8c3245f241e77d401bb88' 526 checkvars['SRC_URI[sha256sum]'] = 'f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5' 527 checkvars['PYPI_PACKAGE'] = pn 528 inherits = ['setuptools3', "pypi"] 529 self._test_recipe_contents(recipefile, checkvars, inherits) 530 531 # Now specify the version as a recipetool parameter 532 runCmd('rm -rf %s' % recipefile) 533 self.assertFalse(os.path.isfile(recipefile)) 534 srcuri = 'https://pypi.org/project/%s' % pn 535 runCmd('recipetool create -o %s %s --version %s' % (temprecipe, srcuri, pv)) 536 self.assertTrue(os.path.isfile(recipefile)) 537 checkvars = {} 538 checkvars['LICENSE'] = set(['MIT']) 539 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=16a934f165e8c3245f241e77d401bb88' 540 checkvars['SRC_URI[sha256sum]'] = 'f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5' 541 checkvars['PYPI_PACKAGE'] = pn 542 inherits = ['setuptools3', "pypi"] 543 self._test_recipe_contents(recipefile, checkvars, inherits) 544 545 # Now, try to grab latest version of the package, so we cannot guess the name of the recipe, 546 # unless hardcoding the latest version but it means we will need to update the test for each release, 547 # so use a regexp 548 runCmd('rm -rf %s' % recipefile) 549 self.assertFalse(os.path.isfile(recipefile)) 550 recipefile_re = r'%s_(.*)\.bb' % pn 551 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 552 dirlist = os.listdir(temprecipe) 553 if len(dirlist) > 1: 554 self.fail('recipetool created more than just one file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist))) 555 if len(dirlist) < 1 or not os.path.isfile(os.path.join(temprecipe, dirlist[0])): 556 self.fail('recipetool did not create recipe file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist))) 557 import re 558 match = re.match(recipefile_re, dirlist[0]) 559 self.assertTrue(match) 560 latest_pv = match.group(1) 561 self.assertTrue(latest_pv != pv) 562 recipefile = os.path.join(temprecipe, '%s_%s.bb' % (pn, latest_pv)) 563 # Do not check LIC_FILES_CHKSUM and SRC_URI checksum here to avoid having updating the test on each release 564 checkvars = {} 565 checkvars['LICENSE'] = set(['MIT']) 566 checkvars['PYPI_PACKAGE'] = pn 567 inherits = ['setuptools3', "pypi"] 568 self._test_recipe_contents(recipefile, checkvars, inherits) 569 570 def test_recipetool_create_python3_pep517_setuptools_build_meta(self): 571 # This test require python 3.11 or above for the tomllib module or tomli module to be installed 572 needTomllib(self) 573 574 # Test creating python3 package from tarball (using setuptools.build_meta class) 575 temprecipe = os.path.join(self.tempdir, 'recipe') 576 os.makedirs(temprecipe) 577 pn = 'webcolors' 578 pv = '1.13' 579 recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv)) 580 srcuri = 'https://files.pythonhosted.org/packages/a1/fb/f95560c6a5d4469d9c49e24cf1b5d4d21ffab5608251c6020a965fb7791c/%s-%s.tar.gz' % (pn, pv) 581 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 582 self.assertTrue(os.path.isfile(recipefile)) 583 checkvars = {} 584 checkvars['SUMMARY'] = 'A library for working with the color formats defined by HTML and CSS.' 585 checkvars['LICENSE'] = set(['BSD-3-Clause']) 586 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=702b1ef12cf66832a88f24c8f2ee9c19' 587 checkvars['SRC_URI[sha256sum]'] = 'c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a' 588 inherits = ['python_setuptools_build_meta', 'pypi'] 589 590 self._test_recipe_contents(recipefile, checkvars, inherits) 591 592 def test_recipetool_create_python3_pep517_poetry_core_masonry_api(self): 593 # This test require python 3.11 or above for the tomllib module or tomli module to be installed 594 needTomllib(self) 595 596 # Test creating python3 package from tarball (using poetry.core.masonry.api class) 597 temprecipe = os.path.join(self.tempdir, 'recipe') 598 os.makedirs(temprecipe) 599 pn = 'iso8601' 600 pv = '2.1.0' 601 recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv)) 602 srcuri = 'https://files.pythonhosted.org/packages/b9/f3/ef59cee614d5e0accf6fd0cbba025b93b272e626ca89fb70a3e9187c5d15/%s-%s.tar.gz' % (pn, pv) 603 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 604 self.assertTrue(os.path.isfile(recipefile)) 605 checkvars = {} 606 checkvars['SUMMARY'] = 'Simple module to parse ISO 8601 dates' 607 checkvars['LICENSE'] = set(['MIT']) 608 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=aab31f2ef7ba214a5a341eaa47a7f367' 609 checkvars['SRC_URI[sha256sum]'] = '6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df' 610 inherits = ['python_poetry_core', 'pypi'] 611 612 self._test_recipe_contents(recipefile, checkvars, inherits) 613 614 def test_recipetool_create_python3_pep517_flit_core_buildapi(self): 615 # This test require python 3.11 or above for the tomllib module or tomli module to be installed 616 needTomllib(self) 617 618 # Test creating python3 package from tarball (using flit_core.buildapi class) 619 temprecipe = os.path.join(self.tempdir, 'recipe') 620 os.makedirs(temprecipe) 621 pn = 'typing-extensions' 622 pv = '4.8.0' 623 recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv)) 624 srcuri = 'https://files.pythonhosted.org/packages/1f/7a/8b94bb016069caa12fc9f587b28080ac33b4fbb8ca369b98bc0a4828543e/typing_extensions-%s.tar.gz' % pv 625 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 626 self.assertTrue(os.path.isfile(recipefile)) 627 checkvars = {} 628 checkvars['SUMMARY'] = 'Backported and Experimental Type Hints for Python 3.8+' 629 checkvars['LICENSE'] = set(['PSF-2.0']) 630 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=fcf6b249c2641540219a727f35d8d2c2' 631 checkvars['SRC_URI[sha256sum]'] = 'df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef' 632 inherits = ['python_flit_core', 'pypi'] 633 634 self._test_recipe_contents(recipefile, checkvars, inherits) 635 636 def test_recipetool_create_python3_pep517_hatchling(self): 637 # This test require python 3.11 or above for the tomllib module or tomli module to be installed 638 needTomllib(self) 639 640 # Test creating python3 package from tarball (using hatchling class) 641 temprecipe = os.path.join(self.tempdir, 'recipe') 642 os.makedirs(temprecipe) 643 pn = 'jsonschema' 644 pv = '4.19.1' 645 recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv)) 646 srcuri = 'https://files.pythonhosted.org/packages/e4/43/087b24516db11722c8687e0caf0f66c7785c0b1c51b0ab951dfde924e3f5/jsonschema-%s.tar.gz' % pv 647 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 648 self.assertTrue(os.path.isfile(recipefile)) 649 checkvars = {} 650 checkvars['SUMMARY'] = 'An implementation of JSON Schema validation for Python' 651 checkvars['HOMEPAGE'] = 'https://github.com/python-jsonschema/jsonschema' 652 checkvars['LICENSE'] = set(['MIT']) 653 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7a60a81c146ec25599a3e1dabb8610a8 file://json/LICENSE;md5=9d4de43111d33570c8fe49b4cb0e01af' 654 checkvars['SRC_URI[sha256sum]'] = 'ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf' 655 inherits = ['python_hatchling', 'pypi'] 656 657 self._test_recipe_contents(recipefile, checkvars, inherits) 658 659 def test_recipetool_create_python3_pep517_maturin(self): 660 # This test require python 3.11 or above for the tomllib module or tomli module to be installed 661 needTomllib(self) 662 663 # Test creating python3 package from tarball (using maturin class) 664 temprecipe = os.path.join(self.tempdir, 'recipe') 665 os.makedirs(temprecipe) 666 pn = 'pydantic-core' 667 pv = '2.14.5' 668 recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv)) 669 srcuri = 'https://files.pythonhosted.org/packages/64/26/cffb93fe9c6b5a91c497f37fae14a4b073ecbc47fc36a9979c7aa888b245/pydantic_core-%s.tar.gz' % pv 670 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 671 self.assertTrue(os.path.isfile(recipefile)) 672 checkvars = {} 673 checkvars['HOMEPAGE'] = 'https://github.com/pydantic/pydantic-core' 674 checkvars['LICENSE'] = set(['MIT']) 675 checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=ab599c188b4a314d2856b3a55030c75c' 676 checkvars['SRC_URI[sha256sum]'] = '6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71' 677 inherits = ['python_maturin', 'pypi'] 678 679 self._test_recipe_contents(recipefile, checkvars, inherits) 680 681 def test_recipetool_create_python3_pep517_mesonpy(self): 682 # This test require python 3.11 or above for the tomllib module or tomli module to be installed 683 needTomllib(self) 684 685 # Test creating python3 package from tarball (using mesonpy class) 686 temprecipe = os.path.join(self.tempdir, 'recipe') 687 os.makedirs(temprecipe) 688 pn = 'siphash24' 689 pv = '1.4' 690 recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv)) 691 srcuri = 'https://files.pythonhosted.org/packages/c2/32/b934a70592f314afcfa86c7f7e388804a8061be65b822e2aa07e573b6477/%s-%s.tar.gz' % (pn, pv) 692 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 693 self.assertTrue(os.path.isfile(recipefile)) 694 checkvars = {} 695 checkvars['SRC_URI[sha256sum]'] = '7fd65e39b2a7c8c4ddc3a168a687f4610751b0ac2ebb518783c0cdfc30bec4a0' 696 inherits = ['python_mesonpy', 'pypi'] 697 698 self._test_recipe_contents(recipefile, checkvars, inherits) 699 700 def test_recipetool_create_github_tarball(self): 701 # Basic test to ensure github URL mangling doesn't apply to release tarballs. 702 # Deliberately use an older release of Meson at present so we don't need a toml parser. 703 temprecipe = os.path.join(self.tempdir, 'recipe') 704 os.makedirs(temprecipe) 705 pv = '0.52.1' 706 recipefile = os.path.join(temprecipe, 'python3-meson_%s.bb' % pv) 707 srcuri = 'https://github.com/mesonbuild/meson/releases/download/%s/meson-%s.tar.gz' % (pv, pv) 708 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 709 self.assertTrue(os.path.isfile(recipefile)) 710 checkvars = {} 711 checkvars['LICENSE'] = set(['Apache-2.0']) 712 checkvars['SRC_URI'] = 'https://github.com/mesonbuild/meson/releases/download/${PV}/meson-${PV}.tar.gz' 713 inherits = ['setuptools3'] 714 self._test_recipe_contents(recipefile, checkvars, inherits) 715 716 def _test_recipetool_create_git(self, srcuri, branch=None): 717 # Basic test to check http git URL mangling works 718 temprecipe = os.path.join(self.tempdir, 'recipe') 719 os.makedirs(temprecipe) 720 name = srcuri.split(';')[0].split('/')[-1] 721 recipefile = os.path.join(temprecipe, name + '_git.bb') 722 options = ' -B %s' % branch if branch else '' 723 result = runCmd('recipetool create -o %s%s "%s"' % (temprecipe, options, srcuri)) 724 self.assertTrue(os.path.isfile(recipefile)) 725 checkvars = {} 726 checkvars['SRC_URI'] = srcuri 727 for scheme in ['http', 'https']: 728 if srcuri.startswith(scheme + ":"): 729 checkvars['SRC_URI'] = 'git%s;protocol=%s' % (srcuri[len(scheme):], scheme) 730 if ';branch=' not in srcuri: 731 checkvars['SRC_URI'] += ';branch=' + (branch or 'master') 732 self._test_recipe_contents(recipefile, checkvars, []) 733 734 def test_recipetool_create_git_http(self): 735 self._test_recipetool_create_git('http://git.yoctoproject.org/git/matchbox-keyboard') 736 737 def test_recipetool_create_git_srcuri_master(self): 738 self._test_recipetool_create_git('git://git.yoctoproject.org/matchbox-keyboard;branch=master;protocol=https') 739 740 def test_recipetool_create_git_srcuri_branch(self): 741 self._test_recipetool_create_git('git://git.yoctoproject.org/matchbox-keyboard;branch=matchbox-keyboard-0-1;protocol=https') 742 743 def test_recipetool_create_git_srcbranch(self): 744 self._test_recipetool_create_git('git://git.yoctoproject.org/matchbox-keyboard;protocol=https', 'matchbox-keyboard-0-1') 745 746 def _go_urifiy(self, url, version, modulepath = None, pathmajor = None, subdir = None): 747 modulepath = ",path='%s'" % modulepath if len(modulepath) else '' 748 pathmajor = ",pathmajor='%s'" % pathmajor if len(pathmajor) else '' 749 subdir = ",subdir='%s'" % subdir if len(subdir) else '' 750 return "${@go_src_uri('%s','%s'%s%s%s)}" % (url, version, modulepath, pathmajor, subdir) 751 752 def test_recipetool_create_go(self): 753 # Basic test to check go recipe generation 754 temprecipe = os.path.join(self.tempdir, 'recipe') 755 os.makedirs(temprecipe) 756 757 recipefile = os.path.join(temprecipe, 'edgex-go_git.bb') 758 deps_require_file = os.path.join(temprecipe, 'edgex-go', 'edgex-go-modules.inc') 759 lics_require_file = os.path.join(temprecipe, 'edgex-go', 'edgex-go-licenses.inc') 760 modules_txt_file = os.path.join(temprecipe, 'edgex-go', 'modules.txt') 761 762 srcuri = 'https://github.com/edgexfoundry/edgex-go.git' 763 srcrev = "v3.0.0" 764 srcbranch = "main" 765 766 result = runCmd('recipetool create -o %s %s -S %s -B %s' % (temprecipe, srcuri, srcrev, srcbranch)) 767 768 self.maxDiff = None 769 inherits = ['go-vendor'] 770 771 checkvars = {} 772 checkvars['GO_IMPORT'] = "github.com/edgexfoundry/edgex-go" 773 checkvars['SRC_URI'] = {'git://${GO_IMPORT};destsuffix=git/src/${GO_IMPORT};nobranch=1;name=${BPN};protocol=https', 774 'file://modules.txt'} 775 checkvars['LIC_FILES_CHKSUM'] = {'file://src/${GO_IMPORT}/LICENSE;md5=8f8bc924cf73f6a32381e5fd4c58d603'} 776 777 self.assertTrue(os.path.isfile(recipefile)) 778 self._test_recipe_contents(recipefile, checkvars, inherits) 779 780 checkvars = {} 781 checkvars['VENDORED_LIC_FILES_CHKSUM'] = set( 782 ['file://src/${GO_IMPORT}/vendor/github.com/Microsoft/go-winio/LICENSE;md5=69205ff73858f2c22b2ca135b557e8ef', 783 'file://src/${GO_IMPORT}/vendor/github.com/armon/go-metrics/LICENSE;md5=d2d77030c0183e3d1e66d26dc1f243be', 784 'file://src/${GO_IMPORT}/vendor/github.com/cenkalti/backoff/LICENSE;md5=1571d94433e3f3aa05267efd4dbea68b', 785 'file://src/${GO_IMPORT}/vendor/github.com/davecgh/go-spew/LICENSE;md5=c06795ed54b2a35ebeeb543cd3a73e56', 786 'file://src/${GO_IMPORT}/vendor/github.com/eclipse/paho.mqtt.golang/LICENSE;md5=dcdb33474b60c38efd27356d8f2edec7', 787 'file://src/${GO_IMPORT}/vendor/github.com/eclipse/paho.mqtt.golang/edl-v10;md5=3adfcc70f5aeb7a44f3f9b495aa1fbf3', 788 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-bootstrap/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', 789 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-configuration/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', 790 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-core-contracts/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', 791 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-messaging/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', 792 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-registry/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', 793 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-secrets/v3/LICENSE;md5=f9fa2f4f8e0ef8cc7b5dd150963eb457', 794 'file://src/${GO_IMPORT}/vendor/github.com/fatih/color/LICENSE.md;md5=316e6d590bdcde7993fb175662c0dd5a', 795 'file://src/${GO_IMPORT}/vendor/github.com/fxamacker/cbor/v2/LICENSE;md5=827f5a2fa861382d35a3943adf9ebb86', 796 'file://src/${GO_IMPORT}/vendor/github.com/go-jose/go-jose/v3/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57', 797 'file://src/${GO_IMPORT}/vendor/github.com/go-jose/go-jose/v3/json/LICENSE;md5=591778525c869cdde0ab5a1bf283cd81', 798 'file://src/${GO_IMPORT}/vendor/github.com/go-kit/log/LICENSE;md5=5b7c15ad5fffe2ff6e9d58a6c161f082', 799 'file://src/${GO_IMPORT}/vendor/github.com/go-logfmt/logfmt/LICENSE;md5=98e39517c38127f969de33057067091e', 800 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/locales/LICENSE;md5=3ccbda375ee345400ad1da85ba522301', 801 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/universal-translator/LICENSE;md5=2e2b21ef8f61057977d27c727c84bef1', 802 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/validator/v10/LICENSE;md5=a718a0f318d76f7c5d510cbae84f0b60', 803 'file://src/${GO_IMPORT}/vendor/github.com/go-redis/redis/v7/LICENSE;md5=58103aa5ea1ee9b7a369c9c4a95ef9b5', 804 'file://src/${GO_IMPORT}/vendor/github.com/golang/protobuf/LICENSE;md5=939cce1ec101726fa754e698ac871622', 805 'file://src/${GO_IMPORT}/vendor/github.com/gomodule/redigo/LICENSE;md5=2ee41112a44fe7014dce33e26468ba93', 806 'file://src/${GO_IMPORT}/vendor/github.com/google/uuid/LICENSE;md5=88073b6dd8ec00fe09da59e0b6dfded1', 807 'file://src/${GO_IMPORT}/vendor/github.com/gorilla/mux/LICENSE;md5=33fa1116c45f9e8de714033f99edde13', 808 'file://src/${GO_IMPORT}/vendor/github.com/gorilla/websocket/LICENSE;md5=c007b54a1743d596f46b2748d9f8c044', 809 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/consul/api/LICENSE;md5=b8a277a612171b7526e9be072f405ef4', 810 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/errwrap/LICENSE;md5=b278a92d2c1509760384428817710378', 811 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-cleanhttp/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea', 812 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-hclog/LICENSE;md5=ec7f605b74b9ad03347d0a93a5cc7eb8', 813 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-immutable-radix/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea', 814 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-multierror/LICENSE;md5=d44fdeb607e2d2614db9464dbedd4094', 815 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-rootcerts/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea', 816 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/golang-lru/LICENSE;md5=f27a50d2e878867827842f2c60e30bfc', 817 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/serf/LICENSE;md5=b278a92d2c1509760384428817710378', 818 'file://src/${GO_IMPORT}/vendor/github.com/leodido/go-urn/LICENSE;md5=8f50db5538ec1148a9b3d14ed96c3418', 819 'file://src/${GO_IMPORT}/vendor/github.com/mattn/go-colorable/LICENSE;md5=24ce168f90aec2456a73de1839037245', 820 'file://src/${GO_IMPORT}/vendor/github.com/mattn/go-isatty/LICENSE;md5=f509beadd5a11227c27b5d2ad6c9f2c6', 821 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/consulstructure/LICENSE;md5=96ada10a9e51c98c4656f2cede08c673', 822 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/copystructure/LICENSE;md5=56da355a12d4821cda57b8f23ec34bc4', 823 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/go-homedir/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd', 824 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/mapstructure/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd', 825 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/reflectwalk/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd', 826 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nats.go/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', 827 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nkeys/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', 828 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nuid/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', 829 'file://src/${GO_IMPORT}/vendor/github.com/pmezard/go-difflib/LICENSE;md5=e9a2ebb8de779a07500ddecca806145e', 830 'file://src/${GO_IMPORT}/vendor/github.com/rcrowley/go-metrics/LICENSE;md5=1bdf5d819f50f141366dabce3be1460f', 831 'file://src/${GO_IMPORT}/vendor/github.com/spiffe/go-spiffe/v2/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', 832 'file://src/${GO_IMPORT}/vendor/github.com/stretchr/objx/LICENSE;md5=d023fd31d3ca39ec61eec65a91732735', 833 'file://src/${GO_IMPORT}/vendor/github.com/stretchr/testify/LICENSE;md5=188f01994659f3c0d310612333d2a26f', 834 'file://src/${GO_IMPORT}/vendor/github.com/x448/float16/LICENSE;md5=de8f8e025d57fe7ee0b67f30d571323b', 835 'file://src/${GO_IMPORT}/vendor/github.com/zeebo/errs/LICENSE;md5=84914ab36fc0eb48edbaa53e66e8d326', 836 'file://src/${GO_IMPORT}/vendor/golang.org/x/crypto/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', 837 'file://src/${GO_IMPORT}/vendor/golang.org/x/mod/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', 838 'file://src/${GO_IMPORT}/vendor/golang.org/x/net/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', 839 'file://src/${GO_IMPORT}/vendor/golang.org/x/sync/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', 840 'file://src/${GO_IMPORT}/vendor/golang.org/x/sys/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', 841 'file://src/${GO_IMPORT}/vendor/golang.org/x/text/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', 842 'file://src/${GO_IMPORT}/vendor/golang.org/x/tools/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', 843 'file://src/${GO_IMPORT}/vendor/google.golang.org/genproto/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57', 844 'file://src/${GO_IMPORT}/vendor/google.golang.org/grpc/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57', 845 'file://src/${GO_IMPORT}/vendor/google.golang.org/protobuf/LICENSE;md5=02d4002e9171d41a8fad93aa7faf3956', 846 'file://src/${GO_IMPORT}/vendor/gopkg.in/eapache/queue.v1/LICENSE;md5=1bfd4408d3de090ef6b908b0cc45a316', 847 'file://src/${GO_IMPORT}/vendor/gopkg.in/yaml.v3/LICENSE;md5=3c91c17266710e16afdbb2b6d15c761c']) 848 849 self.assertTrue(os.path.isfile(lics_require_file)) 850 self._test_recipe_contents(lics_require_file, checkvars, []) 851 852 dependencies = \ 853 [ ('github.com/eclipse/paho.mqtt.golang','v1.4.2', '', '', ''), 854 ('github.com/edgexfoundry/go-mod-bootstrap','v3.0.1','github.com/edgexfoundry/go-mod-bootstrap/v3','/v3', ''), 855 ('github.com/edgexfoundry/go-mod-configuration','v3.0.0','github.com/edgexfoundry/go-mod-configuration/v3','/v3', ''), 856 ('github.com/edgexfoundry/go-mod-core-contracts','v3.0.0','github.com/edgexfoundry/go-mod-core-contracts/v3','/v3', ''), 857 ('github.com/edgexfoundry/go-mod-messaging','v3.0.0','github.com/edgexfoundry/go-mod-messaging/v3','/v3', ''), 858 ('github.com/edgexfoundry/go-mod-secrets','v3.0.1','github.com/edgexfoundry/go-mod-secrets/v3','/v3', ''), 859 ('github.com/fxamacker/cbor','v2.4.0','github.com/fxamacker/cbor/v2','/v2', ''), 860 ('github.com/gomodule/redigo','v1.8.9', '', '', ''), 861 ('github.com/google/uuid','v1.3.0', '', '', ''), 862 ('github.com/gorilla/mux','v1.8.0', '', '', ''), 863 ('github.com/rcrowley/go-metrics','v0.0.0-20201227073835-cf1acfcdf475', '', '', ''), 864 ('github.com/spiffe/go-spiffe','v2.1.4','github.com/spiffe/go-spiffe/v2','/v2', ''), 865 ('github.com/stretchr/testify','v1.8.2', '', '', ''), 866 ('go.googlesource.com/crypto','v0.8.0','golang.org/x/crypto', '', ''), 867 ('gopkg.in/eapache/queue.v1','v1.1.0', '', '', ''), 868 ('gopkg.in/yaml.v3','v3.0.1', '', '', ''), 869 ('github.com/microsoft/go-winio','v0.6.0','github.com/Microsoft/go-winio', '', ''), 870 ('github.com/hashicorp/go-metrics','v0.3.10','github.com/armon/go-metrics', '', ''), 871 ('github.com/cenkalti/backoff','v2.2.1+incompatible', '', '', ''), 872 ('github.com/davecgh/go-spew','v1.1.1', '', '', ''), 873 ('github.com/edgexfoundry/go-mod-registry','v3.0.0','github.com/edgexfoundry/go-mod-registry/v3','/v3', ''), 874 ('github.com/fatih/color','v1.9.0', '', '', ''), 875 ('github.com/go-jose/go-jose','v3.0.0','github.com/go-jose/go-jose/v3','/v3', ''), 876 ('github.com/go-kit/log','v0.2.1', '', '', ''), 877 ('github.com/go-logfmt/logfmt','v0.5.1', '', '', ''), 878 ('github.com/go-playground/locales','v0.14.1', '', '', ''), 879 ('github.com/go-playground/universal-translator','v0.18.1', '', '', ''), 880 ('github.com/go-playground/validator','v10.13.0','github.com/go-playground/validator/v10','/v10', ''), 881 ('github.com/go-redis/redis','v7.3.0','github.com/go-redis/redis/v7','/v7', ''), 882 ('github.com/golang/protobuf','v1.5.2', '', '', ''), 883 ('github.com/gorilla/websocket','v1.4.2', '', '', ''), 884 ('github.com/hashicorp/consul','v1.20.0','github.com/hashicorp/consul/api', '', 'api'), 885 ('github.com/hashicorp/errwrap','v1.0.0', '', '', ''), 886 ('github.com/hashicorp/go-cleanhttp','v0.5.1', '', '', ''), 887 ('github.com/hashicorp/go-hclog','v0.14.1', '', '', ''), 888 ('github.com/hashicorp/go-immutable-radix','v1.3.0', '', '', ''), 889 ('github.com/hashicorp/go-multierror','v1.1.1', '', '', ''), 890 ('github.com/hashicorp/go-rootcerts','v1.0.2', '', '', ''), 891 ('github.com/hashicorp/golang-lru','v0.5.4', '', '', ''), 892 ('github.com/hashicorp/serf','v0.10.1', '', '', ''), 893 ('github.com/leodido/go-urn','v1.2.3', '', '', ''), 894 ('github.com/mattn/go-colorable','v0.1.12', '', '', ''), 895 ('github.com/mattn/go-isatty','v0.0.14', '', '', ''), 896 ('github.com/mitchellh/consulstructure','v0.0.0-20190329231841-56fdc4d2da54', '', '', ''), 897 ('github.com/mitchellh/copystructure','v1.2.0', '', '', ''), 898 ('github.com/mitchellh/go-homedir','v1.1.0', '', '', ''), 899 ('github.com/mitchellh/mapstructure','v1.5.0', '', '', ''), 900 ('github.com/mitchellh/reflectwalk','v1.0.2', '', '', ''), 901 ('github.com/nats-io/nats.go','v1.25.0', '', '', ''), 902 ('github.com/nats-io/nkeys','v0.4.4', '', '', ''), 903 ('github.com/nats-io/nuid','v1.0.1', '', '', ''), 904 ('github.com/pmezard/go-difflib','v1.0.0', '', '', ''), 905 ('github.com/stretchr/objx','v0.5.0', '', '', ''), 906 ('github.com/x448/float16','v0.8.4', '', '', ''), 907 ('github.com/zeebo/errs','v1.3.0', '', '', ''), 908 ('go.googlesource.com/mod','v0.8.0','golang.org/x/mod', '', ''), 909 ('go.googlesource.com/net','v0.9.0','golang.org/x/net', '', ''), 910 ('go.googlesource.com/sync','v0.1.0','golang.org/x/sync', '', ''), 911 ('go.googlesource.com/sys','v0.7.0','golang.org/x/sys', '', ''), 912 ('go.googlesource.com/text','v0.9.0','golang.org/x/text', '', ''), 913 ('go.googlesource.com/tools','v0.6.0','golang.org/x/tools', '', ''), 914 ('github.com/googleapis/go-genproto','v0.0.0-20230223222841-637eb2293923','google.golang.org/genproto', '', ''), 915 ('github.com/grpc/grpc-go','v1.53.0','google.golang.org/grpc', '', ''), 916 ('go.googlesource.com/protobuf','v1.28.1','google.golang.org/protobuf', '', ''), 917 ] 918 919 src_uri = set() 920 for d in dependencies: 921 src_uri.add(self._go_urifiy(*d)) 922 923 checkvars = {} 924 checkvars['GO_DEPENDENCIES_SRC_URI'] = src_uri 925 926 self.assertTrue(os.path.isfile(deps_require_file)) 927 self._test_recipe_contents(deps_require_file, checkvars, []) 928 929 def test_recipetool_create_go_replace_modules(self): 930 # Check handling of replaced modules 931 temprecipe = os.path.join(self.tempdir, 'recipe') 932 os.makedirs(temprecipe) 933 934 recipefile = os.path.join(temprecipe, 'openapi-generator_git.bb') 935 deps_require_file = os.path.join(temprecipe, 'openapi-generator', 'go-modules.inc') 936 lics_require_file = os.path.join(temprecipe, 'openapi-generator', 'go-licenses.inc') 937 modules_txt_file = os.path.join(temprecipe, 'openapi-generator', 'modules.txt') 938 939 srcuri = 'https://github.com/OpenAPITools/openapi-generator.git' 940 srcrev = "v7.2.0" 941 srcbranch = "master" 942 srcsubdir = "samples/openapi3/client/petstore/go" 943 944 result = runCmd('recipetool create -o %s %s -S %s -B %s --src-subdir %s' % (temprecipe, srcuri, srcrev, srcbranch, srcsubdir)) 945 946 self.maxDiff = None 947 inherits = ['go-vendor'] 948 949 checkvars = {} 950 checkvars['GO_IMPORT'] = "github.com/OpenAPITools/openapi-generator/samples/openapi3/client/petstore/go" 951 checkvars['SRC_URI'] = {'git://${GO_IMPORT};destsuffix=git/src/${GO_IMPORT};nobranch=1;name=${BPN};protocol=https', 952 'file://modules.txt'} 953 954 self.assertNotIn('Traceback', result.output) 955 self.assertIn('No license file was detected for the main module', result.output) 956 self.assertTrue(os.path.isfile(recipefile)) 957 self._test_recipe_contents(recipefile, checkvars, inherits) 958 959 # make sure that dependencies don't mention local directory ./go-petstore 960 dependencies = \ 961 [ ('github.com/stretchr/testify','v1.8.4', '', '', ''), 962 ('go.googlesource.com/oauth2','v0.10.0','golang.org/x/oauth2', '', ''), 963 ('github.com/davecgh/go-spew','v1.1.1', '', '', ''), 964 ('github.com/golang/protobuf','v1.5.3', '', '', ''), 965 ('github.com/kr/pretty','v0.3.0', '', '', ''), 966 ('github.com/pmezard/go-difflib','v1.0.0', '', '', ''), 967 ('github.com/rogpeppe/go-internal','v1.9.0', '', '', ''), 968 ('go.googlesource.com/net','v0.12.0','golang.org/x/net', '', ''), 969 ('github.com/golang/appengine','v1.6.7','google.golang.org/appengine', '', ''), 970 ('go.googlesource.com/protobuf','v1.31.0','google.golang.org/protobuf', '', ''), 971 ('gopkg.in/check.v1','v1.0.0-20201130134442-10cb98267c6c', '', '', ''), 972 ('gopkg.in/yaml.v3','v3.0.1', '', '', ''), 973 ] 974 975 src_uri = set() 976 for d in dependencies: 977 src_uri.add(self._go_urifiy(*d)) 978 979 checkvars = {} 980 checkvars['GO_DEPENDENCIES_SRC_URI'] = src_uri 981 982 self.assertTrue(os.path.isfile(deps_require_file)) 983 self._test_recipe_contents(deps_require_file, checkvars, []) 984 985class RecipetoolTests(RecipetoolBase): 986 987 @classmethod 988 def setUpClass(cls): 989 import sys 990 991 super(RecipetoolTests, cls).setUpClass() 992 bb_vars = get_bb_vars(['BBPATH']) 993 cls.bbpath = bb_vars['BBPATH'] 994 libpath = os.path.join(get_bb_var('COREBASE'), 'scripts', 'lib', 'recipetool') 995 sys.path.insert(0, libpath) 996 997 def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths): 998 dstdir = basedstdir 999 self.assertTrue(os.path.exists(dstdir)) 1000 for p in paths: 1001 dstdir = os.path.join(dstdir, p) 1002 if not os.path.exists(dstdir): 1003 try: 1004 os.makedirs(dstdir) 1005 except PermissionError: 1006 return False 1007 except OSError as e: 1008 if e.errno == errno.EROFS: 1009 return False 1010 else: 1011 raise e 1012 if p == "lib": 1013 # Can race with other tests 1014 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir) 1015 else: 1016 self.track_for_cleanup(dstdir) 1017 dstfile = os.path.join(dstdir, os.path.basename(srcfile)) 1018 if srcfile != dstfile: 1019 try: 1020 shutil.copy(srcfile, dstfile) 1021 except PermissionError: 1022 return False 1023 self.track_for_cleanup(dstfile) 1024 return True 1025 1026 def test_recipetool_load_plugin(self): 1027 """Test that recipetool loads only the first found plugin in BBPATH.""" 1028 1029 recipetool = runCmd("which recipetool") 1030 fromname = runCmd("recipetool --quiet pluginfile") 1031 srcfile = fromname.output 1032 searchpath = self.bbpath.split(':') + [os.path.dirname(recipetool.output)] 1033 plugincontent = [] 1034 with open(srcfile) as fh: 1035 plugincontent = fh.readlines() 1036 try: 1037 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found') 1038 searchpath = [ 1039 path for path in searchpath 1040 if self._copy_file_with_cleanup(srcfile, path, 'lib', 'recipetool') 1041 ] 1042 result = runCmd("recipetool --quiet count") 1043 self.assertEqual(result.output, '1') 1044 result = runCmd("recipetool --quiet multiloaded") 1045 self.assertEqual(result.output, "no") 1046 for path in searchpath: 1047 result = runCmd("recipetool --quiet bbdir") 1048 self.assertEqual(os.path.realpath(result.output), os.path.realpath(path)) 1049 os.unlink(os.path.join(result.output, 'lib', 'recipetool', 'bbpath.py')) 1050 finally: 1051 with open(srcfile, 'w') as fh: 1052 fh.writelines(plugincontent) 1053 1054 def test_recipetool_handle_license_vars(self): 1055 from create import handle_license_vars 1056 from unittest.mock import Mock 1057 1058 commonlicdir = get_bb_var('COMMON_LICENSE_DIR') 1059 1060 class DataConnectorCopy(bb.tinfoil.TinfoilDataStoreConnector): 1061 pass 1062 1063 d = DataConnectorCopy 1064 d.getVar = Mock(return_value=commonlicdir) 1065 1066 srctree = tempfile.mkdtemp(prefix='recipetoolqa') 1067 self.track_for_cleanup(srctree) 1068 1069 # Multiple licenses 1070 licenses = ['MIT', 'ISC', 'BSD-3-Clause', 'Apache-2.0'] 1071 for licence in licenses: 1072 shutil.copy(os.path.join(commonlicdir, licence), os.path.join(srctree, 'LICENSE.' + licence)) 1073 # Duplicate license 1074 shutil.copy(os.path.join(commonlicdir, 'MIT'), os.path.join(srctree, 'LICENSE')) 1075 1076 extravalues = { 1077 # Duplicate and missing licenses 1078 'LICENSE': 'Zlib & BSD-2-Clause & Zlib', 1079 'LIC_FILES_CHKSUM': [ 1080 'file://README.md;md5=0123456789abcdef0123456789abcd' 1081 ] 1082 } 1083 lines_before = [] 1084 handled = [] 1085 licvalues = handle_license_vars(srctree, lines_before, handled, extravalues, d) 1086 expected_lines_before = [ 1087 '# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is', 1088 '# your responsibility to verify that the values are complete and correct.', 1089 '# NOTE: Original package / source metadata indicates license is: BSD-2-Clause & Zlib', 1090 '#', 1091 '# NOTE: multiple licenses have been detected; they have been separated with &', 1092 '# in the LICENSE value for now since it is a reasonable assumption that all', 1093 '# of the licenses apply. If instead there is a choice between the multiple', 1094 '# licenses then you should change the value to separate the licenses with |', 1095 '# instead of &. If there is any doubt, check the accompanying documentation', 1096 '# to determine which situation is applicable.', 1097 'LICENSE = "Apache-2.0 & BSD-2-Clause & BSD-3-Clause & ISC & MIT & Zlib"', 1098 'LIC_FILES_CHKSUM = "file://LICENSE;md5=0835ade698e0bcf8506ecda2f7b4f302 \\\n' 1099 ' file://LICENSE.Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10 \\\n' 1100 ' file://LICENSE.BSD-3-Clause;md5=550794465ba0ec5312d6919e203a55f9 \\\n' 1101 ' file://LICENSE.ISC;md5=f3b90e78ea0cffb20bf5cca7947a896d \\\n' 1102 ' file://LICENSE.MIT;md5=0835ade698e0bcf8506ecda2f7b4f302 \\\n' 1103 ' file://README.md;md5=0123456789abcdef0123456789abcd"', 1104 '' 1105 ] 1106 self.assertEqual(lines_before, expected_lines_before) 1107 expected_licvalues = [ 1108 ('MIT', 'LICENSE', '0835ade698e0bcf8506ecda2f7b4f302'), 1109 ('Apache-2.0', 'LICENSE.Apache-2.0', '89aea4e17d99a7cacdbeed46a0096b10'), 1110 ('BSD-3-Clause', 'LICENSE.BSD-3-Clause', '550794465ba0ec5312d6919e203a55f9'), 1111 ('ISC', 'LICENSE.ISC', 'f3b90e78ea0cffb20bf5cca7947a896d'), 1112 ('MIT', 'LICENSE.MIT', '0835ade698e0bcf8506ecda2f7b4f302') 1113 ] 1114 self.assertEqual(handled, [('license', expected_licvalues)]) 1115 self.assertEqual(extravalues, {}) 1116 self.assertEqual(licvalues, expected_licvalues) 1117 1118 1119 def test_recipetool_split_pkg_licenses(self): 1120 from create import split_pkg_licenses 1121 licvalues = [ 1122 # Duplicate licenses 1123 ('BSD-2-Clause', 'x/COPYING', None), 1124 ('BSD-2-Clause', 'x/LICENSE', None), 1125 # Multiple licenses 1126 ('MIT', 'x/a/LICENSE.MIT', None), 1127 ('ISC', 'x/a/LICENSE.ISC', None), 1128 # Alternative licenses 1129 ('(MIT | ISC)', 'x/b/LICENSE', None), 1130 # Alternative licenses without brackets 1131 ('MIT | BSD-2-Clause', 'x/c/LICENSE', None), 1132 # Multi licenses with alternatives 1133 ('MIT', 'x/d/COPYING', None), 1134 ('MIT | BSD-2-Clause', 'x/d/LICENSE', None), 1135 # Multi licenses with alternatives and brackets 1136 ('Apache-2.0 & ((MIT | ISC) & BSD-3-Clause)', 'x/e/LICENSE', None) 1137 ] 1138 packages = { 1139 '${PN}': '', 1140 'a': 'x/a', 1141 'b': 'x/b', 1142 'c': 'x/c', 1143 'd': 'x/d', 1144 'e': 'x/e', 1145 'f': 'x/f', 1146 'g': 'x/g', 1147 } 1148 fallback_licenses = { 1149 # Ignored 1150 'a': 'BSD-3-Clause', 1151 # Used 1152 'f': 'BSD-3-Clause' 1153 } 1154 outlines = [] 1155 outlicenses = split_pkg_licenses(licvalues, packages, outlines, fallback_licenses) 1156 expected_outlicenses = { 1157 '${PN}': ['BSD-2-Clause'], 1158 'a': ['ISC', 'MIT'], 1159 'b': ['(ISC | MIT)'], 1160 'c': ['(BSD-2-Clause | MIT)'], 1161 'd': ['(BSD-2-Clause | MIT)', 'MIT'], 1162 'e': ['(ISC | MIT)', 'Apache-2.0', 'BSD-3-Clause'], 1163 'f': ['BSD-3-Clause'], 1164 'g': ['Unknown'] 1165 } 1166 self.assertEqual(outlicenses, expected_outlicenses) 1167 expected_outlines = [ 1168 'LICENSE:${PN} = "BSD-2-Clause"', 1169 'LICENSE:a = "ISC & MIT"', 1170 'LICENSE:b = "(ISC | MIT)"', 1171 'LICENSE:c = "(BSD-2-Clause | MIT)"', 1172 'LICENSE:d = "(BSD-2-Clause | MIT) & MIT"', 1173 'LICENSE:e = "(ISC | MIT) & Apache-2.0 & BSD-3-Clause"', 1174 'LICENSE:f = "BSD-3-Clause"', 1175 'LICENSE:g = "Unknown"' 1176 ] 1177 self.assertEqual(outlines, expected_outlines) 1178 1179 1180class RecipetoolAppendsrcBase(RecipetoolBase): 1181 def _try_recipetool_appendsrcfile(self, testrecipe, newfile, destfile, options, expectedlines, expectedfiles): 1182 cmd = 'recipetool appendsrcfile %s %s %s %s %s' % (options, self.templayerdir, testrecipe, newfile, destfile) 1183 return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines) 1184 1185 def _try_recipetool_appendsrcfiles(self, testrecipe, newfiles, expectedlines=None, expectedfiles=None, destdir=None, options=''): 1186 1187 if destdir: 1188 options += ' -D %s' % destdir 1189 1190 if expectedfiles is None: 1191 expectedfiles = [os.path.basename(f) for f in newfiles] 1192 1193 cmd = 'recipetool appendsrcfiles %s %s %s %s' % (options, self.templayerdir, testrecipe, ' '.join(newfiles)) 1194 return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines) 1195 1196 def _try_recipetool_appendsrcfile_fail(self, testrecipe, newfile, destfile, checkerror): 1197 cmd = 'recipetool appendsrcfile %s %s %s %s' % (self.templayerdir, testrecipe, newfile, destfile or '') 1198 result = runCmd(cmd, ignore_status=True) 1199 self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd) 1200 self.assertNotIn('Traceback', result.output) 1201 for errorstr in checkerror: 1202 self.assertIn(errorstr, result.output) 1203 1204 @staticmethod 1205 def _get_first_file_uri(recipe): 1206 '''Return the first file:// in SRC_URI for the specified recipe.''' 1207 src_uri = get_bb_var('SRC_URI', recipe).split() 1208 for uri in src_uri: 1209 p = urllib.parse.urlparse(uri) 1210 if p.scheme == 'file': 1211 return p.netloc + p.path, uri 1212 1213 def _test_appendsrcfile(self, testrecipe, filename=None, destdir=None, has_src_uri=True, srcdir=None, newfile=None, remove=None, machine=None , options=''): 1214 if newfile is None: 1215 newfile = self.testfile 1216 1217 if srcdir: 1218 if destdir: 1219 expected_subdir = os.path.join(srcdir, destdir) 1220 else: 1221 expected_subdir = srcdir 1222 else: 1223 options += " -W" 1224 expected_subdir = destdir 1225 1226 if filename: 1227 if destdir: 1228 destpath = os.path.join(destdir, filename) 1229 else: 1230 destpath = filename 1231 else: 1232 filename = os.path.basename(newfile) 1233 if destdir: 1234 destpath = destdir + os.sep 1235 else: 1236 destpath = '.' + os.sep 1237 1238 expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 1239 '\n'] 1240 1241 override = "" 1242 if machine: 1243 options += ' -m %s' % machine 1244 override = ':append:%s' % machine 1245 expectedlines.extend(['PACKAGE_ARCH = "${MACHINE_ARCH}"\n', 1246 '\n']) 1247 1248 if remove: 1249 for entry in remove: 1250 if machine: 1251 entry_remove_line = 'SRC_URI:remove:%s = " %s"\n' % (machine, entry) 1252 else: 1253 entry_remove_line = 'SRC_URI:remove = "%s"\n' % entry 1254 1255 expectedlines.extend([entry_remove_line, 1256 '\n']) 1257 1258 if has_src_uri: 1259 uri = 'file://%s' % filename 1260 if expected_subdir: 1261 uri += ';subdir=%s' % expected_subdir 1262 if machine: 1263 src_uri_line = 'SRC_URI%s = " %s"\n' % (override, uri) 1264 else: 1265 src_uri_line = 'SRC_URI += "%s"\n' % uri 1266 1267 expectedlines.extend([src_uri_line, '\n']) 1268 1269 with open("/tmp/tmp.txt", "w") as file: 1270 print(expectedlines, file=file) 1271 1272 if machine: 1273 filename = '%s/%s' % (machine, filename) 1274 1275 return self._try_recipetool_appendsrcfile(testrecipe, newfile, destpath, options, expectedlines, [filename]) 1276 1277 def _test_appendsrcfiles(self, testrecipe, newfiles, expectedfiles=None, destdir=None, options=''): 1278 if expectedfiles is None: 1279 expectedfiles = [os.path.basename(n) for n in newfiles] 1280 1281 self._try_recipetool_appendsrcfiles(testrecipe, newfiles, expectedfiles=expectedfiles, destdir=destdir, options=options) 1282 1283 bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'FILESEXTRAPATHS'], testrecipe) 1284 src_uri = bb_vars['SRC_URI'].split() 1285 for f in expectedfiles: 1286 if destdir: 1287 self.assertIn('file://%s;subdir=%s' % (f, destdir), src_uri) 1288 else: 1289 self.assertIn('file://%s' % f, src_uri) 1290 1291 recipefile = bb_vars['FILE'] 1292 bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir) 1293 filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe) 1294 filesextrapaths = bb_vars['FILESEXTRAPATHS'].split(':') 1295 self.assertIn(filesdir, filesextrapaths) 1296 1297 1298 1299 1300class RecipetoolAppendsrcTests(RecipetoolAppendsrcBase): 1301 1302 def test_recipetool_appendsrcfile_basic(self): 1303 self._test_appendsrcfile('base-files', 'a-file') 1304 1305 def test_recipetool_appendsrcfile_basic_wildcard(self): 1306 testrecipe = 'base-files' 1307 self._test_appendsrcfile(testrecipe, 'a-file', options='-w') 1308 recipefile = get_bb_var('FILE', testrecipe) 1309 bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir) 1310 self.assertEqual(os.path.basename(bbappendfile), '%s_%%.bbappend' % testrecipe) 1311 1312 def test_recipetool_appendsrcfile_subdir_basic(self): 1313 self._test_appendsrcfile('base-files', 'a-file', 'tmp') 1314 1315 def test_recipetool_appendsrcfile_subdir_basic_dirdest(self): 1316 self._test_appendsrcfile('base-files', destdir='tmp') 1317 1318 def test_recipetool_appendsrcfile_srcdir_basic(self): 1319 testrecipe = 'bash' 1320 bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe) 1321 srcdir = bb_vars['S'] 1322 workdir = bb_vars['WORKDIR'] 1323 subdir = os.path.relpath(srcdir, workdir) 1324 self._test_appendsrcfile(testrecipe, 'a-file', srcdir=subdir) 1325 1326 def test_recipetool_appendsrcfile_existing_in_src_uri(self): 1327 testrecipe = 'base-files' 1328 filepath,_ = self._get_first_file_uri(testrecipe) 1329 self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe) 1330 self._test_appendsrcfile(testrecipe, filepath, has_src_uri=False) 1331 1332 def test_recipetool_appendsrcfile_existing_in_src_uri_diff_params(self, machine=None): 1333 testrecipe = 'base-files' 1334 subdir = 'tmp' 1335 filepath, srcuri_entry = self._get_first_file_uri(testrecipe) 1336 self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe) 1337 1338 self._test_appendsrcfile(testrecipe, filepath, subdir, machine=machine, remove=[srcuri_entry]) 1339 1340 def test_recipetool_appendsrcfile_machine(self): 1341 # A very basic test 1342 self._test_appendsrcfile('base-files', 'a-file', machine='mymachine') 1343 1344 # Force cleaning the output of previous test 1345 self.tearDownLocal() 1346 1347 # A more complex test: existing entry in src_uri with different param 1348 self.test_recipetool_appendsrcfile_existing_in_src_uri_diff_params(machine='mymachine') 1349 1350 def test_recipetool_appendsrcfile_update_recipe_basic(self): 1351 testrecipe = "mtd-utils-selftest" 1352 recipefile = get_bb_var('FILE', testrecipe) 1353 self.assertIn('meta-selftest', recipefile, 'This test expect %s recipe to be in meta-selftest') 1354 cmd = 'recipetool appendsrcfile -W -u meta-selftest %s %s' % (testrecipe, self.testfile) 1355 result = runCmd(cmd) 1356 self.assertNotIn('Traceback', result.output) 1357 self.add_command_to_tearDown('cd %s; rm -f %s/%s; git checkout .' % (os.path.dirname(recipefile), testrecipe, os.path.basename(self.testfile))) 1358 1359 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), 1360 ('??', '.*/%s/%s$' % (testrecipe, os.path.basename(self.testfile)))] 1361 self._check_repo_status(os.path.dirname(recipefile), expected_status) 1362 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile)) 1363 removelines = [] 1364 addlines = [ 1365 'file://%s \\\\' % os.path.basename(self.testfile), 1366 ] 1367 self._check_diff(result.output, addlines, removelines) 1368 1369 def test_recipetool_appendsrcfile_replace_file_srcdir(self): 1370 testrecipe = 'bash' 1371 filepath = 'Makefile.in' 1372 bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe) 1373 srcdir = bb_vars['S'] 1374 workdir = bb_vars['WORKDIR'] 1375 subdir = os.path.relpath(srcdir, workdir) 1376 1377 self._test_appendsrcfile(testrecipe, filepath, srcdir=subdir) 1378 bitbake('%s:do_unpack' % testrecipe) 1379 with open(self.testfile, 'r') as testfile: 1380 with open(os.path.join(srcdir, filepath), 'r') as makefilein: 1381 self.assertEqual(testfile.read(), makefilein.read()) 1382 1383 def test_recipetool_appendsrcfiles_basic(self, destdir=None): 1384 newfiles = [self.testfile] 1385 for i in range(1, 5): 1386 testfile = os.path.join(self.tempdir, 'testfile%d' % i) 1387 with open(testfile, 'w') as f: 1388 f.write('Test file %d\n' % i) 1389 newfiles.append(testfile) 1390 self._test_appendsrcfiles('gcc', newfiles, destdir=destdir, options='-W') 1391 1392 def test_recipetool_appendsrcfiles_basic_subdir(self): 1393 self.test_recipetool_appendsrcfiles_basic(destdir='testdir') 1394