Add support for the syntax "export VARIABLE"
[bitbake.git] / lib / bb / parse / parse_py / ConfHandler.py
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
27 import re, bb.data, os, sys
28 from bb.parse import ParseError
29
30 #__config_regexp__  = re.compile( r"(?P<exp>export\s*)?(?P<var>[a-zA-Z0-9\-_+.${}]+)\s*(?P<colon>:)?(?P<ques>\?)?=\s*(?P<apo>['\"]?)(?P<value>.*)(?P=apo)$")
31 __config_regexp__  = re.compile( r"(?P<exp>export\s*)?(?P<var>[a-zA-Z0-9\-_+.${}/]+)(\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?\s*((?P<colon>:=)|(?P<ques>\?=)|(?P<append>\+=)|(?P<prepend>=\+)|(?P<predot>=\.)|(?P<postdot>\.=)|=)\s*(?P<apo>['\"]?)(?P<value>.*)(?P=apo)$")
32 __include_regexp__ = re.compile( r"include\s+(.+)" )
33 __require_regexp__ = re.compile( r"require\s+(.+)" )
34 __export_regexp__ = re.compile( r"export\s+(.+)" )
35
36 def init(data):
37     if not bb.data.getVar('TOPDIR', data):
38         bb.data.setVar('TOPDIR', os.getcwd(), data)
39     if not bb.data.getVar('BBPATH', data):
40         bb.data.setVar('BBPATH', os.path.join(sys.prefix, 'share', 'bitbake'), data)
41
42 def supports(fn, d):
43     return localpath(fn, d)[-5:] == ".conf"
44
45 def localpath(fn, d):
46     if os.path.exists(fn):
47         return fn
48
49     if "://" not in fn:
50         return fn
51
52     localfn = None
53     try:
54         localfn = bb.fetch.localpath(fn, d, False)
55     except bb.MalformedUrl:
56         pass
57
58     if not localfn:
59         return fn
60     return localfn
61
62 def obtain(fn, data):
63     import sys, bb
64     fn = bb.data.expand(fn, data)
65     localfn = bb.data.expand(localpath(fn, data), data)
66
67     if localfn != fn:
68         dldir = bb.data.getVar('DL_DIR', data, 1)
69         if not dldir:
70             bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: DL_DIR not defined")
71             return localfn
72         bb.mkdirhier(dldir)
73         try:
74             bb.fetch.init([fn], data)
75         except bb.fetch.NoMethodError:
76             (type, value, traceback) = sys.exc_info()
77             bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: no method: %s" % value)
78             return localfn
79
80         try:
81             bb.fetch.go(data)
82         except bb.fetch.MissingParameterError:
83             (type, value, traceback) = sys.exc_info()
84             bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: missing parameters: %s" % value)
85             return localfn
86         except bb.fetch.FetchError:
87             (type, value, traceback) = sys.exc_info()
88             bb.msg.debug(1, bb.msg.domain.Parsing, "obtain: failed: %s" % value)
89             return localfn
90     return localfn
91
92
93 def include(oldfn, fn, data, error_out):
94     """
95
96     error_out If True a ParseError will be reaised if the to be included
97     """
98     if oldfn == fn: # prevent infinate recursion
99         return None
100
101     import bb
102     fn = bb.data.expand(fn, data)
103     oldfn = bb.data.expand(oldfn, data)
104
105     from bb.parse import handle
106     try:
107         ret = handle(fn, data, True)
108     except IOError:
109         if error_out:
110             raise ParseError("Could not %(error_out)s file %(fn)s" % vars() )
111         bb.msg.debug(2, bb.msg.domain.Parsing, "CONF file '%s' not found" % fn)
112
113 def handle(fn, data, include = 0):
114     if include:
115         inc_string = "including"
116     else:
117         inc_string = "reading"
118     init(data)
119
120     if include == 0:
121         bb.data.inheritFromOS(data)
122         oldfile = None
123     else:
124         oldfile = bb.data.getVar('FILE', data)
125
126     fn = obtain(fn, data)
127     if not os.path.isabs(fn):
128         f = None
129         bbpath = bb.data.getVar("BBPATH", data, 1) or []
130         for p in bbpath.split(":"):
131             currname = os.path.join(p, fn)
132             if os.access(currname, os.R_OK):
133                 f = open(currname, 'r')
134                 abs_fn = currname
135                 bb.msg.debug(2, bb.msg.domain.Parsing, "CONF %s %s" % (inc_string, currname))
136                 break
137         if f is None:
138             raise IOError("file '%s' not found" % fn)
139     else:
140         f = open(fn,'r')
141         bb.msg.debug(1, bb.msg.domain.Parsing, "CONF %s %s" % (inc_string,fn))
142         abs_fn = fn
143
144     if include:
145         bb.parse.mark_dependency(data, abs_fn)
146
147     lineno = 0
148     bb.data.setVar('FILE', fn, data)
149     while 1:
150         lineno = lineno + 1
151         s = f.readline()
152         if not s: break
153         w = s.strip()
154         if not w: continue          # skip empty lines
155         s = s.rstrip()
156         if s[0] == '#': continue    # skip comments
157         while s[-1] == '\\':
158             s2 = f.readline()[:-1].strip()
159             lineno = lineno + 1
160             s = s[:-1] + s2
161         feeder(lineno, s, fn, data)
162
163     if oldfile:
164         bb.data.setVar('FILE', oldfile, data)
165     return data
166
167 def feeder(lineno, s, fn, data):
168     def getFunc(groupd, key, data):
169         if 'flag' in groupd and groupd['flag'] != None:
170             return bb.data.getVarFlag(key, groupd['flag'], data)
171         else:
172             return bb.data.getVar(key, data)
173
174     m = __config_regexp__.match(s)
175     if m:
176         groupd = m.groupdict()
177         key = groupd["var"]
178         if "exp" in groupd and groupd["exp"] != None:
179             bb.data.setVarFlag(key, "export", 1, data)
180         if "ques" in groupd and groupd["ques"] != None:
181             val = getFunc(groupd, key, data)
182             if val == None:
183                 val = groupd["value"]
184         elif "colon" in groupd and groupd["colon"] != None:
185             e = data.createCopy()
186             bb.data.update_data(e)
187             val = bb.data.expand(groupd["value"], e)
188         elif "append" in groupd and groupd["append"] != None:
189             val = "%s %s" % ((getFunc(groupd, key, data) or ""), groupd["value"])
190         elif "prepend" in groupd and groupd["prepend"] != None:
191             val = "%s %s" % (groupd["value"], (getFunc(groupd, key, data) or ""))
192         elif "postdot" in groupd and groupd["postdot"] != None:
193             val = "%s%s" % ((getFunc(groupd, key, data) or ""), groupd["value"])
194         elif "predot" in groupd and groupd["predot"] != None:
195             val = "%s%s" % (groupd["value"], (getFunc(groupd, key, data) or ""))
196         else:
197             val = groupd["value"]
198         if 'flag' in groupd and groupd['flag'] != None:
199             bb.msg.debug(3, bb.msg.domain.Parsing, "setVarFlag(%s, %s, %s, data)" % (key, groupd['flag'], val))
200             bb.data.setVarFlag(key, groupd['flag'], val, data)
201         else:
202             bb.data.setVar(key, val, data)
203         return
204
205     m = __include_regexp__.match(s)
206     if m:
207         s = bb.data.expand(m.group(1), data)
208         bb.msg.debug(3, bb.msg.domain.Parsing, "CONF %s:%d: including %s" % (fn, lineno, s))
209         include(fn, s, data, False)
210         return
211
212     m = __require_regexp__.match(s)
213     if m:
214         s = bb.data.expand(m.group(1), data)
215         include(fn, s, data, "include required")
216         return
217
218     m = __export_regexp__.match(s)
219     if m:
220         bb.data.setVarFlag(m.group(1), "export", 1, data)
221         return
222
223     raise ParseError("%s:%d: unparsed line: '%s'" % (fn, lineno, s));
224
225 # Add us to the handlers list
226 from bb.parse import handlers
227 handlers.append({'supports': supports, 'handle': handle, 'init': init})
228 del handlers