• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 # 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 #
7 import sys, cpp, re, os.path, string, time
8 from defaults import *
9 
10 verboseSearch = 0
11 verboseFind   = 0
12 
13 ########################################################################
14 ########################################################################
15 #####                                                              #####
16 #####           H E A D E R   S C A N N E R                        #####
17 #####                                                              #####
18 ########################################################################
19 ########################################################################
20 
21 
22 class 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 
170 class 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 
293 class 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)
339