• 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#
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_str=\
59       r"^.*<((%s)/[\d\w_\+\.\-/]*)>.*$" % string.join(kernel_dirs,"|")
60
61    re_combined = re.compile(re_combined_str)
62
63    # 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)
342