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