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
72import sys, cpp, kernel, glob, os, re, getopt
73from defaults import *
74from utils import *
75
76def print_error(no_update, msg):
77    if no_update:
78        panic(msg)
79    sys.stderr.write("warning: " + msg)
80
81
82def 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.removeStructs(kernel_structs_to_remove)
116    blocks.optimizeMacros(macros)
117    blocks.optimizeIf01()
118    blocks.removeVarsAndFuncs(kernel_known_generic_statics)
119    blocks.replaceTokens(kernel_token_replacements)
120
121    out = StringOutput()
122    out.write(kernel_disclaimer)
123    blocks.write(out)
124    return out.get()
125
126
127if __name__ == "__main__":
128
129    def usage():
130        print """\
131    usage:  %s [options] <header_path>
132
133        options:
134            -v    enable verbose mode
135
136            -u    enabled update mode
137                this will try to update the corresponding 'clean header'
138                if the content has changed. with this, you can pass more
139                than one file on the command-line
140
141            -k<path>  specify path of original kernel headers
142            -d<path>  specify path of cleaned kernel headers
143
144        <header_path> must be in a subdirectory of 'original'
145    """ % os.path.basename(sys.argv[0])
146        sys.exit(1)
147
148    try:
149        optlist, args = getopt.getopt(sys.argv[1:], 'uvk:d:')
150    except:
151        # unrecognized option
152        sys.stderr.write("error: unrecognized option\n")
153        usage()
154
155    no_update = True
156    dst_dir = None
157    src_dir = None
158    for opt, arg in optlist:
159        if opt == '-u':
160            no_update = False
161        elif opt == '-v':
162            logging.basicConfig(level=logging.DEBUG)
163        elif opt == '-k':
164            src_dir = arg
165        elif opt == '-d':
166            dst_dir = arg
167    # get_kernel_dir() and get_kernel_headers_original_dir() require the current
168    # working directory to be a direct or indirect subdirectory of
169    # ANDROID_BUILD_TOP.  Otherwise, these functions print an error message and
170    # exit.  Let's allow the user to run this program from an unrelated
171    # directory, if they specify src_dir and dst_dir on the command line.
172    if dst_dir is None:
173      dst_dir = get_kernel_dir()
174    if src_dir is None:
175      src_dir = get_kernel_headers_original_dir()
176
177    if len(args) == 0:
178        usage()
179
180    if no_update:
181        for path in args:
182            dst_file = os.path.join(dst_dir, path)
183            src_file = os.path.join(src_dir, path)
184            new_data = cleanupFile(dst_file, src_file, path)
185            # Use sys.stdout.write instead of a simple print statement to avoid
186            # sending an extra new line character to stdout.  Running this
187            # program in non-update mode and redirecting stdout to a file should
188            # yield the same result as using update mode, where new_data is
189            # written directly to a file.
190            sys.stdout.write(new_data)
191
192        sys.exit(0)
193
194    # Now let's update our files.
195
196    b = BatchFileUpdater()
197
198    for path in args:
199        dst_file = os.path.join(dst_dir, path)
200        src_file = os.path.join(src_dir, path)
201        new_data = cleanupFile(dst_file, src_file, path, no_update)
202        if not new_data:
203            continue
204
205        b.readFile(dst_file)
206        r = b.editFile(dst_file, new_data)
207        if r == 0:
208            r = "unchanged"
209        elif r == 1:
210            r = "edited"
211        else:
212            r = "added"
213
214        print "cleaning: %-*s -> %-*s (%s)" % (35, path, 35, path, r)
215
216
217    b.updateGitFiles()
218
219    sys.exit(0)
220