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