blob: 9d9b5f02d71fea090f5919cb7e8ecbd6d449b390 [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001# this file contains definitions related to the Linux kernel itself
2#
3
4# list here the macros that you know are always defined/undefined when including
5# the kernel headers
6#
7import sys, cpp, re, os.path, string, time
8from defaults import *
9
10verboseSearch = 0
11verboseFind = 0
12
13########################################################################
14########################################################################
15##### #####
16##### H E A D E R S C A N N E R #####
17##### #####
18########################################################################
19########################################################################
20
21
22class HeaderScanner:
23 """a class used to non-recursively detect which Linux kernel headers are
24 used by a given set of input source files"""
25
26 # to use the HeaderScanner, do the following:
27 #
28 # scanner = HeaderScanner()
29 # for path in <your list of files>:
30 # scanner.parseFile(path)
31 #
32 # # get the set of Linux headers included by your files
33 # headers = scanner.getHeaders()
34 #
35 # # get the set of of input files that do include Linux headers
36 # files = scanner.getFiles()
37 #
38 # note that the result of getHeaders() is a set of strings, each one
39 # corresponding to a non-bracketed path name, e.g.:
40 #
41 # set("linux/types","asm/types.h")
42 #
43
44 # the default algorithm is pretty smart and will analyze the input
45 # files with a custom C pre-processor in order to optimize out macros,
46 # get rid of comments, empty lines, etc..
47 #
48 # this avoids many annoying false positives... !!
49 #
50
51 # this regular expression is used to detect include paths that relate to
52 # the kernel, by default, it selects one of:
53 # <linux/*>
54 # <asm/*>
55 # <asm-generic/*>
56 # <mtd/*>
57 #
58 re_combined =\
59 re.compile(r"^.*<((%s)/[\d\w_\+\.\-/]*)>.*$" % string.join(kernel_dirs,"|") )
60 # some kernel files choose to include files with relative paths (x86 32/64
61 # dispatch for instance)
62 re_rel_dir = re.compile(r'^.*"([\d\w_\+\.\-/]+)".*$')
63
64 def __init__(self,config={}):
65 """initialize a HeaderScanner"""
66 self.reset()
67 self.config = config
68
69 def reset(self,config={}):
70 self.files = set() # set of files being parsed for headers
71 self.headers = {} # maps headers to set of users
72 self.config = config
73
74 def checkInclude(self, line, from_file, kernel_root=None):
75 relative = False
76 m = HeaderScanner.re_combined.match(line)
77 if kernel_root and not m:
78 m = HeaderScanner.re_rel_dir.match(line)
79 relative = True
80 if not m: return
81
82 header = m.group(1)
83 if from_file:
84 self.files.add(from_file)
85 if kernel_root and relative:
86 hdr_dir = os.path.realpath(os.path.dirname(from_file))
87 hdr_dir = hdr_dir.replace("%s/" % os.path.realpath(kernel_root),
88 "")
89 if hdr_dir:
90 _prefix = "%s/" % hdr_dir
91 else:
92 _prefix = ""
93 header = "%s%s" % (_prefix, header)
94
95 if not header in self.headers:
96 self.headers[header] = set()
97
98 if from_file:
99 if verboseFind:
100 print "=== %s uses %s" % (from_file, header)
101 self.headers[header].add(from_file)
102
103 def parseFile(self, path, arch=None, kernel_root=None):
104 """parse a given file for Linux headers"""
105 if not os.path.exists(path):
106 return
107
108 # since tokenizing the file is very slow, we first try a quick grep
109 # to see if this returns any meaningful results. only if this is true
110 # do we do the tokenization"""
111 try:
112 f = open(path, "rt")
113 except:
114 print "!!! can't read '%s'" % path
115 return
116
117 hasIncludes = False
118 for line in f:
119 if (HeaderScanner.re_combined.match(line) or
120 (kernel_root and HeaderScanner.re_rel_dir.match(line))):
121 hasIncludes = True
122 break
123
124 if not hasIncludes:
125 if verboseSearch: print "::: " + path
126 return
127
128 if verboseSearch: print "*** " + path
129
130 list = cpp.BlockParser().parseFile(path)
131 if list:
132 #list.removePrefixed("CONFIG_",self.config)
133 macros = kernel_known_macros.copy()
134 if kernel_root:
135 macros.update(self.config)
136 if arch and arch in kernel_default_arch_macros:
137 macros.update(kernel_default_arch_macros[arch])
138 list.optimizeMacros(macros)
139 list.optimizeIf01()
140 includes = list.findIncludes()
141 for inc in includes:
142 self.checkInclude(inc, path, kernel_root)
143
144 def getHeaders(self):
145 """return the set of all needed kernel headers"""
146 return set(self.headers.keys())
147
148 def getHeaderUsers(self,header):
149 """return the set of all users for a given header"""
150 return set(self.headers.get(header))
151
152 def getAllUsers(self):
153 """return a dictionary mapping heaaders to their user set"""
154 return self.headers.copy()
155
156 def getFiles(self):
157 """returns the set of files that do include kernel headers"""
158 return self.files.copy()
159
160
161##########################################################################
162##########################################################################
163##### #####
164##### H E A D E R F I N D E R #####
165##### #####
166##########################################################################
167##########################################################################
168
169
170class KernelHeaderFinder:
171 """a class used to scan the kernel headers themselves."""
172
173 # this is different
174 # from a HeaderScanner because we need to translate the path returned by
175 # HeaderScanner.getHeaders() into possibly architecture-specific ones.
176 #
177 # for example, <asm/XXXX.h> needs to be translated in <asm-ARCH/XXXX.h>
178 # where ARCH is appropriately chosen
179
180 # here's how to use this:
181 #
182 # scanner = HeaderScanner()
183 # for path in <your list of user sources>:
184 # scanner.parseFile(path)
185 #
186 # used_headers = scanner.getHeaders()
187 # finder = KernelHeaderFinder(used_headers, [ "arm", "x86" ],
188 # "<kernel_include_path>")
189 # all_headers = finder.scanForAllArchs()
190 #
191 # not that the result of scanForAllArchs() is a list of relative
192 # header paths that are not bracketed
193 #
194
195 def __init__(self,headers,archs,kernel_root,kernel_config):
196 """init a KernelHeaderScanner,
197
198 'headers' is a list or set of headers,
199 'archs' is a list of architectures
200 'kernel_root' is the path to the 'include' directory
201 of your original kernel sources
202 """
203
204 if len(kernel_root) > 0 and kernel_root[-1] != "/":
205 kernel_root += "/"
206 #print "using kernel_root %s" % kernel_root
207 self.archs = archs
208 self.searched = set(headers)
209 self.kernel_root = kernel_root
210 self.kernel_config = kernel_config
211 self.needed = {}
212 self.setArch(arch=None)
213
214 def setArch(self,arch=None):
215 self.curr_arch = arch
216 self.arch_headers = set()
217 if arch:
218 self.prefix = "asm-%s/" % arch
219 else:
220 self.prefix = None
221
222 def pathFromHeader(self,header):
223 path = header
224 if self.prefix and path.startswith("asm/"):
225 path = "%s%s" % (self.prefix, path[4:])
226 return path
227
228 def pathToHeader(self,path):
229 if self.prefix and path.startswith(self.prefix):
230 path = "asm/%s" % path[len(self.prefix):]
231 return "%s" % path
232
233 def setSearchedHeaders(self,headers):
234 self.searched = set(headers)
235
236 def scanForArch(self):
237 fparser = HeaderScanner(config=self.kernel_config)
238 workqueue = []
239 needed = {}
240 for h in self.searched:
241 path = self.pathFromHeader(h)
242 if not path in needed:
243 needed[path] = set()
244 workqueue.append(path)
245
246 i = 0
247 while i < len(workqueue):
248 path = workqueue[i]
249 i += 1
250 fparser.parseFile(self.kernel_root + path,
251 arch=self.curr_arch, kernel_root=self.kernel_root)
252 for used in fparser.getHeaders():
253 path = self.pathFromHeader(used)
254 if not path in needed:
255 needed[path] = set()
256 workqueue.append(path)
257 for user in fparser.getHeaderUsers(used):
258 needed[path].add(user)
259
260 # now copy the arch-specific headers into the global list
261 for header in needed.keys():
262 users = needed[header]
263 if not header in self.needed:
264 self.needed[header] = set()
265
266 for user in users:
267 self.needed[header].add(user)
268
269 def scanForAllArchs(self):
270 """scan for all architectures and return the set of all needed kernel headers"""
271 for arch in self.archs:
272 self.setArch(arch)
273 self.scanForArch()
274
275 return set(self.needed.keys())
276
277 def getHeaderUsers(self,header):
278 """return the set of all users for a given header"""
279 return set(self.needed[header])
280
281 def getArchHeaders(self,arch):
282 """return the set of all <asm/...> headers required by a given architecture"""
283 return set() # XXX: TODO
284
285#####################################################################################
286#####################################################################################
287##### #####
288##### C O N F I G P A R S E R #####
289##### #####
290#####################################################################################
291#####################################################################################
292
293class ConfigParser:
294 """a class used to parse the Linux kernel .config file"""
295 re_CONFIG_ = re.compile(r"^(CONFIG_\w+)=(.*)$")
296
297 def __init__(self):
298 self.items = {}
299 self.duplicates = False
300
301 def parseLine(self,line):
302 line = string.strip(line)
303
304 # skip empty and comment lines
305 if len(line) == 0 or line[0] == "#":
306 return
307
308 m = ConfigParser.re_CONFIG_.match(line)
309 if not m: return
310
311 name = m.group(1)
312 value = m.group(2)
313
314 if name in self.items: # aarg, duplicate value
315 self.duplicates = True
316
317 self.items[name] = value
318
319 def parseFile(self,path):
320 f = file(path, "r")
321 for line in f:
322 if len(line) > 0:
323 if line[-1] == "\n":
324 line = line[:-1]
325 if len(line) > 0 and line[-1] == "\r":
326 line = line[:-1]
327 self.parseLine(line)
328 f.close()
329
330 def getDefinitions(self):
331 """retrieve a dictionary containing definitions for CONFIG_XXX"""
332 return self.items.copy()
333
334 def __repr__(self):
335 return repr(self.items)
336
337 def __str__(self):
338 return str(self.items)