The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame^] | 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 =\ |
| 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 | |
| 170 | class 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 | |
| 293 | class 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) |