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_dir, src_dir, 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     full_path = os.path.join(src_dir, rel_path)
     88 
     89     if not os.path.exists(full_path):
     90         print_error(no_update, "file does not exist: '%s'\n" % full_path)
     91         return None, None
     92 
     93     if not os.path.isfile(full_path):
     94         print_error(no_update, "path is not a file: '%s'\n" % full_path)
     95         return None, None
     96 
     97     # convert into destination path, extracting architecture if needed
     98     # and the corresponding list of known static functions
     99     #
    100     arch = None
    101     statics = kernel_known_generic_statics
    102     m = re.match(r"asm-([\w\d_\+\.\-]+)(/.*)", rel_path)
    103     if m and m.group(1) != 'generic':
    104         dst_path = "arch-%s/asm/%s" % m.groups()
    105         arch = m.group(1)
    106         statics  = statics.union(kernel_known_statics.get(arch, set()))
    107     else:
    108         # process headers under the uapi directory
    109         # note the "asm" level has been explicitly added in the original
    110         # kernel header tree for architectural-dependent uapi headers
    111         m_uapi = re.match(r"(uapi)/([\w\d_\+\.\-]+)(/.*)", rel_path)
    112         if m_uapi:
    113             dst_path = rel_path
    114             m_uapi_arch = re.match(r"asm-([\w\d_\+\.\-]+)", m_uapi.group(2))
    115             if m_uapi_arch and m_uapi_arch.group(1) != 'generic':
    116                 arch = m_uapi_arch.group(1)
    117                 statics = statics.union(kernel_known_statics.get(arch, set()))
    118         # common headers (ie non-asm and non-uapi)
    119         else:
    120             dst_path = os.path.join("common", rel_path)
    121 
    122     dst_path = os.path.join(dst_dir, dst_path)
    123 
    124     # now, let's parse the file
    125     #
    126     parser = cpp.BlockParser()
    127     blocks = parser.parseFile(full_path)
    128     if not parser.parsed:
    129         print_error(no_update, "can't parse '%s'%" % full_path)
    130         return None, None
    131 
    132     macros = kernel_known_macros.copy()
    133     if arch and arch in kernel_default_arch_macros:
    134         macros.update(kernel_default_arch_macros[arch])
    135 
    136     if arch and arch in kernel_arch_token_replacements:
    137         blocks.replaceTokens(kernel_arch_token_replacements[arch])
    138 
    139     blocks.optimizeMacros(macros)
    140     blocks.optimizeIf01()
    141     blocks.removeVarsAndFuncs(statics)
    142     blocks.replaceTokens(kernel_token_replacements)
    143     blocks.removeMacroDefines(kernel_ignored_macros)
    144 
    145     out = StringOutput()
    146     out.write(kernel_disclaimer)
    147     blocks.writeWithWarning(out, kernel_warning, 4)
    148     return dst_path, out.get()
    149 
    150 
    151 if __name__ == "__main__":
    152 
    153     def usage():
    154         print """\
    155     usage:  %s [options] <header_path>
    156 
    157         options:
    158             -v    enable verbose mode
    159 
    160             -u    enabled update mode
    161                 this will try to update the corresponding 'clean header'
    162                 if the content has changed. with this, you can pass more
    163                 than one file on the command-line
    164 
    165             -k<path>  specify path of original kernel headers
    166             -d<path>  specify path of cleaned kernel headers
    167 
    168         <header_path> must be in a subdirectory of 'original'
    169     """ % os.path.basename(sys.argv[0])
    170         sys.exit(1)
    171 
    172     try:
    173         optlist, args = getopt.getopt(sys.argv[1:], 'uvk:d:')
    174     except:
    175         # unrecognized option
    176         sys.stderr.write("error: unrecognized option\n")
    177         usage()
    178 
    179     no_update = True
    180     dst_dir = get_kernel_dir()
    181     src_dir = get_kernel_headers_original_dir()
    182     for opt, arg in optlist:
    183         if opt == '-u':
    184             no_update = False
    185         elif opt == '-v':
    186             logging.basicConfig(level=logging.DEBUG)
    187         elif opt == '-k':
    188             src_dir = arg
    189         elif opt == '-d':
    190             dst_dir = arg
    191 
    192     if len(args) == 0:
    193         usage()
    194 
    195     if no_update:
    196         for path in args:
    197             dst_path, newdata = cleanupFile(dst_dir, src_dir, path)
    198             print newdata
    199 
    200         sys.exit(0)
    201 
    202     # now let's update our files.
    203 
    204     b = BatchFileUpdater()
    205 
    206     for path in args:
    207         dst_path, newdata = cleanupFile(dst_dir, src_dir, path, no_update)
    208         if not dst_path:
    209             continue
    210 
    211         b.readFile(dst_path)
    212         r = b.editFile(dst_path, newdata)
    213         if r == 0:
    214             r = "unchanged"
    215         elif r == 1:
    216             r = "edited"
    217         else:
    218             r = "added"
    219 
    220         print "cleaning: %-*s -> %-*s (%s)" % (35, path, 35, dst_path, r)
    221 
    222 
    223     b.updateGitFiles()
    224 
    225     sys.exit(0)
    226