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_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
151if __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