summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/parse/parse_py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/parse/parse_py')
-rw-r--r--bitbake/lib/bb/parse/parse_py/BBHandler.py267
-rw-r--r--bitbake/lib/bb/parse/parse_py/ConfHandler.py189
-rw-r--r--bitbake/lib/bb/parse/parse_py/__init__.py33
3 files changed, 489 insertions, 0 deletions
diff --git a/bitbake/lib/bb/parse/parse_py/BBHandler.py b/bitbake/lib/bb/parse/parse_py/BBHandler.py
new file mode 100644
index 0000000000..408890e48a
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/BBHandler.py
@@ -0,0 +1,267 @@
1#!/usr/bin/env python
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4"""
5 class for handling .bb files
6
7 Reads a .bb file and obtains its metadata
8
9"""
10
11
12# Copyright (C) 2003, 2004 Chris Larson
13# Copyright (C) 2003, 2004 Phil Blundell
14#
15# This program is free software; you can redistribute it and/or modify
16# it under the terms of the GNU General Public License version 2 as
17# published by the Free Software Foundation.
18#
19# This program is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22# GNU General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License along
25# with this program; if not, write to the Free Software Foundation, Inc.,
26# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27
28from __future__ import absolute_import
29import re, bb, os
30import logging
31import bb.build, bb.utils
32from bb import data
33
34from . import ConfHandler
35from .. import resolve_file, ast, logger
36from .ConfHandler import include, init
37
38# For compatibility
39bb.deprecate_import(__name__, "bb.parse", ["vars_from_file"])
40
41__func_start_regexp__ = re.compile( r"(((?P<py>python)|(?P<fr>fakeroot))\s*)*(?P<func>[\w\.\-\+\{\}\$]+)?\s*\(\s*\)\s*{$" )
42__inherit_regexp__ = re.compile( r"inherit\s+(.+)" )
43__export_func_regexp__ = re.compile( r"EXPORT_FUNCTIONS\s+(.+)" )
44__addtask_regexp__ = re.compile("addtask\s+(?P<func>\w+)\s*((before\s*(?P<before>((.*(?=after))|(.*))))|(after\s*(?P<after>((.*(?=before))|(.*)))))*")
45__deltask_regexp__ = re.compile("deltask\s+(?P<func>\w+)")
46__addhandler_regexp__ = re.compile( r"addhandler\s+(.+)" )
47__def_regexp__ = re.compile( r"def\s+(\w+).*:" )
48__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" )
49
50
51__infunc__ = ""
52__inpython__ = False
53__body__ = []
54__classname__ = ""
55
56cached_statements = {}
57
58# We need to indicate EOF to the feeder. This code is so messy that
59# factoring it out to a close_parse_file method is out of question.
60# We will use the IN_PYTHON_EOF as an indicator to just close the method
61#
62# The two parts using it are tightly integrated anyway
63IN_PYTHON_EOF = -9999999999999
64
65
66
67def supports(fn, d):
68 """Return True if fn has a supported extension"""
69 return os.path.splitext(fn)[-1] in [".bb", ".bbclass", ".inc"]
70
71def inherit(files, fn, lineno, d):
72 __inherit_cache = d.getVar('__inherit_cache') or []
73 files = d.expand(files).split()
74 for file in files:
75 if not os.path.isabs(file) and not file.endswith(".bbclass"):
76 file = os.path.join('classes', '%s.bbclass' % file)
77
78 if not os.path.isabs(file):
79 dname = os.path.dirname(fn)
80 bbpath = "%s:%s" % (dname, d.getVar("BBPATH", True))
81 abs_fn, attempts = bb.utils.which(bbpath, file, history=True)
82 for af in attempts:
83 if af != abs_fn:
84 bb.parse.mark_dependency(d, af)
85 if abs_fn:
86 file = abs_fn
87
88 if not file in __inherit_cache:
89 logger.log(logging.DEBUG -1, "BB %s:%d: inheriting %s", fn, lineno, file)
90 __inherit_cache.append( file )
91 d.setVar('__inherit_cache', __inherit_cache)
92 include(fn, file, lineno, d, "inherit")
93 __inherit_cache = d.getVar('__inherit_cache') or []
94
95def get_statements(filename, absolute_filename, base_name):
96 global cached_statements
97
98 try:
99 return cached_statements[absolute_filename]
100 except KeyError:
101 file = open(absolute_filename, 'r')
102 statements = ast.StatementGroup()
103
104 lineno = 0
105 while True:
106 lineno = lineno + 1
107 s = file.readline()
108 if not s: break
109 s = s.rstrip()
110 feeder(lineno, s, filename, base_name, statements)
111 file.close()
112 if __inpython__:
113 # add a blank line to close out any python definition
114 feeder(IN_PYTHON_EOF, "", filename, base_name, statements)
115
116 if filename.endswith(".bbclass") or filename.endswith(".inc"):
117 cached_statements[absolute_filename] = statements
118 return statements
119
120def handle(fn, d, include):
121 global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__, __classname__
122 __body__ = []
123 __infunc__ = ""
124 __classname__ = ""
125 __residue__ = []
126
127
128 if include == 0:
129 logger.debug(2, "BB %s: handle(data)", fn)
130 else:
131 logger.debug(2, "BB %s: handle(data, include)", fn)
132
133 base_name = os.path.basename(fn)
134 (root, ext) = os.path.splitext(base_name)
135 init(d)
136
137 if ext == ".bbclass":
138 __classname__ = root
139 __inherit_cache = d.getVar('__inherit_cache') or []
140 if not fn in __inherit_cache:
141 __inherit_cache.append(fn)
142 d.setVar('__inherit_cache', __inherit_cache)
143
144 if include != 0:
145 oldfile = d.getVar('FILE')
146 else:
147 oldfile = None
148
149 abs_fn = resolve_file(fn, d)
150
151 if include:
152 bb.parse.mark_dependency(d, abs_fn)
153
154 # actual loading
155 statements = get_statements(fn, abs_fn, base_name)
156
157 # DONE WITH PARSING... time to evaluate
158 if ext != ".bbclass":
159 d.setVar('FILE', abs_fn)
160
161 try:
162 statements.eval(d)
163 except bb.parse.SkipPackage:
164 bb.data.setVar("__SKIPPED", True, d)
165 if include == 0:
166 return { "" : d }
167
168 if ext != ".bbclass" and include == 0:
169 return ast.multi_finalize(fn, d)
170
171 if oldfile:
172 d.setVar("FILE", oldfile)
173
174 return d
175
176def feeder(lineno, s, fn, root, statements):
177 global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__, __infunc__, __body__, bb, __residue__, __classname__
178 if __infunc__:
179 if s == '}':
180 __body__.append('')
181 ast.handleMethod(statements, fn, lineno, __infunc__, __body__)
182 __infunc__ = ""
183 __body__ = []
184 else:
185 __body__.append(s)
186 return
187
188 if __inpython__:
189 m = __python_func_regexp__.match(s)
190 if m and lineno != IN_PYTHON_EOF:
191 __body__.append(s)
192 return
193 else:
194 ast.handlePythonMethod(statements, fn, lineno, __inpython__,
195 root, __body__)
196 __body__ = []
197 __inpython__ = False
198
199 if lineno == IN_PYTHON_EOF:
200 return
201
202 if s and s[0] == '#':
203 if len(__residue__) != 0 and __residue__[0][0] != "#":
204 bb.fatal("There is a comment on line %s of file %s (%s) which is in the middle of a multiline expression.\nBitbake used to ignore these but no longer does so, please fix your metadata as errors are likely as a result of this change." % (lineno, fn, s))
205
206 if len(__residue__) != 0 and __residue__[0][0] == "#" and (not s or s[0] != "#"):
207 bb.fatal("There is a confusing multiline, partially commented expression on line %s of file %s (%s).\nPlease clarify whether this is all a comment or should be parsed." % (lineno, fn, s))
208
209 if s and s[-1] == '\\':
210 __residue__.append(s[:-1])
211 return
212
213 s = "".join(__residue__) + s
214 __residue__ = []
215
216 # Skip empty lines
217 if s == '':
218 return
219
220 # Skip comments
221 if s[0] == '#':
222 return
223
224 m = __func_start_regexp__.match(s)
225 if m:
226 __infunc__ = m.group("func") or "__anonymous"
227 ast.handleMethodFlags(statements, fn, lineno, __infunc__, m)
228 return
229
230 m = __def_regexp__.match(s)
231 if m:
232 __body__.append(s)
233 __inpython__ = m.group(1)
234
235 return
236
237 m = __export_func_regexp__.match(s)
238 if m:
239 ast.handleExportFuncs(statements, fn, lineno, m, __classname__)
240 return
241
242 m = __addtask_regexp__.match(s)
243 if m:
244 ast.handleAddTask(statements, fn, lineno, m)
245 return
246
247 m = __deltask_regexp__.match(s)
248 if m:
249 ast.handleDelTask(statements, fn, lineno, m)
250 return
251
252 m = __addhandler_regexp__.match(s)
253 if m:
254 ast.handleBBHandlers(statements, fn, lineno, m)
255 return
256
257 m = __inherit_regexp__.match(s)
258 if m:
259 ast.handleInherit(statements, fn, lineno, m)
260 return
261
262 return ConfHandler.feeder(lineno, s, fn, statements)
263
264# Add us to the handlers list
265from .. import handlers
266handlers.append({'supports': supports, 'handle': handle, 'init': init})
267del handlers
diff --git a/bitbake/lib/bb/parse/parse_py/ConfHandler.py b/bitbake/lib/bb/parse/parse_py/ConfHandler.py
new file mode 100644
index 0000000000..978ebe4608
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/ConfHandler.py
@@ -0,0 +1,189 @@
1#!/usr/bin/env python
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4"""
5 class for handling configuration data files
6
7 Reads a .conf file and obtains its metadata
8
9"""
10
11# Copyright (C) 2003, 2004 Chris Larson
12# Copyright (C) 2003, 2004 Phil Blundell
13#
14# This program is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License version 2 as
16# published by the Free Software Foundation.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License along
24# with this program; if not, write to the Free Software Foundation, Inc.,
25# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26
27import re, os
28import logging
29import bb.utils
30from bb.parse import ParseError, resolve_file, ast, logger
31
32__config_regexp__ = re.compile( r"""
33 ^
34 (?P<exp>export\s*)?
35 (?P<var>[a-zA-Z0-9\-~_+.${}/]+?)
36 (\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?
37
38 \s* (
39 (?P<colon>:=) |
40 (?P<lazyques>\?\?=) |
41 (?P<ques>\?=) |
42 (?P<append>\+=) |
43 (?P<prepend>=\+) |
44 (?P<predot>=\.) |
45 (?P<postdot>\.=) |
46 =
47 ) \s*
48
49 (?!'[^']*'[^']*'$)
50 (?!\"[^\"]*\"[^\"]*\"$)
51 (?P<apo>['\"])
52 (?P<value>.*)
53 (?P=apo)
54 $
55 """, re.X)
56__include_regexp__ = re.compile( r"include\s+(.+)" )
57__require_regexp__ = re.compile( r"require\s+(.+)" )
58__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/]+)$" )
59
60def init(data):
61 topdir = data.getVar('TOPDIR')
62 if not topdir:
63 data.setVar('TOPDIR', os.getcwd())
64
65
66def supports(fn, d):
67 return fn[-5:] == ".conf"
68
69def include(oldfn, fn, lineno, data, error_out):
70 """
71 error_out: A string indicating the verb (e.g. "include", "inherit") to be
72 used in a ParseError that will be raised if the file to be included could
73 not be included. Specify False to avoid raising an error in this case.
74 """
75 if oldfn == fn: # prevent infinite recursion
76 return None
77
78 import bb
79 fn = data.expand(fn)
80 oldfn = data.expand(oldfn)
81
82 if not os.path.isabs(fn):
83 dname = os.path.dirname(oldfn)
84 bbpath = "%s:%s" % (dname, data.getVar("BBPATH", True))
85 abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
86 if abs_fn and bb.parse.check_dependency(data, abs_fn):
87 bb.warn("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE', True)))
88 for af in attempts:
89 bb.parse.mark_dependency(data, af)
90 if abs_fn:
91 fn = abs_fn
92 elif bb.parse.check_dependency(data, fn):
93 bb.warn("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE', True)))
94
95 from bb.parse import handle
96 try:
97 ret = handle(fn, data, True)
98 except (IOError, OSError):
99 if error_out:
100 raise ParseError("Could not %(error_out)s file %(fn)s" % vars(), oldfn, lineno)
101 logger.debug(2, "CONF file '%s' not found", fn)
102 bb.parse.mark_dependency(data, fn)
103
104# We have an issue where a UI might want to enforce particular settings such as
105# an empty DISTRO variable. If configuration files do something like assigning
106# a weak default, it turns out to be very difficult to filter out these changes,
107# particularly when the weak default might appear half way though parsing a chain
108# of configuration files. We therefore let the UIs hook into configuration file
109# parsing. This turns out to be a hard problem to solve any other way.
110confFilters = []
111
112def handle(fn, data, include):
113 init(data)
114
115 if include == 0:
116 oldfile = None
117 else:
118 oldfile = data.getVar('FILE')
119
120 abs_fn = resolve_file(fn, data)
121 f = open(abs_fn, 'r')
122
123 if include:
124 bb.parse.mark_dependency(data, abs_fn)
125
126 statements = ast.StatementGroup()
127 lineno = 0
128 while True:
129 lineno = lineno + 1
130 s = f.readline()
131 if not s:
132 break
133 w = s.strip()
134 # skip empty lines
135 if not w:
136 continue
137 s = s.rstrip()
138 while s[-1] == '\\':
139 s2 = f.readline().strip()
140 lineno = lineno + 1
141 if (not s2 or s2 and s2[0] != "#") and s[0] == "#" :
142 bb.fatal("There is a confusing multiline, partially commented expression on line %s of file %s (%s).\nPlease clarify whether this is all a comment or should be parsed." % (lineno, fn, s))
143 s = s[:-1] + s2
144 # skip comments
145 if s[0] == '#':
146 continue
147 feeder(lineno, s, abs_fn, statements)
148
149 # DONE WITH PARSING... time to evaluate
150 data.setVar('FILE', abs_fn)
151 statements.eval(data)
152 if oldfile:
153 data.setVar('FILE', oldfile)
154
155 f.close()
156
157 for f in confFilters:
158 f(fn, data)
159
160 return data
161
162def feeder(lineno, s, fn, statements):
163 m = __config_regexp__.match(s)
164 if m:
165 groupd = m.groupdict()
166 ast.handleData(statements, fn, lineno, groupd)
167 return
168
169 m = __include_regexp__.match(s)
170 if m:
171 ast.handleInclude(statements, fn, lineno, m, False)
172 return
173
174 m = __require_regexp__.match(s)
175 if m:
176 ast.handleInclude(statements, fn, lineno, m, True)
177 return
178
179 m = __export_regexp__.match(s)
180 if m:
181 ast.handleExport(statements, fn, lineno, m)
182 return
183
184 raise ParseError("unparsed line: '%s'" % s, fn, lineno);
185
186# Add us to the handlers list
187from bb.parse import handlers
188handlers.append({'supports': supports, 'handle': handle, 'init': init})
189del handlers
diff --git a/bitbake/lib/bb/parse/parse_py/__init__.py b/bitbake/lib/bb/parse/parse_py/__init__.py
new file mode 100644
index 0000000000..3e658d0de9
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/__init__.py
@@ -0,0 +1,33 @@
1#!/usr/bin/env python
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4"""
5BitBake Parsers
6
7File parsers for the BitBake build tools.
8
9"""
10
11# Copyright (C) 2003, 2004 Chris Larson
12# Copyright (C) 2003, 2004 Phil Blundell
13#
14# This program is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License version 2 as
16# published by the Free Software Foundation.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License along
24# with this program; if not, write to the Free Software Foundation, Inc.,
25# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26#
27# Based on functions from the base bb module, Copyright 2003 Holger Schurig
28
29from __future__ import absolute_import
30from . import ConfHandler
31from . import BBHandler
32
33__version__ = '1.0'