• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.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
126if __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