1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# SPDX-License-Identifier: MIT 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun 5*4882a593Smuzhiyunimport os 6*4882a593Smuzhiyunimport shutil 7*4882a593Smuzhiyunimport tempfile 8*4882a593Smuzhiyunimport urllib.parse 9*4882a593Smuzhiyun 10*4882a593Smuzhiyunfrom oeqa.utils.commands import runCmd, bitbake, get_bb_var 11*4882a593Smuzhiyunfrom oeqa.utils.commands import get_bb_vars, create_temp_layer 12*4882a593Smuzhiyunfrom oeqa.selftest.cases import devtool 13*4882a593Smuzhiyun 14*4882a593Smuzhiyuntemplayerdir = None 15*4882a593Smuzhiyun 16*4882a593Smuzhiyundef setUpModule(): 17*4882a593Smuzhiyun global templayerdir 18*4882a593Smuzhiyun templayerdir = tempfile.mkdtemp(prefix='recipetoolqa') 19*4882a593Smuzhiyun create_temp_layer(templayerdir, 'selftestrecipetool') 20*4882a593Smuzhiyun runCmd('bitbake-layers add-layer %s' % templayerdir) 21*4882a593Smuzhiyun 22*4882a593Smuzhiyun 23*4882a593Smuzhiyundef tearDownModule(): 24*4882a593Smuzhiyun runCmd('bitbake-layers remove-layer %s' % templayerdir, ignore_status=True) 25*4882a593Smuzhiyun runCmd('rm -rf %s' % templayerdir) 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun 28*4882a593Smuzhiyunclass RecipetoolBase(devtool.DevtoolTestCase): 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun def setUpLocal(self): 31*4882a593Smuzhiyun super(RecipetoolBase, self).setUpLocal() 32*4882a593Smuzhiyun self.templayerdir = templayerdir 33*4882a593Smuzhiyun self.tempdir = tempfile.mkdtemp(prefix='recipetoolqa') 34*4882a593Smuzhiyun self.track_for_cleanup(self.tempdir) 35*4882a593Smuzhiyun self.testfile = os.path.join(self.tempdir, 'testfile') 36*4882a593Smuzhiyun with open(self.testfile, 'w') as f: 37*4882a593Smuzhiyun f.write('Test file\n') 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun def tearDownLocal(self): 40*4882a593Smuzhiyun runCmd('rm -rf %s/recipes-*' % self.templayerdir) 41*4882a593Smuzhiyun super(RecipetoolBase, self).tearDownLocal() 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun def _try_recipetool_appendcmd(self, cmd, testrecipe, expectedfiles, expectedlines=None): 44*4882a593Smuzhiyun result = runCmd(cmd) 45*4882a593Smuzhiyun self.assertNotIn('Traceback', result.output) 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun # Check the bbappend was created and applies properly 48*4882a593Smuzhiyun recipefile = get_bb_var('FILE', testrecipe) 49*4882a593Smuzhiyun bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir) 50*4882a593Smuzhiyun 51*4882a593Smuzhiyun # Check the bbappend contents 52*4882a593Smuzhiyun if expectedlines is not None: 53*4882a593Smuzhiyun with open(bbappendfile, 'r') as f: 54*4882a593Smuzhiyun self.assertEqual(expectedlines, f.readlines(), "Expected lines are not present in %s" % bbappendfile) 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun # Check file was copied 57*4882a593Smuzhiyun filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe) 58*4882a593Smuzhiyun for expectedfile in expectedfiles: 59*4882a593Smuzhiyun self.assertTrue(os.path.isfile(os.path.join(filesdir, expectedfile)), 'Expected file %s to be copied next to bbappend, but it wasn\'t' % expectedfile) 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun # Check no other files created 62*4882a593Smuzhiyun createdfiles = [] 63*4882a593Smuzhiyun for root, _, files in os.walk(filesdir): 64*4882a593Smuzhiyun for f in files: 65*4882a593Smuzhiyun createdfiles.append(os.path.relpath(os.path.join(root, f), filesdir)) 66*4882a593Smuzhiyun self.assertTrue(sorted(createdfiles), sorted(expectedfiles)) 67*4882a593Smuzhiyun 68*4882a593Smuzhiyun return bbappendfile, result.output 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun 71*4882a593Smuzhiyunclass RecipetoolAppendTests(RecipetoolBase): 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun @classmethod 74*4882a593Smuzhiyun def setUpClass(cls): 75*4882a593Smuzhiyun super(RecipetoolAppendTests, cls).setUpClass() 76*4882a593Smuzhiyun # Ensure we have the right data in shlibs/pkgdata 77*4882a593Smuzhiyun cls.logger.info('Running bitbake to generate pkgdata') 78*4882a593Smuzhiyun bitbake('-c packagedata base-files coreutils busybox selftest-recipetool-appendfile') 79*4882a593Smuzhiyun bb_vars = get_bb_vars(['COREBASE']) 80*4882a593Smuzhiyun cls.corebase = bb_vars['COREBASE'] 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun def _try_recipetool_appendfile(self, testrecipe, destfile, newfile, options, expectedlines, expectedfiles): 83*4882a593Smuzhiyun cmd = 'recipetool appendfile %s %s %s %s' % (self.templayerdir, destfile, newfile, options) 84*4882a593Smuzhiyun return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines) 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun def _try_recipetool_appendfile_fail(self, destfile, newfile, checkerror): 87*4882a593Smuzhiyun cmd = 'recipetool appendfile %s %s %s' % (self.templayerdir, destfile, newfile) 88*4882a593Smuzhiyun result = runCmd(cmd, ignore_status=True) 89*4882a593Smuzhiyun self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd) 90*4882a593Smuzhiyun self.assertNotIn('Traceback', result.output) 91*4882a593Smuzhiyun for errorstr in checkerror: 92*4882a593Smuzhiyun self.assertIn(errorstr, result.output) 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun def test_recipetool_appendfile_basic(self): 95*4882a593Smuzhiyun # Basic test 96*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 97*4882a593Smuzhiyun '\n'] 98*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('base-files', '/etc/motd', self.testfile, '', expectedlines, ['motd']) 99*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 100*4882a593Smuzhiyun 101*4882a593Smuzhiyun def test_recipetool_appendfile_invalid(self): 102*4882a593Smuzhiyun # Test some commands that should error 103*4882a593Smuzhiyun self._try_recipetool_appendfile_fail('/etc/passwd', self.testfile, ['ERROR: /etc/passwd cannot be handled by this tool', 'useradd', 'extrausers']) 104*4882a593Smuzhiyun self._try_recipetool_appendfile_fail('/etc/timestamp', self.testfile, ['ERROR: /etc/timestamp cannot be handled by this tool']) 105*4882a593Smuzhiyun self._try_recipetool_appendfile_fail('/dev/console', self.testfile, ['ERROR: /dev/console cannot be handled by this tool']) 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun def test_recipetool_appendfile_alternatives(self): 108*4882a593Smuzhiyun # Now try with a file we know should be an alternative 109*4882a593Smuzhiyun # (this is very much a fake example, but one we know is reliably an alternative) 110*4882a593Smuzhiyun self._try_recipetool_appendfile_fail('/bin/ls', self.testfile, ['ERROR: File /bin/ls is an alternative possibly provided by the following recipes:', 'coreutils', 'busybox']) 111*4882a593Smuzhiyun # Need a test file - should be executable 112*4882a593Smuzhiyun testfile2 = os.path.join(self.corebase, 'oe-init-build-env') 113*4882a593Smuzhiyun testfile2name = os.path.basename(testfile2) 114*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 115*4882a593Smuzhiyun '\n', 116*4882a593Smuzhiyun 'SRC_URI += "file://%s"\n' % testfile2name, 117*4882a593Smuzhiyun '\n', 118*4882a593Smuzhiyun 'do_install:append() {\n', 119*4882a593Smuzhiyun ' install -d ${D}${base_bindir}\n', 120*4882a593Smuzhiyun ' install -m 0755 ${WORKDIR}/%s ${D}${base_bindir}/ls\n' % testfile2name, 121*4882a593Smuzhiyun '}\n'] 122*4882a593Smuzhiyun self._try_recipetool_appendfile('coreutils', '/bin/ls', testfile2, '-r coreutils', expectedlines, [testfile2name]) 123*4882a593Smuzhiyun # Now try bbappending the same file again, contents should not change 124*4882a593Smuzhiyun bbappendfile, _ = self._try_recipetool_appendfile('coreutils', '/bin/ls', self.testfile, '-r coreutils', expectedlines, [testfile2name]) 125*4882a593Smuzhiyun # But file should have 126*4882a593Smuzhiyun copiedfile = os.path.join(os.path.dirname(bbappendfile), 'coreutils', testfile2name) 127*4882a593Smuzhiyun result = runCmd('diff -q %s %s' % (testfile2, copiedfile), ignore_status=True) 128*4882a593Smuzhiyun self.assertNotEqual(result.status, 0, 'New file should have been copied but was not %s' % result.output) 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun def test_recipetool_appendfile_binary(self): 131*4882a593Smuzhiyun # Try appending a binary file 132*4882a593Smuzhiyun # /bin/ls can be a symlink to /usr/bin/ls 133*4882a593Smuzhiyun ls = os.path.realpath("/bin/ls") 134*4882a593Smuzhiyun result = runCmd('recipetool appendfile %s /bin/ls %s -r coreutils' % (self.templayerdir, ls)) 135*4882a593Smuzhiyun self.assertIn('WARNING: ', result.output) 136*4882a593Smuzhiyun self.assertIn('is a binary', result.output) 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun def test_recipetool_appendfile_add(self): 139*4882a593Smuzhiyun # Try arbitrary file add to a recipe 140*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 141*4882a593Smuzhiyun '\n', 142*4882a593Smuzhiyun 'SRC_URI += "file://testfile"\n', 143*4882a593Smuzhiyun '\n', 144*4882a593Smuzhiyun 'do_install:append() {\n', 145*4882a593Smuzhiyun ' install -d ${D}${datadir}\n', 146*4882a593Smuzhiyun ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', 147*4882a593Smuzhiyun '}\n'] 148*4882a593Smuzhiyun self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase', expectedlines, ['testfile']) 149*4882a593Smuzhiyun # Try adding another file, this time where the source file is executable 150*4882a593Smuzhiyun # (so we're testing that, plus modifying an existing bbappend) 151*4882a593Smuzhiyun testfile2 = os.path.join(self.corebase, 'oe-init-build-env') 152*4882a593Smuzhiyun testfile2name = os.path.basename(testfile2) 153*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 154*4882a593Smuzhiyun '\n', 155*4882a593Smuzhiyun 'SRC_URI += "file://testfile \\\n', 156*4882a593Smuzhiyun ' file://%s \\\n' % testfile2name, 157*4882a593Smuzhiyun ' "\n', 158*4882a593Smuzhiyun '\n', 159*4882a593Smuzhiyun 'do_install:append() {\n', 160*4882a593Smuzhiyun ' install -d ${D}${datadir}\n', 161*4882a593Smuzhiyun ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', 162*4882a593Smuzhiyun ' install -m 0755 ${WORKDIR}/%s ${D}${datadir}/scriptname\n' % testfile2name, 163*4882a593Smuzhiyun '}\n'] 164*4882a593Smuzhiyun self._try_recipetool_appendfile('netbase', '/usr/share/scriptname', testfile2, '-r netbase', expectedlines, ['testfile', testfile2name]) 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun def test_recipetool_appendfile_add_bindir(self): 167*4882a593Smuzhiyun # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable 168*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 169*4882a593Smuzhiyun '\n', 170*4882a593Smuzhiyun 'SRC_URI += "file://testfile"\n', 171*4882a593Smuzhiyun '\n', 172*4882a593Smuzhiyun 'do_install:append() {\n', 173*4882a593Smuzhiyun ' install -d ${D}${bindir}\n', 174*4882a593Smuzhiyun ' install -m 0755 ${WORKDIR}/testfile ${D}${bindir}/selftest-recipetool-testbin\n', 175*4882a593Smuzhiyun '}\n'] 176*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('netbase', '/usr/bin/selftest-recipetool-testbin', self.testfile, '-r netbase', expectedlines, ['testfile']) 177*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 178*4882a593Smuzhiyun 179*4882a593Smuzhiyun def test_recipetool_appendfile_add_machine(self): 180*4882a593Smuzhiyun # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable 181*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 182*4882a593Smuzhiyun '\n', 183*4882a593Smuzhiyun 'PACKAGE_ARCH = "${MACHINE_ARCH}"\n', 184*4882a593Smuzhiyun '\n', 185*4882a593Smuzhiyun 'SRC_URI:append:mymachine = " file://testfile"\n', 186*4882a593Smuzhiyun '\n', 187*4882a593Smuzhiyun 'do_install:append:mymachine() {\n', 188*4882a593Smuzhiyun ' install -d ${D}${datadir}\n', 189*4882a593Smuzhiyun ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', 190*4882a593Smuzhiyun '}\n'] 191*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase -m mymachine', expectedlines, ['mymachine/testfile']) 192*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 193*4882a593Smuzhiyun 194*4882a593Smuzhiyun def test_recipetool_appendfile_orig(self): 195*4882a593Smuzhiyun # A file that's in SRC_URI and in do_install with the same name 196*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 197*4882a593Smuzhiyun '\n'] 198*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-orig', self.testfile, '', expectedlines, ['selftest-replaceme-orig']) 199*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun def test_recipetool_appendfile_todir(self): 202*4882a593Smuzhiyun # A file that's in SRC_URI and in do_install with destination directory rather than file 203*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 204*4882a593Smuzhiyun '\n'] 205*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-todir', self.testfile, '', expectedlines, ['selftest-replaceme-todir']) 206*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 207*4882a593Smuzhiyun 208*4882a593Smuzhiyun def test_recipetool_appendfile_renamed(self): 209*4882a593Smuzhiyun # A file that's in SRC_URI with a different name to the destination file 210*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 211*4882a593Smuzhiyun '\n'] 212*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-renamed', self.testfile, '', expectedlines, ['file1']) 213*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun def test_recipetool_appendfile_subdir(self): 216*4882a593Smuzhiyun # A file that's in SRC_URI in a subdir 217*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 218*4882a593Smuzhiyun '\n', 219*4882a593Smuzhiyun 'SRC_URI += "file://testfile"\n', 220*4882a593Smuzhiyun '\n', 221*4882a593Smuzhiyun 'do_install:append() {\n', 222*4882a593Smuzhiyun ' install -d ${D}${datadir}\n', 223*4882a593Smuzhiyun ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-subdir\n', 224*4882a593Smuzhiyun '}\n'] 225*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-subdir', self.testfile, '', expectedlines, ['testfile']) 226*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 227*4882a593Smuzhiyun 228*4882a593Smuzhiyun def test_recipetool_appendfile_inst_glob(self): 229*4882a593Smuzhiyun # A file that's in do_install as a glob 230*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 231*4882a593Smuzhiyun '\n'] 232*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-globfile']) 233*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 234*4882a593Smuzhiyun 235*4882a593Smuzhiyun def test_recipetool_appendfile_inst_todir_glob(self): 236*4882a593Smuzhiyun # A file that's in do_install as a glob with destination as a directory 237*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 238*4882a593Smuzhiyun '\n'] 239*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-todir-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-todir-globfile']) 240*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 241*4882a593Smuzhiyun 242*4882a593Smuzhiyun def test_recipetool_appendfile_patch(self): 243*4882a593Smuzhiyun # A file that's added by a patch in SRC_URI 244*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 245*4882a593Smuzhiyun '\n', 246*4882a593Smuzhiyun 'SRC_URI += "file://testfile"\n', 247*4882a593Smuzhiyun '\n', 248*4882a593Smuzhiyun 'do_install:append() {\n', 249*4882a593Smuzhiyun ' install -d ${D}${sysconfdir}\n', 250*4882a593Smuzhiyun ' install -m 0644 ${WORKDIR}/testfile ${D}${sysconfdir}/selftest-replaceme-patched\n', 251*4882a593Smuzhiyun '}\n'] 252*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/etc/selftest-replaceme-patched', self.testfile, '', expectedlines, ['testfile']) 253*4882a593Smuzhiyun for line in output.splitlines(): 254*4882a593Smuzhiyun if 'WARNING: ' in line: 255*4882a593Smuzhiyun self.assertIn('add-file.patch', line, 'Unexpected warning found in output:\n%s' % line) 256*4882a593Smuzhiyun break 257*4882a593Smuzhiyun else: 258*4882a593Smuzhiyun self.fail('Patch warning not found in output:\n%s' % output) 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun def test_recipetool_appendfile_script(self): 261*4882a593Smuzhiyun # Now, a file that's in SRC_URI but installed by a script (so no mention in do_install) 262*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 263*4882a593Smuzhiyun '\n', 264*4882a593Smuzhiyun 'SRC_URI += "file://testfile"\n', 265*4882a593Smuzhiyun '\n', 266*4882a593Smuzhiyun 'do_install:append() {\n', 267*4882a593Smuzhiyun ' install -d ${D}${datadir}\n', 268*4882a593Smuzhiyun ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-scripted\n', 269*4882a593Smuzhiyun '}\n'] 270*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-scripted', self.testfile, '', expectedlines, ['testfile']) 271*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 272*4882a593Smuzhiyun 273*4882a593Smuzhiyun def test_recipetool_appendfile_inst_func(self): 274*4882a593Smuzhiyun # A file that's installed from a function called by do_install 275*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 276*4882a593Smuzhiyun '\n'] 277*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-func', self.testfile, '', expectedlines, ['selftest-replaceme-inst-func']) 278*4882a593Smuzhiyun self.assertNotIn('WARNING: ', output) 279*4882a593Smuzhiyun 280*4882a593Smuzhiyun def test_recipetool_appendfile_postinstall(self): 281*4882a593Smuzhiyun # A file that's created by a postinstall script (and explicitly mentioned in it) 282*4882a593Smuzhiyun # First try without specifying recipe 283*4882a593Smuzhiyun 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']) 284*4882a593Smuzhiyun # Now specify recipe 285*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 286*4882a593Smuzhiyun '\n', 287*4882a593Smuzhiyun 'SRC_URI += "file://testfile"\n', 288*4882a593Smuzhiyun '\n', 289*4882a593Smuzhiyun 'do_install:append() {\n', 290*4882a593Smuzhiyun ' install -d ${D}${datadir}\n', 291*4882a593Smuzhiyun ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-postinst\n', 292*4882a593Smuzhiyun '}\n'] 293*4882a593Smuzhiyun _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-postinst', self.testfile, '-r selftest-recipetool-appendfile', expectedlines, ['testfile']) 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun def test_recipetool_appendfile_extlayer(self): 296*4882a593Smuzhiyun # Try creating a bbappend in a layer that's not in bblayers.conf and has a different structure 297*4882a593Smuzhiyun exttemplayerdir = os.path.join(self.tempdir, 'extlayer') 298*4882a593Smuzhiyun self._create_temp_layer(exttemplayerdir, False, 'oeselftestextlayer', recipepathspec='metadata/recipes/recipes-*/*') 299*4882a593Smuzhiyun result = runCmd('recipetool appendfile %s /usr/share/selftest-replaceme-orig %s' % (exttemplayerdir, self.testfile)) 300*4882a593Smuzhiyun self.assertNotIn('Traceback', result.output) 301*4882a593Smuzhiyun createdfiles = [] 302*4882a593Smuzhiyun for root, _, files in os.walk(exttemplayerdir): 303*4882a593Smuzhiyun for f in files: 304*4882a593Smuzhiyun createdfiles.append(os.path.relpath(os.path.join(root, f), exttemplayerdir)) 305*4882a593Smuzhiyun createdfiles.remove('conf/layer.conf') 306*4882a593Smuzhiyun expectedfiles = ['metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile.bbappend', 307*4882a593Smuzhiyun 'metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile/selftest-replaceme-orig'] 308*4882a593Smuzhiyun self.assertEqual(sorted(createdfiles), sorted(expectedfiles)) 309*4882a593Smuzhiyun 310*4882a593Smuzhiyun def test_recipetool_appendfile_wildcard(self): 311*4882a593Smuzhiyun 312*4882a593Smuzhiyun def try_appendfile_wc(options): 313*4882a593Smuzhiyun result = runCmd('recipetool appendfile %s /etc/profile %s %s' % (self.templayerdir, self.testfile, options)) 314*4882a593Smuzhiyun self.assertNotIn('Traceback', result.output) 315*4882a593Smuzhiyun bbappendfile = None 316*4882a593Smuzhiyun for root, _, files in os.walk(self.templayerdir): 317*4882a593Smuzhiyun for f in files: 318*4882a593Smuzhiyun if f.endswith('.bbappend'): 319*4882a593Smuzhiyun bbappendfile = f 320*4882a593Smuzhiyun break 321*4882a593Smuzhiyun if not bbappendfile: 322*4882a593Smuzhiyun self.fail('No bbappend file created') 323*4882a593Smuzhiyun runCmd('rm -rf %s/recipes-*' % self.templayerdir) 324*4882a593Smuzhiyun return bbappendfile 325*4882a593Smuzhiyun 326*4882a593Smuzhiyun # Check without wildcard option 327*4882a593Smuzhiyun recipefn = os.path.basename(get_bb_var('FILE', 'base-files')) 328*4882a593Smuzhiyun filename = try_appendfile_wc('') 329*4882a593Smuzhiyun self.assertEqual(filename, recipefn.replace('.bb', '.bbappend')) 330*4882a593Smuzhiyun # Now check with wildcard option 331*4882a593Smuzhiyun filename = try_appendfile_wc('-w') 332*4882a593Smuzhiyun self.assertEqual(filename, recipefn.split('_')[0] + '_%.bbappend') 333*4882a593Smuzhiyun 334*4882a593Smuzhiyun 335*4882a593Smuzhiyunclass RecipetoolCreateTests(RecipetoolBase): 336*4882a593Smuzhiyun 337*4882a593Smuzhiyun def test_recipetool_create(self): 338*4882a593Smuzhiyun # Try adding a recipe 339*4882a593Smuzhiyun tempsrc = os.path.join(self.tempdir, 'srctree') 340*4882a593Smuzhiyun os.makedirs(tempsrc) 341*4882a593Smuzhiyun recipefile = os.path.join(self.tempdir, 'logrotate_3.12.3.bb') 342*4882a593Smuzhiyun srcuri = 'https://github.com/logrotate/logrotate/releases/download/3.12.3/logrotate-3.12.3.tar.xz' 343*4882a593Smuzhiyun result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc)) 344*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile)) 345*4882a593Smuzhiyun checkvars = {} 346*4882a593Smuzhiyun checkvars['LICENSE'] = 'GPL-2.0-only' 347*4882a593Smuzhiyun checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' 348*4882a593Smuzhiyun checkvars['SRC_URI'] = 'https://github.com/logrotate/logrotate/releases/download/${PV}/logrotate-${PV}.tar.xz' 349*4882a593Smuzhiyun checkvars['SRC_URI[md5sum]'] = 'a560c57fac87c45b2fc17406cdf79288' 350*4882a593Smuzhiyun checkvars['SRC_URI[sha256sum]'] = '2e6a401cac9024db2288297e3be1a8ab60e7401ba8e91225218aaf4a27e82a07' 351*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, []) 352*4882a593Smuzhiyun 353*4882a593Smuzhiyun def test_recipetool_create_autotools(self): 354*4882a593Smuzhiyun if 'x11' not in get_bb_var('DISTRO_FEATURES'): 355*4882a593Smuzhiyun self.skipTest('Test requires x11 as distro feature') 356*4882a593Smuzhiyun # Ensure we have the right data in shlibs/pkgdata 357*4882a593Smuzhiyun bitbake('libpng pango libx11 libxext jpeg libcheck') 358*4882a593Smuzhiyun # Try adding a recipe 359*4882a593Smuzhiyun tempsrc = os.path.join(self.tempdir, 'srctree') 360*4882a593Smuzhiyun os.makedirs(tempsrc) 361*4882a593Smuzhiyun recipefile = os.path.join(self.tempdir, 'libmatchbox.bb') 362*4882a593Smuzhiyun srcuri = 'git://git.yoctoproject.org/libmatchbox' 363*4882a593Smuzhiyun result = runCmd(['recipetool', 'create', '-o', recipefile, srcuri + ";rev=9f7cf8895ae2d39c465c04cc78e918c157420269", '-x', tempsrc]) 364*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile), 'recipetool did not create recipe file; output:\n%s' % result.output) 365*4882a593Smuzhiyun checkvars = {} 366*4882a593Smuzhiyun checkvars['LICENSE'] = 'LGPL-2.1-only' 367*4882a593Smuzhiyun checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34' 368*4882a593Smuzhiyun checkvars['S'] = '${WORKDIR}/git' 369*4882a593Smuzhiyun checkvars['PV'] = '1.11+git${SRCPV}' 370*4882a593Smuzhiyun checkvars['SRC_URI'] = srcuri + ';branch=master' 371*4882a593Smuzhiyun checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxext', 'pango']) 372*4882a593Smuzhiyun inherits = ['autotools', 'pkgconfig'] 373*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, inherits) 374*4882a593Smuzhiyun 375*4882a593Smuzhiyun def test_recipetool_create_simple(self): 376*4882a593Smuzhiyun # Try adding a recipe 377*4882a593Smuzhiyun temprecipe = os.path.join(self.tempdir, 'recipe') 378*4882a593Smuzhiyun os.makedirs(temprecipe) 379*4882a593Smuzhiyun pv = '1.7.4.1' 380*4882a593Smuzhiyun srcuri = 'http://www.dest-unreach.org/socat/download/Archive/socat-%s.tar.bz2' % pv 381*4882a593Smuzhiyun result = runCmd('recipetool create %s -o %s' % (srcuri, temprecipe)) 382*4882a593Smuzhiyun dirlist = os.listdir(temprecipe) 383*4882a593Smuzhiyun if len(dirlist) > 1: 384*4882a593Smuzhiyun self.fail('recipetool created more than just one file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist))) 385*4882a593Smuzhiyun if len(dirlist) < 1 or not os.path.isfile(os.path.join(temprecipe, dirlist[0])): 386*4882a593Smuzhiyun self.fail('recipetool did not create recipe file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist))) 387*4882a593Smuzhiyun self.assertEqual(dirlist[0], 'socat_%s.bb' % pv, 'Recipe file incorrectly named') 388*4882a593Smuzhiyun checkvars = {} 389*4882a593Smuzhiyun checkvars['LICENSE'] = set(['Unknown', 'GPL-2.0-only']) 390*4882a593Smuzhiyun checkvars['LIC_FILES_CHKSUM'] = set(['file://COPYING.OpenSSL;md5=5c9bccc77f67a8328ef4ebaf468116f4', 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263']) 391*4882a593Smuzhiyun # We don't check DEPENDS since they are variable for this recipe depending on what's in the sysroot 392*4882a593Smuzhiyun checkvars['S'] = None 393*4882a593Smuzhiyun checkvars['SRC_URI'] = srcuri.replace(pv, '${PV}') 394*4882a593Smuzhiyun inherits = ['autotools'] 395*4882a593Smuzhiyun self._test_recipe_contents(os.path.join(temprecipe, dirlist[0]), checkvars, inherits) 396*4882a593Smuzhiyun 397*4882a593Smuzhiyun def test_recipetool_create_cmake(self): 398*4882a593Smuzhiyun temprecipe = os.path.join(self.tempdir, 'recipe') 399*4882a593Smuzhiyun os.makedirs(temprecipe) 400*4882a593Smuzhiyun recipefile = os.path.join(temprecipe, 'taglib_1.11.1.bb') 401*4882a593Smuzhiyun srcuri = 'http://taglib.github.io/releases/taglib-1.11.1.tar.gz' 402*4882a593Smuzhiyun result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 403*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile)) 404*4882a593Smuzhiyun checkvars = {} 405*4882a593Smuzhiyun checkvars['LICENSE'] = set(['LGPL-2.1-only', 'MPL-1.1-only']) 406*4882a593Smuzhiyun checkvars['SRC_URI'] = 'http://taglib.github.io/releases/taglib-${PV}.tar.gz' 407*4882a593Smuzhiyun checkvars['SRC_URI[md5sum]'] = 'cee7be0ccfc892fa433d6c837df9522a' 408*4882a593Smuzhiyun checkvars['SRC_URI[sha256sum]'] = 'b6d1a5a610aae6ff39d93de5efd0fdc787aa9e9dc1e7026fa4c961b26563526b' 409*4882a593Smuzhiyun checkvars['DEPENDS'] = set(['boost', 'zlib']) 410*4882a593Smuzhiyun inherits = ['cmake'] 411*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, inherits) 412*4882a593Smuzhiyun 413*4882a593Smuzhiyun def test_recipetool_create_npm(self): 414*4882a593Smuzhiyun collections = get_bb_var('BBFILE_COLLECTIONS').split() 415*4882a593Smuzhiyun if "openembedded-layer" not in collections: 416*4882a593Smuzhiyun self.skipTest("Test needs meta-oe for nodejs") 417*4882a593Smuzhiyun 418*4882a593Smuzhiyun temprecipe = os.path.join(self.tempdir, 'recipe') 419*4882a593Smuzhiyun os.makedirs(temprecipe) 420*4882a593Smuzhiyun recipefile = os.path.join(temprecipe, 'savoirfairelinux-node-server-example_1.0.0.bb') 421*4882a593Smuzhiyun shrinkwrap = os.path.join(temprecipe, 'savoirfairelinux-node-server-example', 'npm-shrinkwrap.json') 422*4882a593Smuzhiyun srcuri = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 423*4882a593Smuzhiyun result = runCmd('recipetool create -o %s \'%s\'' % (temprecipe, srcuri)) 424*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile)) 425*4882a593Smuzhiyun self.assertTrue(os.path.isfile(shrinkwrap)) 426*4882a593Smuzhiyun checkvars = {} 427*4882a593Smuzhiyun checkvars['SUMMARY'] = 'Node Server Example' 428*4882a593Smuzhiyun checkvars['HOMEPAGE'] = 'https://github.com/savoirfairelinux/node-server-example#readme' 429*4882a593Smuzhiyun checkvars['LICENSE'] = 'BSD-3-Clause & ISC & MIT & Unknown' 430*4882a593Smuzhiyun urls = [] 431*4882a593Smuzhiyun urls.append('npm://registry.npmjs.org/;package=@savoirfairelinux/node-server-example;version=${PV}') 432*4882a593Smuzhiyun urls.append('npmsw://${THISDIR}/${BPN}/npm-shrinkwrap.json') 433*4882a593Smuzhiyun checkvars['SRC_URI'] = set(urls) 434*4882a593Smuzhiyun checkvars['S'] = '${WORKDIR}/npm' 435*4882a593Smuzhiyun checkvars['LICENSE:${PN}'] = 'MIT' 436*4882a593Smuzhiyun checkvars['LICENSE:${PN}-base64'] = 'Unknown' 437*4882a593Smuzhiyun checkvars['LICENSE:${PN}-accepts'] = 'MIT' 438*4882a593Smuzhiyun checkvars['LICENSE:${PN}-inherits'] = 'ISC' 439*4882a593Smuzhiyun inherits = ['npm'] 440*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, inherits) 441*4882a593Smuzhiyun 442*4882a593Smuzhiyun def test_recipetool_create_github(self): 443*4882a593Smuzhiyun # Basic test to see if github URL mangling works 444*4882a593Smuzhiyun temprecipe = os.path.join(self.tempdir, 'recipe') 445*4882a593Smuzhiyun os.makedirs(temprecipe) 446*4882a593Smuzhiyun recipefile = os.path.join(temprecipe, 'meson_git.bb') 447*4882a593Smuzhiyun srcuri = 'https://github.com/mesonbuild/meson;rev=0.32.0' 448*4882a593Smuzhiyun result = runCmd(['recipetool', 'create', '-o', temprecipe, srcuri]) 449*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile)) 450*4882a593Smuzhiyun checkvars = {} 451*4882a593Smuzhiyun checkvars['LICENSE'] = set(['Apache-2.0']) 452*4882a593Smuzhiyun checkvars['SRC_URI'] = 'git://github.com/mesonbuild/meson;protocol=https;branch=master' 453*4882a593Smuzhiyun inherits = ['setuptools3'] 454*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, inherits) 455*4882a593Smuzhiyun 456*4882a593Smuzhiyun def test_recipetool_create_python3_setuptools(self): 457*4882a593Smuzhiyun # Test creating python3 package from tarball (using setuptools3 class) 458*4882a593Smuzhiyun temprecipe = os.path.join(self.tempdir, 'recipe') 459*4882a593Smuzhiyun os.makedirs(temprecipe) 460*4882a593Smuzhiyun pn = 'python-magic' 461*4882a593Smuzhiyun pv = '0.4.15' 462*4882a593Smuzhiyun recipefile = os.path.join(temprecipe, '%s_%s.bb' % (pn, pv)) 463*4882a593Smuzhiyun srcuri = 'https://files.pythonhosted.org/packages/84/30/80932401906eaf787f2e9bd86dc458f1d2e75b064b4c187341f29516945c/python-magic-%s.tar.gz' % pv 464*4882a593Smuzhiyun result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 465*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile)) 466*4882a593Smuzhiyun checkvars = {} 467*4882a593Smuzhiyun checkvars['LICENSE'] = set(['MIT']) 468*4882a593Smuzhiyun checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=16a934f165e8c3245f241e77d401bb88' 469*4882a593Smuzhiyun checkvars['SRC_URI'] = 'https://files.pythonhosted.org/packages/84/30/80932401906eaf787f2e9bd86dc458f1d2e75b064b4c187341f29516945c/python-magic-${PV}.tar.gz' 470*4882a593Smuzhiyun checkvars['SRC_URI[md5sum]'] = 'e384c95a47218f66c6501cd6dd45ff59' 471*4882a593Smuzhiyun checkvars['SRC_URI[sha256sum]'] = 'f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5' 472*4882a593Smuzhiyun inherits = ['setuptools3'] 473*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, inherits) 474*4882a593Smuzhiyun 475*4882a593Smuzhiyun def test_recipetool_create_github_tarball(self): 476*4882a593Smuzhiyun # Basic test to ensure github URL mangling doesn't apply to release tarballs 477*4882a593Smuzhiyun temprecipe = os.path.join(self.tempdir, 'recipe') 478*4882a593Smuzhiyun os.makedirs(temprecipe) 479*4882a593Smuzhiyun pv = '0.32.0' 480*4882a593Smuzhiyun recipefile = os.path.join(temprecipe, 'meson_%s.bb' % pv) 481*4882a593Smuzhiyun srcuri = 'https://github.com/mesonbuild/meson/releases/download/%s/meson-%s.tar.gz' % (pv, pv) 482*4882a593Smuzhiyun result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri)) 483*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile)) 484*4882a593Smuzhiyun checkvars = {} 485*4882a593Smuzhiyun checkvars['LICENSE'] = set(['Apache-2.0']) 486*4882a593Smuzhiyun checkvars['SRC_URI'] = 'https://github.com/mesonbuild/meson/releases/download/${PV}/meson-${PV}.tar.gz' 487*4882a593Smuzhiyun inherits = ['setuptools3'] 488*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, inherits) 489*4882a593Smuzhiyun 490*4882a593Smuzhiyun def _test_recipetool_create_git(self, srcuri, branch=None): 491*4882a593Smuzhiyun # Basic test to check http git URL mangling works 492*4882a593Smuzhiyun temprecipe = os.path.join(self.tempdir, 'recipe') 493*4882a593Smuzhiyun os.makedirs(temprecipe) 494*4882a593Smuzhiyun name = srcuri.split(';')[0].split('/')[-1] 495*4882a593Smuzhiyun recipefile = os.path.join(temprecipe, name + '_git.bb') 496*4882a593Smuzhiyun options = ' -B %s' % branch if branch else '' 497*4882a593Smuzhiyun result = runCmd('recipetool create -o %s%s "%s"' % (temprecipe, options, srcuri)) 498*4882a593Smuzhiyun self.assertTrue(os.path.isfile(recipefile)) 499*4882a593Smuzhiyun checkvars = {} 500*4882a593Smuzhiyun checkvars['SRC_URI'] = srcuri 501*4882a593Smuzhiyun for scheme in ['http', 'https']: 502*4882a593Smuzhiyun if srcuri.startswith(scheme + ":"): 503*4882a593Smuzhiyun checkvars['SRC_URI'] = 'git%s;protocol=%s' % (srcuri[len(scheme):], scheme) 504*4882a593Smuzhiyun if ';branch=' not in srcuri: 505*4882a593Smuzhiyun checkvars['SRC_URI'] += ';branch=' + (branch or 'master') 506*4882a593Smuzhiyun self._test_recipe_contents(recipefile, checkvars, []) 507*4882a593Smuzhiyun 508*4882a593Smuzhiyun def test_recipetool_create_git_http(self): 509*4882a593Smuzhiyun self._test_recipetool_create_git('http://git.yoctoproject.org/git/matchbox-keyboard') 510*4882a593Smuzhiyun 511*4882a593Smuzhiyun def test_recipetool_create_git_srcuri_master(self): 512*4882a593Smuzhiyun self._test_recipetool_create_git('git://git.yoctoproject.org/matchbox-keyboard;branch=master') 513*4882a593Smuzhiyun 514*4882a593Smuzhiyun def test_recipetool_create_git_srcuri_branch(self): 515*4882a593Smuzhiyun self._test_recipetool_create_git('git://git.yoctoproject.org/matchbox-keyboard;branch=matchbox-keyboard-0-1') 516*4882a593Smuzhiyun 517*4882a593Smuzhiyun def test_recipetool_create_git_srcbranch(self): 518*4882a593Smuzhiyun self._test_recipetool_create_git('git://git.yoctoproject.org/matchbox-keyboard', 'matchbox-keyboard-0-1') 519*4882a593Smuzhiyun 520*4882a593Smuzhiyun 521*4882a593Smuzhiyunclass RecipetoolTests(RecipetoolBase): 522*4882a593Smuzhiyun 523*4882a593Smuzhiyun @classmethod 524*4882a593Smuzhiyun def setUpClass(cls): 525*4882a593Smuzhiyun import sys 526*4882a593Smuzhiyun 527*4882a593Smuzhiyun super(RecipetoolTests, cls).setUpClass() 528*4882a593Smuzhiyun bb_vars = get_bb_vars(['BBPATH']) 529*4882a593Smuzhiyun cls.bbpath = bb_vars['BBPATH'] 530*4882a593Smuzhiyun libpath = os.path.join(get_bb_var('COREBASE'), 'scripts', 'lib', 'recipetool') 531*4882a593Smuzhiyun sys.path.insert(0, libpath) 532*4882a593Smuzhiyun 533*4882a593Smuzhiyun def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths): 534*4882a593Smuzhiyun dstdir = basedstdir 535*4882a593Smuzhiyun self.assertTrue(os.path.exists(dstdir)) 536*4882a593Smuzhiyun for p in paths: 537*4882a593Smuzhiyun dstdir = os.path.join(dstdir, p) 538*4882a593Smuzhiyun if not os.path.exists(dstdir): 539*4882a593Smuzhiyun os.makedirs(dstdir) 540*4882a593Smuzhiyun if p == "lib": 541*4882a593Smuzhiyun # Can race with other tests 542*4882a593Smuzhiyun self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir) 543*4882a593Smuzhiyun else: 544*4882a593Smuzhiyun self.track_for_cleanup(dstdir) 545*4882a593Smuzhiyun dstfile = os.path.join(dstdir, os.path.basename(srcfile)) 546*4882a593Smuzhiyun if srcfile != dstfile: 547*4882a593Smuzhiyun shutil.copy(srcfile, dstfile) 548*4882a593Smuzhiyun self.track_for_cleanup(dstfile) 549*4882a593Smuzhiyun 550*4882a593Smuzhiyun def test_recipetool_load_plugin(self): 551*4882a593Smuzhiyun """Test that recipetool loads only the first found plugin in BBPATH.""" 552*4882a593Smuzhiyun 553*4882a593Smuzhiyun recipetool = runCmd("which recipetool") 554*4882a593Smuzhiyun fromname = runCmd("recipetool --quiet pluginfile") 555*4882a593Smuzhiyun srcfile = fromname.output 556*4882a593Smuzhiyun searchpath = self.bbpath.split(':') + [os.path.dirname(recipetool.output)] 557*4882a593Smuzhiyun plugincontent = [] 558*4882a593Smuzhiyun with open(srcfile) as fh: 559*4882a593Smuzhiyun plugincontent = fh.readlines() 560*4882a593Smuzhiyun try: 561*4882a593Smuzhiyun self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found') 562*4882a593Smuzhiyun for path in searchpath: 563*4882a593Smuzhiyun self._copy_file_with_cleanup(srcfile, path, 'lib', 'recipetool') 564*4882a593Smuzhiyun result = runCmd("recipetool --quiet count") 565*4882a593Smuzhiyun self.assertEqual(result.output, '1') 566*4882a593Smuzhiyun result = runCmd("recipetool --quiet multiloaded") 567*4882a593Smuzhiyun self.assertEqual(result.output, "no") 568*4882a593Smuzhiyun for path in searchpath: 569*4882a593Smuzhiyun result = runCmd("recipetool --quiet bbdir") 570*4882a593Smuzhiyun self.assertEqual(result.output, path) 571*4882a593Smuzhiyun os.unlink(os.path.join(result.output, 'lib', 'recipetool', 'bbpath.py')) 572*4882a593Smuzhiyun finally: 573*4882a593Smuzhiyun with open(srcfile, 'w') as fh: 574*4882a593Smuzhiyun fh.writelines(plugincontent) 575*4882a593Smuzhiyun 576*4882a593Smuzhiyun def test_recipetool_handle_license_vars(self): 577*4882a593Smuzhiyun from create import handle_license_vars 578*4882a593Smuzhiyun from unittest.mock import Mock 579*4882a593Smuzhiyun 580*4882a593Smuzhiyun commonlicdir = get_bb_var('COMMON_LICENSE_DIR') 581*4882a593Smuzhiyun 582*4882a593Smuzhiyun class DataConnectorCopy(bb.tinfoil.TinfoilDataStoreConnector): 583*4882a593Smuzhiyun pass 584*4882a593Smuzhiyun 585*4882a593Smuzhiyun d = DataConnectorCopy 586*4882a593Smuzhiyun d.getVar = Mock(return_value=commonlicdir) 587*4882a593Smuzhiyun 588*4882a593Smuzhiyun srctree = tempfile.mkdtemp(prefix='recipetoolqa') 589*4882a593Smuzhiyun self.track_for_cleanup(srctree) 590*4882a593Smuzhiyun 591*4882a593Smuzhiyun # Multiple licenses 592*4882a593Smuzhiyun licenses = ['MIT', 'ISC', 'BSD-3-Clause', 'Apache-2.0'] 593*4882a593Smuzhiyun for licence in licenses: 594*4882a593Smuzhiyun shutil.copy(os.path.join(commonlicdir, licence), os.path.join(srctree, 'LICENSE.' + licence)) 595*4882a593Smuzhiyun # Duplicate license 596*4882a593Smuzhiyun shutil.copy(os.path.join(commonlicdir, 'MIT'), os.path.join(srctree, 'LICENSE')) 597*4882a593Smuzhiyun 598*4882a593Smuzhiyun extravalues = { 599*4882a593Smuzhiyun # Duplicate and missing licenses 600*4882a593Smuzhiyun 'LICENSE': 'Zlib & BSD-2-Clause & Zlib', 601*4882a593Smuzhiyun 'LIC_FILES_CHKSUM': [ 602*4882a593Smuzhiyun 'file://README.md;md5=0123456789abcdef0123456789abcd' 603*4882a593Smuzhiyun ] 604*4882a593Smuzhiyun } 605*4882a593Smuzhiyun lines_before = [] 606*4882a593Smuzhiyun handled = [] 607*4882a593Smuzhiyun licvalues = handle_license_vars(srctree, lines_before, handled, extravalues, d) 608*4882a593Smuzhiyun expected_lines_before = [ 609*4882a593Smuzhiyun '# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is', 610*4882a593Smuzhiyun '# your responsibility to verify that the values are complete and correct.', 611*4882a593Smuzhiyun '# NOTE: Original package / source metadata indicates license is: BSD-2-Clause & Zlib', 612*4882a593Smuzhiyun '#', 613*4882a593Smuzhiyun '# NOTE: multiple licenses have been detected; they have been separated with &', 614*4882a593Smuzhiyun '# in the LICENSE value for now since it is a reasonable assumption that all', 615*4882a593Smuzhiyun '# of the licenses apply. If instead there is a choice between the multiple', 616*4882a593Smuzhiyun '# licenses then you should change the value to separate the licenses with |', 617*4882a593Smuzhiyun '# instead of &. If there is any doubt, check the accompanying documentation', 618*4882a593Smuzhiyun '# to determine which situation is applicable.', 619*4882a593Smuzhiyun 'LICENSE = "Apache-2.0 & BSD-2-Clause & BSD-3-Clause & ISC & MIT & Zlib"', 620*4882a593Smuzhiyun 'LIC_FILES_CHKSUM = "file://LICENSE;md5=0835ade698e0bcf8506ecda2f7b4f302 \\\n' 621*4882a593Smuzhiyun ' file://LICENSE.Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10 \\\n' 622*4882a593Smuzhiyun ' file://LICENSE.BSD-3-Clause;md5=550794465ba0ec5312d6919e203a55f9 \\\n' 623*4882a593Smuzhiyun ' file://LICENSE.ISC;md5=f3b90e78ea0cffb20bf5cca7947a896d \\\n' 624*4882a593Smuzhiyun ' file://LICENSE.MIT;md5=0835ade698e0bcf8506ecda2f7b4f302 \\\n' 625*4882a593Smuzhiyun ' file://README.md;md5=0123456789abcdef0123456789abcd"', 626*4882a593Smuzhiyun '' 627*4882a593Smuzhiyun ] 628*4882a593Smuzhiyun self.assertEqual(lines_before, expected_lines_before) 629*4882a593Smuzhiyun expected_licvalues = [ 630*4882a593Smuzhiyun ('MIT', 'LICENSE', '0835ade698e0bcf8506ecda2f7b4f302'), 631*4882a593Smuzhiyun ('Apache-2.0', 'LICENSE.Apache-2.0', '89aea4e17d99a7cacdbeed46a0096b10'), 632*4882a593Smuzhiyun ('BSD-3-Clause', 'LICENSE.BSD-3-Clause', '550794465ba0ec5312d6919e203a55f9'), 633*4882a593Smuzhiyun ('ISC', 'LICENSE.ISC', 'f3b90e78ea0cffb20bf5cca7947a896d'), 634*4882a593Smuzhiyun ('MIT', 'LICENSE.MIT', '0835ade698e0bcf8506ecda2f7b4f302') 635*4882a593Smuzhiyun ] 636*4882a593Smuzhiyun self.assertEqual(handled, [('license', expected_licvalues)]) 637*4882a593Smuzhiyun self.assertEqual(extravalues, {}) 638*4882a593Smuzhiyun self.assertEqual(licvalues, expected_licvalues) 639*4882a593Smuzhiyun 640*4882a593Smuzhiyun 641*4882a593Smuzhiyun def test_recipetool_split_pkg_licenses(self): 642*4882a593Smuzhiyun from create import split_pkg_licenses 643*4882a593Smuzhiyun licvalues = [ 644*4882a593Smuzhiyun # Duplicate licenses 645*4882a593Smuzhiyun ('BSD-2-Clause', 'x/COPYING', None), 646*4882a593Smuzhiyun ('BSD-2-Clause', 'x/LICENSE', None), 647*4882a593Smuzhiyun # Multiple licenses 648*4882a593Smuzhiyun ('MIT', 'x/a/LICENSE.MIT', None), 649*4882a593Smuzhiyun ('ISC', 'x/a/LICENSE.ISC', None), 650*4882a593Smuzhiyun # Alternative licenses 651*4882a593Smuzhiyun ('(MIT | ISC)', 'x/b/LICENSE', None), 652*4882a593Smuzhiyun # Alternative licenses without brackets 653*4882a593Smuzhiyun ('MIT | BSD-2-Clause', 'x/c/LICENSE', None), 654*4882a593Smuzhiyun # Multi licenses with alternatives 655*4882a593Smuzhiyun ('MIT', 'x/d/COPYING', None), 656*4882a593Smuzhiyun ('MIT | BSD-2-Clause', 'x/d/LICENSE', None), 657*4882a593Smuzhiyun # Multi licenses with alternatives and brackets 658*4882a593Smuzhiyun ('Apache-2.0 & ((MIT | ISC) & BSD-3-Clause)', 'x/e/LICENSE', None) 659*4882a593Smuzhiyun ] 660*4882a593Smuzhiyun packages = { 661*4882a593Smuzhiyun '${PN}': '', 662*4882a593Smuzhiyun 'a': 'x/a', 663*4882a593Smuzhiyun 'b': 'x/b', 664*4882a593Smuzhiyun 'c': 'x/c', 665*4882a593Smuzhiyun 'd': 'x/d', 666*4882a593Smuzhiyun 'e': 'x/e', 667*4882a593Smuzhiyun 'f': 'x/f', 668*4882a593Smuzhiyun 'g': 'x/g', 669*4882a593Smuzhiyun } 670*4882a593Smuzhiyun fallback_licenses = { 671*4882a593Smuzhiyun # Ignored 672*4882a593Smuzhiyun 'a': 'BSD-3-Clause', 673*4882a593Smuzhiyun # Used 674*4882a593Smuzhiyun 'f': 'BSD-3-Clause' 675*4882a593Smuzhiyun } 676*4882a593Smuzhiyun outlines = [] 677*4882a593Smuzhiyun outlicenses = split_pkg_licenses(licvalues, packages, outlines, fallback_licenses) 678*4882a593Smuzhiyun expected_outlicenses = { 679*4882a593Smuzhiyun '${PN}': ['BSD-2-Clause'], 680*4882a593Smuzhiyun 'a': ['ISC', 'MIT'], 681*4882a593Smuzhiyun 'b': ['(ISC | MIT)'], 682*4882a593Smuzhiyun 'c': ['(BSD-2-Clause | MIT)'], 683*4882a593Smuzhiyun 'd': ['(BSD-2-Clause | MIT)', 'MIT'], 684*4882a593Smuzhiyun 'e': ['(ISC | MIT)', 'Apache-2.0', 'BSD-3-Clause'], 685*4882a593Smuzhiyun 'f': ['BSD-3-Clause'], 686*4882a593Smuzhiyun 'g': ['Unknown'] 687*4882a593Smuzhiyun } 688*4882a593Smuzhiyun self.assertEqual(outlicenses, expected_outlicenses) 689*4882a593Smuzhiyun expected_outlines = [ 690*4882a593Smuzhiyun 'LICENSE:${PN} = "BSD-2-Clause"', 691*4882a593Smuzhiyun 'LICENSE:a = "ISC & MIT"', 692*4882a593Smuzhiyun 'LICENSE:b = "(ISC | MIT)"', 693*4882a593Smuzhiyun 'LICENSE:c = "(BSD-2-Clause | MIT)"', 694*4882a593Smuzhiyun 'LICENSE:d = "(BSD-2-Clause | MIT) & MIT"', 695*4882a593Smuzhiyun 'LICENSE:e = "(ISC | MIT) & Apache-2.0 & BSD-3-Clause"', 696*4882a593Smuzhiyun 'LICENSE:f = "BSD-3-Clause"', 697*4882a593Smuzhiyun 'LICENSE:g = "Unknown"' 698*4882a593Smuzhiyun ] 699*4882a593Smuzhiyun self.assertEqual(outlines, expected_outlines) 700*4882a593Smuzhiyun 701*4882a593Smuzhiyun 702*4882a593Smuzhiyunclass RecipetoolAppendsrcBase(RecipetoolBase): 703*4882a593Smuzhiyun def _try_recipetool_appendsrcfile(self, testrecipe, newfile, destfile, options, expectedlines, expectedfiles): 704*4882a593Smuzhiyun cmd = 'recipetool appendsrcfile %s %s %s %s %s' % (options, self.templayerdir, testrecipe, newfile, destfile) 705*4882a593Smuzhiyun return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines) 706*4882a593Smuzhiyun 707*4882a593Smuzhiyun def _try_recipetool_appendsrcfiles(self, testrecipe, newfiles, expectedlines=None, expectedfiles=None, destdir=None, options=''): 708*4882a593Smuzhiyun 709*4882a593Smuzhiyun if destdir: 710*4882a593Smuzhiyun options += ' -D %s' % destdir 711*4882a593Smuzhiyun 712*4882a593Smuzhiyun if expectedfiles is None: 713*4882a593Smuzhiyun expectedfiles = [os.path.basename(f) for f in newfiles] 714*4882a593Smuzhiyun 715*4882a593Smuzhiyun cmd = 'recipetool appendsrcfiles %s %s %s %s' % (options, self.templayerdir, testrecipe, ' '.join(newfiles)) 716*4882a593Smuzhiyun return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines) 717*4882a593Smuzhiyun 718*4882a593Smuzhiyun def _try_recipetool_appendsrcfile_fail(self, testrecipe, newfile, destfile, checkerror): 719*4882a593Smuzhiyun cmd = 'recipetool appendsrcfile %s %s %s %s' % (self.templayerdir, testrecipe, newfile, destfile or '') 720*4882a593Smuzhiyun result = runCmd(cmd, ignore_status=True) 721*4882a593Smuzhiyun self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd) 722*4882a593Smuzhiyun self.assertNotIn('Traceback', result.output) 723*4882a593Smuzhiyun for errorstr in checkerror: 724*4882a593Smuzhiyun self.assertIn(errorstr, result.output) 725*4882a593Smuzhiyun 726*4882a593Smuzhiyun @staticmethod 727*4882a593Smuzhiyun def _get_first_file_uri(recipe): 728*4882a593Smuzhiyun '''Return the first file:// in SRC_URI for the specified recipe.''' 729*4882a593Smuzhiyun src_uri = get_bb_var('SRC_URI', recipe).split() 730*4882a593Smuzhiyun for uri in src_uri: 731*4882a593Smuzhiyun p = urllib.parse.urlparse(uri) 732*4882a593Smuzhiyun if p.scheme == 'file': 733*4882a593Smuzhiyun return p.netloc + p.path 734*4882a593Smuzhiyun 735*4882a593Smuzhiyun def _test_appendsrcfile(self, testrecipe, filename=None, destdir=None, has_src_uri=True, srcdir=None, newfile=None, options=''): 736*4882a593Smuzhiyun if newfile is None: 737*4882a593Smuzhiyun newfile = self.testfile 738*4882a593Smuzhiyun 739*4882a593Smuzhiyun if srcdir: 740*4882a593Smuzhiyun if destdir: 741*4882a593Smuzhiyun expected_subdir = os.path.join(srcdir, destdir) 742*4882a593Smuzhiyun else: 743*4882a593Smuzhiyun expected_subdir = srcdir 744*4882a593Smuzhiyun else: 745*4882a593Smuzhiyun options += " -W" 746*4882a593Smuzhiyun expected_subdir = destdir 747*4882a593Smuzhiyun 748*4882a593Smuzhiyun if filename: 749*4882a593Smuzhiyun if destdir: 750*4882a593Smuzhiyun destpath = os.path.join(destdir, filename) 751*4882a593Smuzhiyun else: 752*4882a593Smuzhiyun destpath = filename 753*4882a593Smuzhiyun else: 754*4882a593Smuzhiyun filename = os.path.basename(newfile) 755*4882a593Smuzhiyun if destdir: 756*4882a593Smuzhiyun destpath = destdir + os.sep 757*4882a593Smuzhiyun else: 758*4882a593Smuzhiyun destpath = '.' + os.sep 759*4882a593Smuzhiyun 760*4882a593Smuzhiyun expectedlines = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', 761*4882a593Smuzhiyun '\n'] 762*4882a593Smuzhiyun if has_src_uri: 763*4882a593Smuzhiyun uri = 'file://%s' % filename 764*4882a593Smuzhiyun if expected_subdir: 765*4882a593Smuzhiyun uri += ';subdir=%s' % expected_subdir 766*4882a593Smuzhiyun expectedlines[0:0] = ['SRC_URI += "%s"\n' % uri, 767*4882a593Smuzhiyun '\n'] 768*4882a593Smuzhiyun 769*4882a593Smuzhiyun return self._try_recipetool_appendsrcfile(testrecipe, newfile, destpath, options, expectedlines, [filename]) 770*4882a593Smuzhiyun 771*4882a593Smuzhiyun def _test_appendsrcfiles(self, testrecipe, newfiles, expectedfiles=None, destdir=None, options=''): 772*4882a593Smuzhiyun if expectedfiles is None: 773*4882a593Smuzhiyun expectedfiles = [os.path.basename(n) for n in newfiles] 774*4882a593Smuzhiyun 775*4882a593Smuzhiyun self._try_recipetool_appendsrcfiles(testrecipe, newfiles, expectedfiles=expectedfiles, destdir=destdir, options=options) 776*4882a593Smuzhiyun 777*4882a593Smuzhiyun bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'FILESEXTRAPATHS'], testrecipe) 778*4882a593Smuzhiyun src_uri = bb_vars['SRC_URI'].split() 779*4882a593Smuzhiyun for f in expectedfiles: 780*4882a593Smuzhiyun if destdir: 781*4882a593Smuzhiyun self.assertIn('file://%s;subdir=%s' % (f, destdir), src_uri) 782*4882a593Smuzhiyun else: 783*4882a593Smuzhiyun self.assertIn('file://%s' % f, src_uri) 784*4882a593Smuzhiyun 785*4882a593Smuzhiyun recipefile = bb_vars['FILE'] 786*4882a593Smuzhiyun bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir) 787*4882a593Smuzhiyun filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe) 788*4882a593Smuzhiyun filesextrapaths = bb_vars['FILESEXTRAPATHS'].split(':') 789*4882a593Smuzhiyun self.assertIn(filesdir, filesextrapaths) 790*4882a593Smuzhiyun 791*4882a593Smuzhiyun 792*4882a593Smuzhiyun 793*4882a593Smuzhiyun 794*4882a593Smuzhiyunclass RecipetoolAppendsrcTests(RecipetoolAppendsrcBase): 795*4882a593Smuzhiyun 796*4882a593Smuzhiyun def test_recipetool_appendsrcfile_basic(self): 797*4882a593Smuzhiyun self._test_appendsrcfile('base-files', 'a-file') 798*4882a593Smuzhiyun 799*4882a593Smuzhiyun def test_recipetool_appendsrcfile_basic_wildcard(self): 800*4882a593Smuzhiyun testrecipe = 'base-files' 801*4882a593Smuzhiyun self._test_appendsrcfile(testrecipe, 'a-file', options='-w') 802*4882a593Smuzhiyun recipefile = get_bb_var('FILE', testrecipe) 803*4882a593Smuzhiyun bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir) 804*4882a593Smuzhiyun self.assertEqual(os.path.basename(bbappendfile), '%s_%%.bbappend' % testrecipe) 805*4882a593Smuzhiyun 806*4882a593Smuzhiyun def test_recipetool_appendsrcfile_subdir_basic(self): 807*4882a593Smuzhiyun self._test_appendsrcfile('base-files', 'a-file', 'tmp') 808*4882a593Smuzhiyun 809*4882a593Smuzhiyun def test_recipetool_appendsrcfile_subdir_basic_dirdest(self): 810*4882a593Smuzhiyun self._test_appendsrcfile('base-files', destdir='tmp') 811*4882a593Smuzhiyun 812*4882a593Smuzhiyun def test_recipetool_appendsrcfile_srcdir_basic(self): 813*4882a593Smuzhiyun testrecipe = 'bash' 814*4882a593Smuzhiyun bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe) 815*4882a593Smuzhiyun srcdir = bb_vars['S'] 816*4882a593Smuzhiyun workdir = bb_vars['WORKDIR'] 817*4882a593Smuzhiyun subdir = os.path.relpath(srcdir, workdir) 818*4882a593Smuzhiyun self._test_appendsrcfile(testrecipe, 'a-file', srcdir=subdir) 819*4882a593Smuzhiyun 820*4882a593Smuzhiyun def test_recipetool_appendsrcfile_existing_in_src_uri(self): 821*4882a593Smuzhiyun testrecipe = 'base-files' 822*4882a593Smuzhiyun filepath = self._get_first_file_uri(testrecipe) 823*4882a593Smuzhiyun self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe) 824*4882a593Smuzhiyun self._test_appendsrcfile(testrecipe, filepath, has_src_uri=False) 825*4882a593Smuzhiyun 826*4882a593Smuzhiyun def test_recipetool_appendsrcfile_existing_in_src_uri_diff_params(self): 827*4882a593Smuzhiyun testrecipe = 'base-files' 828*4882a593Smuzhiyun subdir = 'tmp' 829*4882a593Smuzhiyun filepath = self._get_first_file_uri(testrecipe) 830*4882a593Smuzhiyun self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe) 831*4882a593Smuzhiyun 832*4882a593Smuzhiyun output = self._test_appendsrcfile(testrecipe, filepath, subdir, has_src_uri=False) 833*4882a593Smuzhiyun self.assertTrue(any('with different parameters' in l for l in output)) 834*4882a593Smuzhiyun 835*4882a593Smuzhiyun def test_recipetool_appendsrcfile_replace_file_srcdir(self): 836*4882a593Smuzhiyun testrecipe = 'bash' 837*4882a593Smuzhiyun filepath = 'Makefile.in' 838*4882a593Smuzhiyun bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe) 839*4882a593Smuzhiyun srcdir = bb_vars['S'] 840*4882a593Smuzhiyun workdir = bb_vars['WORKDIR'] 841*4882a593Smuzhiyun subdir = os.path.relpath(srcdir, workdir) 842*4882a593Smuzhiyun 843*4882a593Smuzhiyun self._test_appendsrcfile(testrecipe, filepath, srcdir=subdir) 844*4882a593Smuzhiyun bitbake('%s:do_unpack' % testrecipe) 845*4882a593Smuzhiyun with open(self.testfile, 'r') as testfile: 846*4882a593Smuzhiyun with open(os.path.join(srcdir, filepath), 'r') as makefilein: 847*4882a593Smuzhiyun self.assertEqual(testfile.read(), makefilein.read()) 848*4882a593Smuzhiyun 849*4882a593Smuzhiyun def test_recipetool_appendsrcfiles_basic(self, destdir=None): 850*4882a593Smuzhiyun newfiles = [self.testfile] 851*4882a593Smuzhiyun for i in range(1, 5): 852*4882a593Smuzhiyun testfile = os.path.join(self.tempdir, 'testfile%d' % i) 853*4882a593Smuzhiyun with open(testfile, 'w') as f: 854*4882a593Smuzhiyun f.write('Test file %d\n' % i) 855*4882a593Smuzhiyun newfiles.append(testfile) 856*4882a593Smuzhiyun self._test_appendsrcfiles('gcc', newfiles, destdir=destdir, options='-W') 857*4882a593Smuzhiyun 858*4882a593Smuzhiyun def test_recipetool_appendsrcfiles_basic_subdir(self): 859*4882a593Smuzhiyun self.test_recipetool_appendsrcfiles_basic(destdir='testdir') 860