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 =\ 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 170class 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 293class 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