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. Whitespace cleanup:
     64 #
     65 #   The final pass removes any comments and empty lines from the final headers.
     66 #
     67 #
     68 # 4. Add a standard disclaimer:
     69 #
     70 #   The message:
     71 #
     72 #   /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
     73 #
     74 #   Is prepended to each generated header.
     75 #------------------------------------------------------------------------------
     76 
     77 import sys, cpp, kernel, glob, os, re, getopt
     78 from defaults import *
     79 from utils import *
     80 
     81 noUpdate = 1
     82 
     83 def cleanupFile(path, original_path):
     84     """reads an original header and perform the cleanup operation on it
     85        this functions returns the destination path and the clean header
     86        as a single string"""
     87     # check the header path
     88     src_path = path
     89 
     90     if not os.path.exists(src_path):
     91         if noUpdate:
     92             panic( "file does not exist: '%s'\n" % path )
     93         sys.stderr.write( "warning: file does not exit: %s\n" % path )
     94         return None, None
     95 
     96     if not os.path.isfile(src_path):
     97         if noUpdate:
     98             panic( "path is not a file: '%s'\n" % path )
     99         sys.stderr.write( "warning: not a file: %s\n" % path )
    100         return None, None
    101 
    102     if os.path.commonprefix( [ src_path, original_path ] ) != original_path:
    103         if noUpdate:
    104             panic( "file is not in 'original' directory: %s\n" % path );
    105         sys.stderr.write( "warning: file not in 'original' ignored: %s\n" % path )
    106         return None, None
    107 
    108     src_path = src_path[len(original_path):]
    109     if len(src_path) > 0 and src_path[0] == '/':
    110         src_path = src_path[1:]
    111 
    112     if len(src_path) == 0:
    113         panic( "oops, internal error, can't extract correct relative path\n" )
    114 
    115     # convert into destination path, extracting architecture if needed
    116     # and the corresponding list of known static functions
    117     #
    118     arch = None
    119     statics = kernel_known_generic_statics
    120     m = re.match(r"asm-([\w\d_\+\.\-]+)(/.*)", src_path)
    121     if m and m.group(1) != 'generic':
    122         dst_path = "arch-%s/asm/%s" % m.groups()
    123         arch     = m.group(1)
    124         statics  = statics.union( kernel_known_statics.get( arch, set() ) )
    125     else:
    126         # process headers under the uapi directory
    127         # note the "asm" level has been explicitly added in the original
    128         # kernel header tree for architectural-dependent uapi headers
    129         m_uapi = re.match(r"(uapi)/([\w\d_\+\.\-]+)(/.*)", src_path)
    130         if m_uapi:
    131             dst_path = src_path
    132             m_uapi_arch = re.match(r"asm-([\w\d_\+\.\-]+)", m_uapi.group(2))
    133             if m_uapi_arch and m_uapi_arch.group(1) != 'generic':
    134                 arch     = m_uapi_arch.group(1)
    135                 statics  = statics.union( kernel_known_statics.get( arch, set() ) )
    136         # common headers (ie non-asm and non-uapi)
    137         else:
    138             dst_path = "common/" + src_path
    139 
    140     dst_path = os.path.normpath( kernel_cleaned_path + "/" + dst_path )
    141 
    142     # now, let's parse the file
    143     #
    144     blocks = cpp.BlockParser().parseFile(path)
    145     if not blocks:
    146         sys.stderr.write( "error: can't parse '%s'" % path )
    147         sys.exit(1)
    148 
    149     macros = kernel_known_macros.copy()
    150     if arch and arch in kernel_default_arch_macros:
    151         macros.update(kernel_default_arch_macros[arch])
    152 
    153     if arch and arch in kernel_arch_token_replacements:
    154         blocks.replaceTokens( kernel_arch_token_replacements[arch] )
    155 
    156     blocks.optimizeMacros( macros )
    157     blocks.optimizeIf01()
    158     blocks.removeVarsAndFuncs( statics )
    159     blocks.replaceTokens( kernel_token_replacements )
    160     blocks.removeComments()
    161     blocks.removeMacroDefines( kernel_ignored_macros )
    162     blocks.removeWhiteSpace()
    163 
    164     out = StringOutput()
    165     out.write( kernel_disclaimer )
    166     blocks.writeWithWarning(out, kernel_warning, 4)
    167     return dst_path, out.get()
    168 
    169 
    170 if __name__ == "__main__":
    171 
    172     def usage():
    173         print """\
    174     usage:  %s [options] <header_path>
    175 
    176         options:
    177             -v    enable verbose mode
    178 
    179             -u    enabled update mode
    180                 this will try to update the corresponding 'clean header'
    181                 if the content has changed. with this, you can pass more
    182                 than one file on the command-line
    183 
    184             -k<path>  specify path of original kernel headers
    185             -d<path>  specify path of cleaned kernel headers
    186 
    187         <header_path> must be in a subdirectory of 'original'
    188     """ % os.path.basename(sys.argv[0])
    189         sys.exit(1)
    190 
    191     try:
    192         optlist, args = getopt.getopt( sys.argv[1:], 'uvk:d:' )
    193     except:
    194         # unrecognized option
    195         sys.stderr.write( "error: unrecognized option\n" )
    196         usage()
    197 
    198     for opt, arg in optlist:
    199         if opt == '-u':
    200             noUpdate = 0
    201         elif opt == '-v':
    202             verbose = 1
    203             D_setlevel(1)
    204         elif opt == '-k':
    205             kernel_original_path = arg
    206         elif opt == '-d':
    207             kernel_cleaned_path = arg
    208 
    209     if len(args) == 0:
    210         usage()
    211 
    212     if noUpdate:
    213         for path in args:
    214             dst_path, newdata = cleanupFile(path,kernel_original_path)
    215             print newdata
    216 
    217         sys.exit(0)
    218 
    219     # now let's update our files.
    220 
    221     b = BatchFileUpdater()
    222 
    223     for path in args:
    224         dst_path, newdata = cleanupFile(path,kernel_original_path)
    225         if not dst_path:
    226             continue
    227 
    228         b.readFile( dst_path )
    229         r = b.editFile( dst_path, newdata )
    230         if r == 0:
    231             r = "unchanged"
    232         elif r == 1:
    233             r = "edited"
    234         else:
    235             r = "added"
    236 
    237         print "cleaning: %-*s -> %-*s (%s)" % ( 35, path, 35, dst_path, r )
    238 
    239 
    240     b.updateGitFiles()
    241 
    242     sys.exit(0)
    243