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.py261
-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, 483 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..9633340d1b
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/BBHandler.py
@@ -0,0 +1,261 @@
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.debug(1, "Inheriting %s (from %s:%d)" % (file, fn, lineno))
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 base_name = os.path.basename(fn)
128 (root, ext) = os.path.splitext(base_name)
129 init(d)
130
131 if ext == ".bbclass":
132 __classname__ = root
133 __inherit_cache = d.getVar('__inherit_cache') or []
134 if not fn in __inherit_cache:
135 __inherit_cache.append(fn)
136 d.setVar('__inherit_cache', __inherit_cache)
137
138 if include != 0:
139 oldfile = d.getVar('FILE')
140 else:
141 oldfile = None
142
143 abs_fn = resolve_file(fn, d)
144
145 if include:
146 bb.parse.mark_dependency(d, abs_fn)
147
148 # actual loading
149 statements = get_statements(fn, abs_fn, base_name)
150
151 # DONE WITH PARSING... time to evaluate
152 if ext != ".bbclass":
153 d.setVar('FILE', abs_fn)
154
155 try:
156 statements.eval(d)
157 except bb.parse.SkipRecipe:
158 bb.data.setVar("__SKIPPED", True, d)
159 if include == 0:
160 return { "" : d }
161
162 if ext != ".bbclass" and include == 0:
163 return ast.multi_finalize(fn, d)
164
165 if oldfile:
166 d.setVar("FILE", oldfile)
167
168 return d
169
170def feeder(lineno, s, fn, root, statements):
171 global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__, __infunc__, __body__, bb, __residue__, __classname__
172 if __infunc__:
173 if s == '}':
174 __body__.append('')
175 ast.handleMethod(statements, fn, lineno, __infunc__, __body__)
176 __infunc__ = ""
177 __body__ = []
178 else:
179 __body__.append(s)
180 return
181
182 if __inpython__:
183 m = __python_func_regexp__.match(s)
184 if m and lineno != IN_PYTHON_EOF:
185 __body__.append(s)
186 return
187 else:
188 ast.handlePythonMethod(statements, fn, lineno, __inpython__,
189 root, __body__)
190 __body__ = []
191 __inpython__ = False
192
193 if lineno == IN_PYTHON_EOF:
194 return
195
196 if s and s[0] == '#':
197 if len(__residue__) != 0 and __residue__[0][0] != "#":
198 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))
199
200 if len(__residue__) != 0 and __residue__[0][0] == "#" and (not s or s[0] != "#"):
201 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))
202
203 if s and s[-1] == '\\':
204 __residue__.append(s[:-1])
205 return
206
207 s = "".join(__residue__) + s
208 __residue__ = []
209
210 # Skip empty lines
211 if s == '':
212 return
213
214 # Skip comments
215 if s[0] == '#':
216 return
217
218 m = __func_start_regexp__.match(s)
219 if m:
220 __infunc__ = m.group("func") or "__anonymous"
221 ast.handleMethodFlags(statements, fn, lineno, __infunc__, m)
222 return
223
224 m = __def_regexp__.match(s)
225 if m:
226 __body__.append(s)
227 __inpython__ = m.group(1)
228
229 return
230
231 m = __export_func_regexp__.match(s)
232 if m:
233 ast.handleExportFuncs(statements, fn, lineno, m, __classname__)
234 return
235
236 m = __addtask_regexp__.match(s)
237 if m:
238 ast.handleAddTask(statements, fn, lineno, m)
239 return
240
241 m = __deltask_regexp__.match(s)
242 if m:
243 ast.handleDelTask(statements, fn, lineno, m)
244 return
245
246 m = __addhandler_regexp__.match(s)
247 if m:
248 ast.handleBBHandlers(statements, fn, lineno, m)
249 return
250
251 m = __inherit_regexp__.match(s)
252 if m:
253 ast.handleInherit(statements, fn, lineno, m)
254 return
255
256 return ConfHandler.feeder(lineno, s, fn, statements)
257
258# Add us to the handlers list
259from .. import handlers
260handlers.append({'supports': supports, 'handle': handle, 'init': init})
261del 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'