1"""
2 pygments.lexers.bitbake
3 ~~~~~~~~~~~~~~~~~~~~~~~
4
5 Lexer for BitBake recipes, classes, includes and configuration files
6 used by the Yocto Project / OpenEmbedded build system.
7
8 :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
9 :license: BSD, see LICENSE for details.
10"""
11
12import re
13
14from pygments.lexer import RegexLexer, bygroups, include, using, words
15from pygments.lexers.python import PythonLexer
16from pygments.lexers.shell import BashLexer
17from pygments.token import Comment, Keyword, Name, Operator, Punctuation, \
18 String, Text, Whitespace
19
20__all__ = ['BitBakeLexer']
21
22# A bare BitBake identifier (variable, function or flag name). Allows the
23# characters used by OE-Core variable names (digits, ``-``, ``.``, ``+``).
24_IDENT = r'[A-Za-z_][A-Za-z0-9_\-.+]*'
25
26# Optional OE override chain such as ``:append``, ``:remove``, ``:class-target``
27# or ``:${PN}-doc``. Anchored so it only consumes ``:foo`` runs and never
28# eats the leading ``:`` of the ``:=`` assignment operator.
29_OVERRIDE = r'(?::[A-Za-z0-9_\-.+${}]+)*'
30
31# All BitBake variable assignment operators, ordered so that the longer
32# operators win the regex alternation.
33_ASSIGN = r'(?:\?\?=|\?=|:=|\+=|=\+|\.=|=\.|=)'
34
35
36class BitBakeLexer(RegexLexer):
37 """
38 Lexer for BitBake recipes, classes, includes and configuration files
39 used by the Yocto Project and OpenEmbedded build system.
40 """
41
42 name = 'BitBake'
43 url = 'https://docs.yoctoproject.org/bitbake/'
44 aliases = ['bitbake']
45 filenames = ['*.bbclass', '*.bbappend']
46 mimetypes = ['text/x-bitbake']
47 version_added = '2.21'
48
49 flags = re.MULTILINE
50
51 tokens = {
52 'root': [
53 (r'[ \t]+', Whitespace),
54 (r'\n', Whitespace),
55 (r'#.*$', Comment.Single),
56
57 # ``python [name]() { ... }`` blocks (also ``fakeroot python``).
58 # Must be tried before the generic shell function rule so the
59 # ``python`` keyword is not mistaken for a shell function name.
60 (r'(^(?:fakeroot[ \t]+)?)(python)((?:[ \t]+' + _IDENT + r')?)'
61 r'([ \t]*\([ \t]*\)[ \t]*)(\{[ \t]*\n)'
62 r'((?:.*\n)*?)'
63 r'(^\}[ \t]*$)',
64 bygroups(Keyword.Type, Keyword, Name.Function, Text,
65 Punctuation, using(PythonLexer), Punctuation)),
66
67 # Shell task bodies: ``[fakeroot ]name[:override]() { ... }``.
68 (r'(^(?:fakeroot[ \t]+)?)(' + _IDENT + r')(' + _OVERRIDE + r')'
69 r'([ \t]*\([ \t]*\)[ \t]*)(\{[ \t]*\n)'
70 r'((?:.*\n)*?)'
71 r'(^\}[ \t]*$)',
72 bygroups(Keyword.Type, Name.Function, Name.Decorator, Text,
73 Punctuation, using(BashLexer), Punctuation)),
74
75 # Top-level python ``def`` blocks; the body is any run of
76 # indented or blank lines following the signature.
77 (r'^def[ \t]+' + _IDENT + r'[ \t]*\([^)]*\)[ \t]*:[ \t]*\n'
78 r'(?:[ \t]+.*\n|\n)+',
79 using(PythonLexer)),
80
81 # ``inherit`` / ``inherit_defer`` / ``include`` / ``include_all`` /
82 # ``require`` directives. Longer keywords are listed first so the
83 # regex alternation does not match the shorter prefix.
84 (r'^(inherit_defer|inherit|include_all|include|require)\b',
85 Keyword.Namespace, 'include-line'),
86
87 # ``addtask`` / ``deltask`` / ``addhandler`` / ``EXPORT_FUNCTIONS``.
88 (r'^(addtask|deltask|addhandler|EXPORT_FUNCTIONS)\b',
89 Keyword, 'statement'),
90
91 # ``VAR[flag] = "value"`` (varflag assignment).
92 (r'^(' + _IDENT + r')(\[)(' + _IDENT + r')(\])([ \t]*)('
93 + _ASSIGN + r')',
94 bygroups(Name.Variable, Punctuation, Name.Attribute,
95 Punctuation, Whitespace, Operator),
96 'value'),
97
98 # ``[export ]VAR[:override...] OP "value"`` assignments.
99 (r'^(export[ \t]+)?(' + _IDENT + r')(' + _OVERRIDE + r')'
100 r'([ \t]*)(' + _ASSIGN + r')',
101 bygroups(Keyword.Type, Name.Variable, Name.Decorator,
102 Whitespace, Operator),
103 'value'),
104
105 # Anything else: consume runs of "boring" characters in one
106 # token, then fall back to a single character if needed.
107 (r'[^\s#${}\[\]:=+?.@\\"\']+', Text),
108 (r'.', Text),
109 ],
110
111 'include-line': [
112 (r'[ \t]+', Whitespace),
113 (r'\\\n', Text),
114 (r'\n', Whitespace, '#pop'),
115 include('interp'),
116 (r'[^\s$]+', String),
117 ],
118
119 'statement': [
120 (r'[ \t]+', Whitespace),
121 (r'\\\n', Text),
122 (r'\n', Whitespace, '#pop'),
123 (words(('after', 'before'), suffix=r'\b'), Keyword),
124 include('interp'),
125 (r'[^\s$\\]+', Name),
126 ],
127
128 'value': [
129 (r'[ \t]+', Whitespace),
130 (r'\\\n', String.Escape),
131 (r'\n', Whitespace, '#pop'),
132 (r'"', String.Double, 'string-double'),
133 (r"'", String.Single, 'string-single'),
134 include('interp'),
135 (r'[^\s"\'$\\]+', String),
136 ],
137
138 'string-double': [
139 (r'\\\n', String.Escape),
140 (r'\\.', String.Escape),
141 (r'"', String.Double, '#pop'),
142 include('interp'),
143 (r'[^"\\$]+', String.Double),
144 ],
145
146 'string-single': [
147 (r'\\\n', String.Escape),
148 (r'\\.', String.Escape),
149 (r"'", String.Single, '#pop'),
150 include('interp'),
151 (r"[^'\\$]+", String.Single),
152 ],
153
154 'interp': [
155 # ``${@ python expression }`` evaluated by BitBake at parse time.
156 (r'\$\{@', String.Interpol, 'py-interp'),
157 # ``${VAR}`` variable expansion.
158 (r'(\$\{)([A-Za-z0-9_\-:.+/]+)(\})',
159 bygroups(String.Interpol, Name.Variable, String.Interpol)),
160 ],
161
162 'py-interp': [
163 (r'\}', String.Interpol, '#pop'),
164 (r'[^}]+', using(PythonLexer)),
165 ],
166 }