| # this file contains definitions related to the Linux kernel itself | 
 | # | 
 |  | 
 | # list here the macros that you know are always defined/undefined when including | 
 | # the kernel headers | 
 | # | 
 | import sys, cpp, re, os.path, time | 
 | from defaults import * | 
 |  | 
 | verboseSearch = 0 | 
 | verboseFind   = 0 | 
 |  | 
 | ######################################################################## | 
 | ######################################################################## | 
 | #####                                                              ##### | 
 | #####           H E A D E R   S C A N N E R                        ##### | 
 | #####                                                              ##### | 
 | ######################################################################## | 
 | ######################################################################## | 
 |  | 
 |  | 
 | class HeaderScanner: | 
 |     """a class used to non-recursively detect which Linux kernel headers are | 
 |        used by a given set of input source files""" | 
 |  | 
 |     # to use the HeaderScanner, do the following: | 
 |     # | 
 |     #    scanner = HeaderScanner() | 
 |     #    for path in <your list of files>: | 
 |     #        scanner.parseFile(path) | 
 |     # | 
 |     #    # get the set of Linux headers included by your files | 
 |     #    headers = scanner.getHeaders() | 
 |     # | 
 |     #    # get the set of of input files that do include Linux headers | 
 |     #    files   = scanner.getFiles() | 
 |     # | 
 |     #    note that the result of getHeaders() is a set of strings, each one | 
 |     #    corresponding to a non-bracketed path name, e.g.: | 
 |     # | 
 |     #        set("linux/types","asm/types.h") | 
 |     # | 
 |  | 
 |     # the default algorithm is pretty smart and will analyze the input | 
 |     # files with a custom C pre-processor in order to optimize out macros, | 
 |     # get rid of comments, empty lines, etc.. | 
 |     # | 
 |     # this avoids many annoying false positives... !! | 
 |     # | 
 |  | 
 |     # this regular expression is used to detect include paths that relate to | 
 |     # the kernel, by default, it selects one of: | 
 |     #    <linux/*> | 
 |     #    <asm/*> | 
 |     #    <asm-generic/*> | 
 |     #    <mtd/*> | 
 |     # | 
 |     re_combined_str=\ | 
 |        r"^.*<((%s)/[\d\w_\+\.\-/]*)>.*$" % "|".join(kernel_dirs) | 
 |  | 
 |     re_combined = re.compile(re_combined_str) | 
 |  | 
 |     # some kernel files choose to include files with relative paths (x86 32/64 | 
 |     # dispatch for instance) | 
 |     re_rel_dir = re.compile(r'^.*"([\d\w_\+\.\-/]+)".*$') | 
 |  | 
 |     def __init__(self,config={}): | 
 |         """initialize a HeaderScanner""" | 
 |         self.reset() | 
 |         self.config = config | 
 |  | 
 |     def reset(self,config={}): | 
 |         self.files    = set()  # set of files being parsed for headers | 
 |         self.headers  = {}     # maps headers to set of users | 
 |         self.config   = config | 
 |  | 
 |     def checkInclude(self, line, from_file, kernel_root=None): | 
 |         relative = False | 
 |         m = HeaderScanner.re_combined.match(line) | 
 |         if kernel_root and not m: | 
 |             m = HeaderScanner.re_rel_dir.match(line) | 
 |             relative = True | 
 |         if not m: return | 
 |  | 
 |         header = m.group(1) | 
 |         if from_file: | 
 |             self.files.add(from_file) | 
 |             if kernel_root and relative: | 
 |                 hdr_dir = os.path.realpath(os.path.dirname(from_file)) | 
 |                 hdr_dir = hdr_dir.replace("%s/" % os.path.realpath(kernel_root), | 
 |                                           "") | 
 |                 if hdr_dir: | 
 |                     _prefix = "%s/" % hdr_dir | 
 |                 else: | 
 |                     _prefix = "" | 
 |                 header = "%s%s" % (_prefix, header) | 
 |  | 
 |         if not header in self.headers: | 
 |             self.headers[header] = set() | 
 |  | 
 |         if from_file: | 
 |             if verboseFind: | 
 |                 print("=== %s uses %s" % (from_file, header)) | 
 |             self.headers[header].add(from_file) | 
 |  | 
 |     def parseFile(self, path, arch=None, kernel_root=None): | 
 |         """parse a given file for Linux headers""" | 
 |         if not os.path.exists(path): | 
 |             return | 
 |  | 
 |         # since tokenizing the file is very slow, we first try a quick grep | 
 |         # to see if this returns any meaningful results. only if this is true | 
 |         # do we do the tokenization""" | 
 |         try: | 
 |             f = open(path, "rt") | 
 |         except: | 
 |             print("!!! can't read '%s'" % path) | 
 |             return | 
 |  | 
 |         hasIncludes = False | 
 |         for line in f: | 
 |             if (HeaderScanner.re_combined.match(line) or | 
 |                 (kernel_root and HeaderScanner.re_rel_dir.match(line))): | 
 |                 hasIncludes = True | 
 |                 break | 
 |  | 
 |         if not hasIncludes: | 
 |             if verboseSearch: print("::: " + path) | 
 |             return | 
 |  | 
 |         if verboseSearch: print("*** " + path) | 
 |  | 
 |         list = cpp.BlockParser().parseFile(path) | 
 |         if list: | 
 |             macros = kernel_known_macros.copy() | 
 |             if kernel_root: | 
 |                 macros.update(self.config) | 
 |                 if arch and arch in kernel_default_arch_macros: | 
 |                     macros.update(kernel_default_arch_macros[arch]) | 
 |             list.optimizeMacros(macros) | 
 |             list.optimizeIf01() | 
 |             includes = list.findIncludes() | 
 |             for inc in includes: | 
 |                 self.checkInclude(inc, path, kernel_root) | 
 |  | 
 |     def getHeaders(self): | 
 |         """return the set of all needed kernel headers""" | 
 |         return set(self.headers.keys()) | 
 |  | 
 |     def getHeaderUsers(self,header): | 
 |         """return the set of all users for a given header""" | 
 |         return set(self.headers.get(header)) | 
 |  | 
 |     def getAllUsers(self): | 
 |         """return a dictionary mapping heaaders to their user set""" | 
 |         return self.headers.copy() | 
 |  | 
 |     def getFiles(self): | 
 |         """returns the set of files that do include kernel headers""" | 
 |         return self.files.copy() | 
 |  | 
 |  | 
 | ########################################################################## | 
 | ########################################################################## | 
 | #####                                                                ##### | 
 | #####           H E A D E R   F I N D E R                            ##### | 
 | #####                                                                ##### | 
 | ########################################################################## | 
 | ########################################################################## | 
 |  | 
 |  | 
 | class KernelHeaderFinder: | 
 |     """a class used to scan the kernel headers themselves.""" | 
 |  | 
 |     # this is different | 
 |     #  from a HeaderScanner because we need to translate the path returned by | 
 |     #  HeaderScanner.getHeaders() into possibly architecture-specific ones. | 
 |     # | 
 |     # for example, <asm/XXXX.h> needs to be translated in <asm-ARCH/XXXX.h> | 
 |     # where ARCH is appropriately chosen | 
 |  | 
 |     # here's how to use this: | 
 |     # | 
 |     #    scanner = HeaderScanner() | 
 |     #    for path in <your list of user sources>: | 
 |     #        scanner.parseFile(path) | 
 |     # | 
 |     #    used_headers = scanner.getHeaders() | 
 |     #    finder       = KernelHeaderFinder(used_headers, [ "arm", "x86" ], | 
 |     #                                      "<kernel_include_path>") | 
 |     #    all_headers  = finder.scanForAllArchs() | 
 |     # | 
 |     #   not that the result of scanForAllArchs() is a list of relative | 
 |     #   header paths that are not bracketed | 
 |     # | 
 |  | 
 |     def __init__(self,headers,archs,kernel_root,kernel_config): | 
 |         """init a KernelHeaderScanner, | 
 |  | 
 |             'headers' is a list or set of headers, | 
 |             'archs' is a list of architectures | 
 |             'kernel_root' is the path to the 'include' directory | 
 |              of your original kernel sources | 
 |         """ | 
 |  | 
 |         if len(kernel_root) > 0 and kernel_root[-1] != "/": | 
 |             kernel_root += "/" | 
 |         self.archs         = archs | 
 |         self.searched      = set(headers) | 
 |         self.kernel_root   = kernel_root | 
 |         self.kernel_config = kernel_config | 
 |         self.needed        = {} | 
 |         self.setArch(arch=None) | 
 |  | 
 |     def setArch(self,arch=None): | 
 |         self.curr_arch = arch | 
 |         self.arch_headers = set() | 
 |         if arch: | 
 |             self.prefix = "asm-%s/" % arch | 
 |         else: | 
 |             self.prefix = None | 
 |  | 
 |     def pathFromHeader(self,header): | 
 |         path = header | 
 |         if self.prefix and path.startswith("asm/"): | 
 |             path = "%s%s" % (self.prefix, path[4:]) | 
 |         return path | 
 |  | 
 |     def pathToHeader(self,path): | 
 |         if self.prefix and path.startswith(self.prefix): | 
 |             path = "asm/%s" % path[len(self.prefix):] | 
 |         return "%s" % path | 
 |  | 
 |     def setSearchedHeaders(self,headers): | 
 |         self.searched = set(headers) | 
 |  | 
 |     def scanForArch(self): | 
 |         fparser   = HeaderScanner(config=self.kernel_config) | 
 |         workqueue = [] | 
 |         needed    = {} | 
 |         for h in self.searched: | 
 |             path = self.pathFromHeader(h) | 
 |             if not path in needed: | 
 |                 needed[path] = set() | 
 |             workqueue.append(path) | 
 |  | 
 |         i = 0 | 
 |         while i < len(workqueue): | 
 |             path = workqueue[i] | 
 |             i   += 1 | 
 |             fparser.parseFile(self.kernel_root + path, | 
 |                               arch=self.curr_arch, kernel_root=self.kernel_root) | 
 |             for used in fparser.getHeaders(): | 
 |                 path  = self.pathFromHeader(used) | 
 |                 if not path in needed: | 
 |                     needed[path] = set() | 
 |                     workqueue.append(path) | 
 |                 for user in fparser.getHeaderUsers(used): | 
 |                     needed[path].add(user) | 
 |  | 
 |         # now copy the arch-specific headers into the global list | 
 |         for header in needed.keys(): | 
 |             users = needed[header] | 
 |             if not header in self.needed: | 
 |                 self.needed[header] = set() | 
 |  | 
 |             for user in users: | 
 |                 self.needed[header].add(user) | 
 |  | 
 |     def scanForAllArchs(self): | 
 |         """scan for all architectures and return the set of all needed kernel headers""" | 
 |         for arch in self.archs: | 
 |             self.setArch(arch) | 
 |             self.scanForArch() | 
 |  | 
 |         return set(self.needed.keys()) | 
 |  | 
 |     def getHeaderUsers(self,header): | 
 |         """return the set of all users for a given header""" | 
 |         return set(self.needed[header]) | 
 |  | 
 |     def getArchHeaders(self,arch): | 
 |         """return the set of all <asm/...> headers required by a given architecture""" | 
 |         return set()  # XXX: TODO | 
 |  | 
 | ##################################################################################### | 
 | ##################################################################################### | 
 | #####                                                                           ##### | 
 | #####           C O N F I G   P A R S E R                                       ##### | 
 | #####                                                                           ##### | 
 | ##################################################################################### | 
 | ##################################################################################### | 
 |  | 
 | class ConfigParser: | 
 |     """a class used to parse the Linux kernel .config file""" | 
 |     re_CONFIG_ = re.compile(r"^(CONFIG_\w+)=(.*)$") | 
 |  | 
 |     def __init__(self): | 
 |         self.items = {} | 
 |         self.duplicates = False | 
 |  | 
 |     def parseLine(self, line): | 
 |         line = line.strip() | 
 |  | 
 |         # skip empty and comment lines | 
 |         if len(line) == 0 or line[0] == "#": | 
 |             return | 
 |  | 
 |         m = ConfigParser.re_CONFIG_.match(line) | 
 |         if not m: return | 
 |  | 
 |         name  = m.group(1) | 
 |         value = m.group(2) | 
 |  | 
 |         if name in self.items:  # aarg, duplicate value | 
 |             self.duplicates = True | 
 |  | 
 |         self.items[name] = value | 
 |  | 
 |     def parseFile(self,path): | 
 |         f = file(path, "r") | 
 |         for line in f: | 
 |             if len(line) > 0: | 
 |                 if line[-1] == "\n": | 
 |                     line = line[:-1] | 
 |                     if len(line) > 0 and line[-1] == "\r": | 
 |                         line = line[:-1] | 
 |                 self.parseLine(line) | 
 |         f.close() | 
 |  | 
 |     def getDefinitions(self): | 
 |         """retrieve a dictionary containing definitions for CONFIG_XXX""" | 
 |         return self.items.copy() | 
 |  | 
 |     def __repr__(self): | 
 |         return repr(self.items) | 
 |  | 
 |     def __str__(self): | 
 |         return str(self.items) |