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