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