| # 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, string, 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_\+\.\-/]*)>.*$" % string.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: |
| #list.removePrefixed("CONFIG_",self.config) |
| 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 += "/" |
| #print "using kernel_root %s" % 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 = string.strip(line) |
| |
| # 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) |