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