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
     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
     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 = None
    156     src_dir = None
    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     # get_kernel_dir() and get_kernel_headers_original_dir() require the current
    167     # working directory to be a direct or indirect subdirectory of
    168     # ANDROID_BUILD_TOP.  Otherwise, these functions print an error message and
    169     # exit.  Let's allow the user to run this program from an unrelated
    170     # directory, if they specify src_dir and dst_dir on the command line.
    171     if dst_dir is None:
    172       dst_dir = get_kernel_dir()
    173     if src_dir is None:
    174       src_dir = get_kernel_headers_original_dir()
    175 
    176     if len(args) == 0:
    177         usage()
    178 
    179     if no_update:
    180         for path in args:
    181             dst_file = os.path.join(dst_dir, path)
    182             src_file = os.path.join(src_dir, path)
    183             new_data = cleanupFile(dst_file, src_file, path)
    184             # Use sys.stdout.write instead of a simple print statement to avoid
    185             # sending an extra new line character to stdout.  Running this
    186             # program in non-update mode and redirecting stdout to a file should
    187             # yield the same result as using update mode, where new_data is
    188             # written directly to a file.
    189             sys.stdout.write(new_data)
    190 
    191         sys.exit(0)
    192 
    193     # Now let's update our files.
    194 
    195     b = BatchFileUpdater()
    196 
    197     for path in args:
    198         dst_file = os.path.join(dst_dir, path)
    199         src_file = os.path.join(src_dir, path)
    200         new_data = cleanupFile(dst_file, src_file, path, no_update)
    201         if not new_data:
    202             continue
    203 
    204         b.readFile(dst_file)
    205         r = b.editFile(dst_file, new_data)
    206         if r == 0:
    207             r = "unchanged"
    208         elif r == 1:
    209             r = "edited"
    210         else:
    211             r = "added"
    212 
    213         print "cleaning: %-*s -> %-*s (%s)" % (35, path, 35, path, r)
    214 
    215 
    216     b.updateGitFiles()
    217 
    218     sys.exit(0)
    219