xref: /openbmc/openbmc/poky/bitbake/lib/bb/tests/parse.py (revision 705982a5)
1#
2# BitBake Test for lib/bb/parse/
3#
4# Copyright (C) 2015 Richard Purdie
5#
6# SPDX-License-Identifier: GPL-2.0-only
7#
8
9import unittest
10import tempfile
11import logging
12import bb
13import os
14
15logger = logging.getLogger('BitBake.TestParse')
16
17import bb.parse
18import bb.data
19import bb.siggen
20
21class ParseTest(unittest.TestCase):
22
23    testfile = """
24A = "1"
25B = "2"
26do_install() {
27	echo "hello"
28}
29
30C = "3"
31"""
32
33    def setUp(self):
34        self.origdir = os.getcwd()
35        self.d = bb.data.init()
36        bb.parse.siggen = bb.siggen.init(self.d)
37
38    def tearDown(self):
39        os.chdir(self.origdir)
40
41    def parsehelper(self, content, suffix = ".bb"):
42
43        f = tempfile.NamedTemporaryFile(suffix = suffix)
44        f.write(bytes(content, "utf-8"))
45        f.flush()
46        os.chdir(os.path.dirname(f.name))
47        return f
48
49    def test_parse_simple(self):
50        f = self.parsehelper(self.testfile)
51        d = bb.parse.handle(f.name, self.d)['']
52        self.assertEqual(d.getVar("A"), "1")
53        self.assertEqual(d.getVar("B"), "2")
54        self.assertEqual(d.getVar("C"), "3")
55
56    def test_parse_incomplete_function(self):
57        testfileB = self.testfile.replace("}", "")
58        f = self.parsehelper(testfileB)
59        with self.assertRaises(bb.parse.ParseError):
60            d = bb.parse.handle(f.name, self.d)['']
61
62    unsettest = """
63A = "1"
64B = "2"
65B[flag] = "3"
66
67unset A
68unset B[flag]
69"""
70
71    def test_parse_unset(self):
72        f = self.parsehelper(self.unsettest)
73        d = bb.parse.handle(f.name, self.d)['']
74        self.assertEqual(d.getVar("A"), None)
75        self.assertEqual(d.getVarFlag("A","flag"), None)
76        self.assertEqual(d.getVar("B"), "2")
77
78    exporttest = """
79A = "a"
80export B = "b"
81export C
82exportD = "d"
83"""
84
85    def test_parse_exports(self):
86        f = self.parsehelper(self.exporttest)
87        d = bb.parse.handle(f.name, self.d)['']
88        self.assertEqual(d.getVar("A"), "a")
89        self.assertIsNone(d.getVarFlag("A", "export"))
90        self.assertEqual(d.getVar("B"), "b")
91        self.assertEqual(d.getVarFlag("B", "export"), 1)
92        self.assertIsNone(d.getVar("C"))
93        self.assertEqual(d.getVarFlag("C", "export"), 1)
94        self.assertIsNone(d.getVar("D"))
95        self.assertIsNone(d.getVarFlag("D", "export"))
96        self.assertEqual(d.getVar("exportD"), "d")
97        self.assertIsNone(d.getVarFlag("exportD", "export"))
98
99
100    overridetest = """
101RRECOMMENDS:${PN} = "a"
102RRECOMMENDS:${PN}:libc = "b"
103OVERRIDES = "libc:${PN}"
104PN = "gtk+"
105"""
106
107    def test_parse_overrides(self):
108        f = self.parsehelper(self.overridetest)
109        d = bb.parse.handle(f.name, self.d)['']
110        self.assertEqual(d.getVar("RRECOMMENDS"), "b")
111        bb.data.expandKeys(d)
112        self.assertEqual(d.getVar("RRECOMMENDS"), "b")
113        d.setVar("RRECOMMENDS:gtk+", "c")
114        self.assertEqual(d.getVar("RRECOMMENDS"), "c")
115
116    overridetest2 = """
117EXTRA_OECONF = ""
118EXTRA_OECONF:class-target = "b"
119EXTRA_OECONF:append = " c"
120"""
121
122    def test_parse_overrides2(self):
123        f = self.parsehelper(self.overridetest2)
124        d = bb.parse.handle(f.name, self.d)['']
125        d.appendVar("EXTRA_OECONF", " d")
126        d.setVar("OVERRIDES", "class-target")
127        self.assertEqual(d.getVar("EXTRA_OECONF"), "b c d")
128
129    overridetest3 = """
130DESCRIPTION = "A"
131DESCRIPTION:${PN}-dev = "${DESCRIPTION} B"
132PN = "bc"
133"""
134
135    def test_parse_combinations(self):
136        f = self.parsehelper(self.overridetest3)
137        d = bb.parse.handle(f.name, self.d)['']
138        bb.data.expandKeys(d)
139        self.assertEqual(d.getVar("DESCRIPTION:bc-dev"), "A B")
140        d.setVar("DESCRIPTION", "E")
141        d.setVar("DESCRIPTION:bc-dev", "C D")
142        d.setVar("OVERRIDES", "bc-dev")
143        self.assertEqual(d.getVar("DESCRIPTION"), "C D")
144
145
146    classextend = """
147VAR_var:override1 = "B"
148EXTRA = ":override1"
149OVERRIDES = "nothing${EXTRA}"
150
151BBCLASSEXTEND = "###CLASS###"
152"""
153    classextend_bbclass = """
154EXTRA = ""
155python () {
156    d.renameVar("VAR_var", "VAR_var2")
157}
158"""
159
160    #
161    # Test based upon a real world data corruption issue. One
162    # data store changing a variable poked through into a different data
163    # store. This test case replicates that issue where the value 'B' would
164    # become unset/disappear.
165    #
166    def test_parse_classextend_contamination(self):
167        self.d.setVar("__bbclasstype", "recipe")
168        cls = self.parsehelper(self.classextend_bbclass, suffix=".bbclass")
169        #clsname = os.path.basename(cls.name).replace(".bbclass", "")
170        self.classextend = self.classextend.replace("###CLASS###", cls.name)
171        f = self.parsehelper(self.classextend)
172        alldata = bb.parse.handle(f.name, self.d)
173        d1 = alldata['']
174        d2 = alldata[cls.name]
175        self.assertEqual(d1.getVar("VAR_var"), "B")
176        self.assertEqual(d2.getVar("VAR_var"), None)
177
178    addtask_deltask = """
179addtask do_patch after do_foo after do_unpack before do_configure before do_compile
180addtask do_fetch do_patch
181
182MYVAR = "do_patch"
183EMPTYVAR = ""
184deltask do_fetch ${MYVAR} ${EMPTYVAR}
185deltask ${EMPTYVAR}
186"""
187    def test_parse_addtask_deltask(self):
188        import sys
189
190        with self.assertLogs() as logs:
191            f = self.parsehelper(self.addtask_deltask)
192            d = bb.parse.handle(f.name, self.d)['']
193
194        output = "".join(logs.output)
195        self.assertTrue("addtask contained multiple 'before' keywords" in output)
196        self.assertTrue("addtask contained multiple 'after' keywords" in output)
197        self.assertTrue('addtask ignored: " do_patch"' in output)
198        #self.assertTrue('dependent task do_foo for do_patch does not exist' in output)
199
200    broken_multiline_comment = """
201# First line of comment \\
202# Second line of comment \\
203
204"""
205    def test_parse_broken_multiline_comment(self):
206        f = self.parsehelper(self.broken_multiline_comment)
207        with self.assertRaises(bb.BBHandledException):
208            d = bb.parse.handle(f.name, self.d)['']
209
210
211    comment_in_var = """
212VAR = " \\
213    SOMEVAL \\
214#   some comment \\
215    SOMEOTHERVAL \\
216"
217"""
218    def test_parse_comment_in_var(self):
219        f = self.parsehelper(self.comment_in_var)
220        with self.assertRaises(bb.BBHandledException):
221            d = bb.parse.handle(f.name, self.d)['']
222
223
224    at_sign_in_var_flag = """
225A[flag@.service] = "nonet"
226B[flag@.target] = "ntb"
227C[f] = "flag"
228
229unset A[flag@.service]
230"""
231    def test_parse_at_sign_in_var_flag(self):
232        f = self.parsehelper(self.at_sign_in_var_flag)
233        d = bb.parse.handle(f.name, self.d)['']
234        self.assertEqual(d.getVar("A"), None)
235        self.assertEqual(d.getVar("B"), None)
236        self.assertEqual(d.getVarFlag("A","flag@.service"), None)
237        self.assertEqual(d.getVarFlag("B","flag@.target"), "ntb")
238        self.assertEqual(d.getVarFlag("C","f"), "flag")
239
240    def test_parse_invalid_at_sign_in_var_flag(self):
241        invalid_at_sign = self.at_sign_in_var_flag.replace("B[f", "B[@f")
242        f = self.parsehelper(invalid_at_sign)
243        with self.assertRaises(bb.parse.ParseError):
244            d = bb.parse.handle(f.name, self.d)['']
245
246    export_function_recipe = """
247inherit someclass
248"""
249
250    export_function_recipe2 = """
251inherit someclass
252
253do_compile () {
254    false
255}
256
257python do_compilepython () {
258    bb.note("Something else")
259}
260
261"""
262    export_function_class = """
263someclass_do_compile() {
264    true
265}
266
267python someclass_do_compilepython () {
268    bb.note("Something")
269}
270
271EXPORT_FUNCTIONS do_compile do_compilepython
272"""
273
274    export_function_class2 = """
275secondclass_do_compile() {
276    true
277}
278
279python secondclass_do_compilepython () {
280    bb.note("Something")
281}
282
283EXPORT_FUNCTIONS do_compile do_compilepython
284"""
285
286    def test_parse_export_functions(self):
287        def check_function_flags(d):
288            self.assertEqual(d.getVarFlag("do_compile", "func"), 1)
289            self.assertEqual(d.getVarFlag("do_compilepython", "func"), 1)
290            self.assertEqual(d.getVarFlag("do_compile", "python"), None)
291            self.assertEqual(d.getVarFlag("do_compilepython", "python"), "1")
292
293        with tempfile.TemporaryDirectory() as tempdir:
294            self.d.setVar("__bbclasstype", "recipe")
295            recipename = tempdir + "/recipe.bb"
296            os.makedirs(tempdir + "/classes")
297            with open(tempdir + "/classes/someclass.bbclass", "w") as f:
298                f.write(self.export_function_class)
299                f.flush()
300            with open(tempdir + "/classes/secondclass.bbclass", "w") as f:
301                f.write(self.export_function_class2)
302                f.flush()
303
304            with open(recipename, "w") as f:
305                f.write(self.export_function_recipe)
306                f.flush()
307            os.chdir(tempdir)
308            d = bb.parse.handle(recipename, bb.data.createCopy(self.d))['']
309            self.assertIn("someclass_do_compile", d.getVar("do_compile"))
310            self.assertIn("someclass_do_compilepython", d.getVar("do_compilepython"))
311            check_function_flags(d)
312
313            recipename2 = tempdir + "/recipe2.bb"
314            with open(recipename2, "w") as f:
315                f.write(self.export_function_recipe2)
316                f.flush()
317
318            d = bb.parse.handle(recipename2, bb.data.createCopy(self.d))['']
319            self.assertNotIn("someclass_do_compile", d.getVar("do_compile"))
320            self.assertNotIn("someclass_do_compilepython", d.getVar("do_compilepython"))
321            self.assertIn("false", d.getVar("do_compile"))
322            self.assertIn("else", d.getVar("do_compilepython"))
323            check_function_flags(d)
324
325            with open(recipename, "a+") as f:
326                f.write("\ninherit secondclass\n")
327                f.flush()
328            with open(recipename2, "a+") as f:
329                f.write("\ninherit secondclass\n")
330                f.flush()
331
332            d = bb.parse.handle(recipename, bb.data.createCopy(self.d))['']
333            self.assertIn("secondclass_do_compile", d.getVar("do_compile"))
334            self.assertIn("secondclass_do_compilepython", d.getVar("do_compilepython"))
335            check_function_flags(d)
336
337            d = bb.parse.handle(recipename2, bb.data.createCopy(self.d))['']
338            self.assertNotIn("someclass_do_compile", d.getVar("do_compile"))
339            self.assertNotIn("someclass_do_compilepython", d.getVar("do_compilepython"))
340            self.assertIn("false", d.getVar("do_compile"))
341            self.assertIn("else", d.getVar("do_compilepython"))
342            check_function_flags(d)
343
344