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 # 7 import sys, cpp, re, os.path, string, time 8 from defaults import * 9 10 verboseSearch = 0 11 verboseFind = 0 12 13 ######################################################################## 14 ######################################################################## 15 ##### ##### 16 ##### H E A D E R S C A N N E R ##### 17 ##### ##### 18 ######################################################################## 19 ######################################################################## 20 21 22 class 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 macros = kernel_known_macros.copy() 136 if kernel_root: 137 macros.update(self.config) 138 if arch and arch in kernel_default_arch_macros: 139 macros.update(kernel_default_arch_macros[arch]) 140 list.optimizeMacros(macros) 141 list.optimizeIf01() 142 includes = list.findIncludes() 143 for inc in includes: 144 self.checkInclude(inc, path, kernel_root) 145 146 def getHeaders(self): 147 """return the set of all needed kernel headers""" 148 return set(self.headers.keys()) 149 150 def getHeaderUsers(self,header): 151 """return the set of all users for a given header""" 152 return set(self.headers.get(header)) 153 154 def getAllUsers(self): 155 """return a dictionary mapping heaaders to their user set""" 156 return self.headers.copy() 157 158 def getFiles(self): 159 """returns the set of files that do include kernel headers""" 160 return self.files.copy() 161 162 163 ########################################################################## 164 ########################################################################## 165 ##### ##### 166 ##### H E A D E R F I N D E R ##### 167 ##### ##### 168 ########################################################################## 169 ########################################################################## 170 171 172 class KernelHeaderFinder: 173 """a class used to scan the kernel headers themselves.""" 174 175 # this is different 176 # from a HeaderScanner because we need to translate the path returned by 177 # HeaderScanner.getHeaders() into possibly architecture-specific ones. 178 # 179 # for example, <asm/XXXX.h> needs to be translated in <asm-ARCH/XXXX.h> 180 # where ARCH is appropriately chosen 181 182 # here's how to use this: 183 # 184 # scanner = HeaderScanner() 185 # for path in <your list of user sources>: 186 # scanner.parseFile(path) 187 # 188 # used_headers = scanner.getHeaders() 189 # finder = KernelHeaderFinder(used_headers, [ "arm", "x86" ], 190 # "<kernel_include_path>") 191 # all_headers = finder.scanForAllArchs() 192 # 193 # not that the result of scanForAllArchs() is a list of relative 194 # header paths that are not bracketed 195 # 196 197 def __init__(self,headers,archs,kernel_root,kernel_config): 198 """init a KernelHeaderScanner, 199 200 'headers' is a list or set of headers, 201 'archs' is a list of architectures 202 'kernel_root' is the path to the 'include' directory 203 of your original kernel sources 204 """ 205 206 if len(kernel_root) > 0 and kernel_root[-1] != "/": 207 kernel_root += "/" 208 #print "using kernel_root %s" % kernel_root 209 self.archs = archs 210 self.searched = set(headers) 211 self.kernel_root = kernel_root 212 self.kernel_config = kernel_config 213 self.needed = {} 214 self.setArch(arch=None) 215 216 def setArch(self,arch=None): 217 self.curr_arch = arch 218 self.arch_headers = set() 219 if arch: 220 self.prefix = "asm-%s/" % arch 221 else: 222 self.prefix = None 223 224 def pathFromHeader(self,header): 225 path = header 226 if self.prefix and path.startswith("asm/"): 227 path = "%s%s" % (self.prefix, path[4:]) 228 return path 229 230 def pathToHeader(self,path): 231 if self.prefix and path.startswith(self.prefix): 232 path = "asm/%s" % path[len(self.prefix):] 233 return "%s" % path 234 235 def setSearchedHeaders(self,headers): 236 self.searched = set(headers) 237 238 def scanForArch(self): 239 fparser = HeaderScanner(config=self.kernel_config) 240 workqueue = [] 241 needed = {} 242 for h in self.searched: 243 path = self.pathFromHeader(h) 244 if not path in needed: 245 needed[path] = set() 246 workqueue.append(path) 247 248 i = 0 249 while i < len(workqueue): 250 path = workqueue[i] 251 i += 1 252 fparser.parseFile(self.kernel_root + path, 253 arch=self.curr_arch, kernel_root=self.kernel_root) 254 for used in fparser.getHeaders(): 255 path = self.pathFromHeader(used) 256 if not path in needed: 257 needed[path] = set() 258 workqueue.append(path) 259 for user in fparser.getHeaderUsers(used): 260 needed[path].add(user) 261 262 # now copy the arch-specific headers into the global list 263 for header in needed.keys(): 264 users = needed[header] 265 if not header in self.needed: 266 self.needed[header] = set() 267 268 for user in users: 269 self.needed[header].add(user) 270 271 def scanForAllArchs(self): 272 """scan for all architectures and return the set of all needed kernel headers""" 273 for arch in self.archs: 274 self.setArch(arch) 275 self.scanForArch() 276 277 return set(self.needed.keys()) 278 279 def getHeaderUsers(self,header): 280 """return the set of all users for a given header""" 281 return set(self.needed[header]) 282 283 def getArchHeaders(self,arch): 284 """return the set of all <asm/...> headers required by a given architecture""" 285 return set() # XXX: TODO 286 287 ##################################################################################### 288 ##################################################################################### 289 ##### ##### 290 ##### C O N F I G P A R S E R ##### 291 ##### ##### 292 ##################################################################################### 293 ##################################################################################### 294 295 class ConfigParser: 296 """a class used to parse the Linux kernel .config file""" 297 re_CONFIG_ = re.compile(r"^(CONFIG_\w+)=(.*)$") 298 299 def __init__(self): 300 self.items = {} 301 self.duplicates = False 302 303 def parseLine(self,line): 304 line = string.strip(line) 305 306 # skip empty and comment lines 307 if len(line) == 0 or line[0] == "#": 308 return 309 310 m = ConfigParser.re_CONFIG_.match(line) 311 if not m: return 312 313 name = m.group(1) 314 value = m.group(2) 315 316 if name in self.items: # aarg, duplicate value 317 self.duplicates = True 318 319 self.items[name] = value 320 321 def parseFile(self,path): 322 f = file(path, "r") 323 for line in f: 324 if len(line) > 0: 325 if line[-1] == "\n": 326 line = line[:-1] 327 if len(line) > 0 and line[-1] == "\r": 328 line = line[:-1] 329 self.parseLine(line) 330 f.close() 331 332 def getDefinitions(self): 333 """retrieve a dictionary containing definitions for CONFIG_XXX""" 334 return self.items.copy() 335 336 def __repr__(self): 337 return repr(self.items) 338 339 def __str__(self): 340 return str(self.items) 341