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