Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 
      3 #------------------------------------------------------------------------------
      4 # Description of the header clean process
      5 #------------------------------------------------------------------------------
      6 # Here is the list of actions performed by this script to clean the original
      7 # kernel headers.
      8 #
      9 # 1. Optimize well-known macros (e.g. __KERNEL__, __KERNEL_STRICT_NAMES)
     10 #
     11 #     This pass gets rid of everything that is guarded by a well-known macro
     12 #     definition. This means that a block like:
     13 #
     14 #        #ifdef __KERNEL__
     15 #        ....
     16 #        #endif
     17 #
     18 #     Will be totally omitted from the output. The optimizer is smart enough to
     19 #     handle all complex C-preprocessor conditional expression appropriately.
     20 #     This means that, for example:
     21 #
     22 #        #if defined(__KERNEL__) || defined(FOO)
     23 #        ...
     24 #        #endif
     25 #
     26 #     Will be transformed into:
     27 #
     28 #        #ifdef FOO
     29 #        ...
     30 #        #endif
     31 #
     32 #     See tools/defaults.py for the list of well-known macros used in this pass,
     33 #     in case you need to update it in the future.
     34 #
     35 #     Note that this also removes any reference to a kernel-specific
     36 #     configuration macro like CONFIG_FOO from the clean headers.
     37 #
     38 #
     39 # 2. Remove variable and function declarations:
     40 #
     41 #   This pass scans non-directive text and only keeps things that look like a
     42 #   typedef/struct/union/enum declaration. This allows us to get rid of any
     43 #   variables or function declarations that should only be used within the
     44 #   kernel anyway (and which normally *should* be guarded by an #ifdef
     45 #   __KERNEL__ ...  #endif block, if the kernel writers were not so messy).
     46 #
     47 #   There are, however, a few exceptions: it is seldom useful to keep the
     48 #   definition of some static inline functions performing very simple
     49 #   operations. A good example is the optimized 32-bit byte-swap function
     50 #   found in:
     51 #
     52 #     arch-arm/asm/byteorder.h
     53 #
     54 #   The list of exceptions is in tools/defaults.py in case you need to update
     55 #   it in the future.
     56 #
     57 #   Note that we do *not* remove macro definitions, including these macro that
     58 #   perform a call to one of these kernel-header functions, or even define other
     59 #   functions. We consider it safe since userland applications have no business
     60 #   using them anyway.
     61 #
     62 #
     63 # 3. Add a standard disclaimer:
     64 #
     65 #   The message:
     66 #
     67 #   /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
     68 #
     69 #   Is prepended to each generated header.
     70 #------------------------------------------------------------------------------
     71 
     72 import sys, cpp, kernel, glob, os, re, getopt
     73 from defaults import *
     74 from utils import *
     75 
     76 def print_error(no_update, msg):
     77     if no_update:
     78         panic(msg)
     79     sys.stderr.write("warning: " + msg)
     80 
     81 
     82 def cleanupFile(dst_file, src_file, rel_path, no_update = True):
     83     """reads an original header and perform the cleanup operation on it
     84        this functions returns the destination path and the clean header
     85        as a single string"""
     86     # Check the header path
     87     if not os.path.exists(src_file):
     88         print_error(no_update, "'%s' does not exist\n" % src_file)
     89         return None, None
     90 
     91     if not os.path.isfile(src_file):
     92         print_error(no_update, "'%s' is not a file\n" % src_file)
     93         return None, None
     94 
     95     # Extract the architecture if found.
     96     arch = None
     97     m = re.search(r"(^|/)asm-([\w\d_\+\.\-]+)/.*", rel_path)
     98     if m and m.group(2) != 'generic':
     99         arch = m.group(2)
    100 
    101     # Now, let's parse the file.
    102     parser = cpp.BlockParser()
    103     blocks = parser.parseFile(src_file)
    104     if not parser.parsed:
    105         print_error(no_update, "Can't parse '%s'" % src_file)
    106         return None
    107 
    108     macros = kernel_known_macros.copy()
    109     if arch and arch in kernel_default_arch_macros:
    110         macros.update(kernel_default_arch_macros[arch])
    111 
    112     if arch and arch in kernel_arch_token_replacements:
    113         blocks.replaceTokens(kernel_arch_token_replacements[arch])
    114 
    115     blocks.optimizeMacros(macros)
    116     blocks.optimizeIf01()
    117     blocks.removeVarsAndFuncs(kernel_known_generic_statics)
    118     blocks.replaceTokens(kernel_token_replacements)
    119 
    120     out = StringOutput()
    121     out.write(kernel_disclaimer)
    122     blocks.write(out)
    123     return out.get()
    124 
    125 
    126 if __name__ == "__main__":
    127 
    128     def usage():
    129         print """\
    130     usage:  %s [options] <header_path>
    131 
    132         options:
    133             -v    enable verbose mode
    134 
    135             -u    enabled update mode
    136                 this will try to update the corresponding 'clean header'
    137                 if the content has changed. with this, you can pass more
    138                 than one file on the command-line
    139 
    140             -k<path>  specify path of original kernel headers
    141             -d<path>  specify path of cleaned kernel headers
    142 
    143         <header_path> must be in a subdirectory of 'original'
    144     """ % os.path.basename(sys.argv[0])
    145         sys.exit(1)
    146 
    147     try:
    148         optlist, args = getopt.getopt(sys.argv[1:], 'uvk:d:')
    149     except:
    150         # unrecognized option
    151         sys.stderr.write("error: unrecognized option\n")
    152         usage()
    153 
    154     no_update = True
    155     dst_dir = get_kernel_dir()
    156     src_dir = get_kernel_headers_original_dir()
    157     for opt, arg in optlist:
    158         if opt == '-u':
    159             no_update = False
    160         elif opt == '-v':
    161             logging.basicConfig(level=logging.DEBUG)
    162         elif opt == '-k':
    163             src_dir = arg
    164         elif opt == '-d':
    165             dst_dir = arg
    166 
    167     if len(args) == 0:
    168         usage()
    169 
    170     if no_update:
    171         for path in args:
    172             dst_file = os.path.join(dst_dir, path)
    173             src_file = os.path.join(src_dir, path)
    174             new_data = cleanupFile(dst_file, src_file, path)
    175             print new_data
    176 
    177         sys.exit(0)
    178 
    179     # Now let's update our files.
    180 
    181     b = BatchFileUpdater()
    182 
    183     for path in args:
    184         dst_file = os.path.join(dst_dir, path)
    185         src_file = os.path.join(src_dir, path)
    186         new_data = cleanupFile(dst_file, src_file, path, no_update)
    187         if not new_data:
    188             continue
    189 
    190         b.readFile(path)
    191         r = b.editFile(path, new_data)
    192         if r == 0:
    193             r = "unchanged"
    194         elif r == 1:
    195             r = "edited"
    196         else:
    197             r = "added"
    198 
    199         print "cleaning: %-*s -> %-*s (%s)" % (35, path, 35, path, r)
    200 
    201 
    202     b.updateGitFiles()
    203 
    204     sys.exit(0)
    205