| #!/usr/bin/env python3 |
| |
| #------------------------------------------------------------------------------ |
| # Description of the header clean process |
| #------------------------------------------------------------------------------ |
| # Here is the list of actions performed by this script to clean the original |
| # kernel headers. |
| # |
| # 1. Optimize well-known macros (e.g. __KERNEL__, __KERNEL_STRICT_NAMES) |
| # |
| # This pass gets rid of everything that is guarded by a well-known macro |
| # definition. This means that a block like: |
| # |
| # #ifdef __KERNEL__ |
| # .... |
| # #endif |
| # |
| # Will be totally omitted from the output. The optimizer is smart enough to |
| # handle all complex C-preprocessor conditional expression appropriately. |
| # This means that, for example: |
| # |
| # #if defined(__KERNEL__) || defined(FOO) |
| # ... |
| # #endif |
| # |
| # Will be transformed into: |
| # |
| # #ifdef FOO |
| # ... |
| # #endif |
| # |
| # See tools/defaults.py for the list of well-known macros used in this pass, |
| # in case you need to update it in the future. |
| # |
| # Note that this also removes any reference to a kernel-specific |
| # configuration macro like CONFIG_FOO from the clean headers. |
| # |
| # |
| # 2. Remove variable and function declarations: |
| # |
| # This pass scans non-directive text and only keeps things that look like a |
| # typedef/struct/union/enum declaration. This allows us to get rid of any |
| # variables or function declarations that should only be used within the |
| # kernel anyway (and which normally *should* be guarded by an #ifdef |
| # __KERNEL__ ... #endif block, if the kernel writers were not so messy). |
| # |
| # There are, however, a few exceptions: it is seldom useful to keep the |
| # definition of some static inline functions performing very simple |
| # operations. A good example is the optimized 32-bit byte-swap function |
| # found in: |
| # |
| # arch-arm/asm/byteorder.h |
| # |
| # The list of exceptions is in tools/defaults.py in case you need to update |
| # it in the future. |
| # |
| # Note that we do *not* remove macro definitions, including these macro that |
| # perform a call to one of these kernel-header functions, or even define other |
| # functions. We consider it safe since userland applications have no business |
| # using them anyway. |
| # |
| # |
| # 3. Add a standard disclaimer: |
| # |
| # The message: |
| # |
| # /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ |
| # |
| # Is prepended to each generated header. |
| #------------------------------------------------------------------------------ |
| |
| import sys, cpp, kernel, glob, os, re, getopt, textwrap |
| from defaults import * |
| from utils import * |
| |
| def print_error(no_update, msg): |
| if no_update: |
| panic(msg) |
| sys.stderr.write("warning: " + msg) |
| |
| |
| def cleanupFile(dst_file, src_file, rel_path, no_update = True): |
| """reads an original header and perform the cleanup operation on it |
| this functions returns the destination path and the clean header |
| as a single string""" |
| # Check the header path |
| if not os.path.exists(src_file): |
| print_error(no_update, "'%s' does not exist\n" % src_file) |
| return None |
| |
| if not os.path.isfile(src_file): |
| print_error(no_update, "'%s' is not a file\n" % src_file) |
| return None |
| |
| # Extract the architecture if found. |
| arch = None |
| m = re.search(r"(^|/)asm-([\w\d_\+\.\-]+)/.*", rel_path) |
| if m and m.group(2) != 'generic': |
| arch = m.group(2) |
| |
| # Now, let's parse the file. |
| parser = cpp.BlockParser() |
| blocks = parser.parseFile(src_file) |
| if not parser.parsed: |
| print_error(no_update, "Can't parse '%s'" % src_file) |
| return None |
| |
| macros = kernel_known_macros.copy() |
| if arch and arch in kernel_default_arch_macros: |
| macros.update(kernel_default_arch_macros[arch]) |
| |
| blocks.removeStructs(kernel_structs_to_remove) |
| blocks.optimizeMacros(macros) |
| blocks.optimizeIf01() |
| blocks.removeVarsAndFuncs(kernel_known_generic_statics) |
| blocks.replaceTokens(kernel_token_replacements) |
| |
| out = StringOutput() |
| out.write(textwrap.dedent("""\ |
| /* |
| * This file is auto-generated. Modifications will be lost. |
| * |
| * See https://android.googlesource.com/platform/bionic/+/master/libc/kernel/ |
| * for more information. |
| */ |
| """)) |
| blocks.write(out) |
| return out.get() |
| |
| |
| if __name__ == "__main__": |
| |
| def usage(): |
| print("""\ |
| usage: %s [options] <header_path> |
| |
| options: |
| -v enable verbose mode |
| |
| -u enabled update mode |
| this will try to update the corresponding 'clean header' |
| if the content has changed. with this, you can pass more |
| than one file on the command-line |
| |
| -k<path> specify path of original kernel headers |
| -d<path> specify path of cleaned kernel headers |
| |
| <header_path> must be in a subdirectory of 'original' |
| """ % os.path.basename(sys.argv[0])) |
| sys.exit(1) |
| |
| try: |
| optlist, args = getopt.getopt(sys.argv[1:], 'uvk:d:') |
| except: |
| # unrecognized option |
| sys.stderr.write("error: unrecognized option\n") |
| usage() |
| |
| no_update = True |
| dst_dir = None |
| src_dir = None |
| for opt, arg in optlist: |
| if opt == '-u': |
| no_update = False |
| elif opt == '-v': |
| logging.basicConfig(level=logging.DEBUG) |
| elif opt == '-k': |
| src_dir = arg |
| elif opt == '-d': |
| dst_dir = arg |
| # get_kernel_dir() and get_kernel_headers_original_dir() require the current |
| # working directory to be a direct or indirect subdirectory of |
| # ANDROID_BUILD_TOP. Otherwise, these functions print an error message and |
| # exit. Let's allow the user to run this program from an unrelated |
| # directory, if they specify src_dir and dst_dir on the command line. |
| if dst_dir is None: |
| dst_dir = get_kernel_dir() |
| if src_dir is None: |
| src_dir = get_kernel_headers_original_dir() |
| |
| if len(args) == 0: |
| usage() |
| |
| if no_update: |
| for path in args: |
| dst_file = os.path.join(dst_dir, path) |
| src_file = os.path.join(src_dir, path) |
| new_data = cleanupFile(dst_file, src_file, path) |
| # Use sys.stdout.write instead of a simple print statement to avoid |
| # sending an extra new line character to stdout. Running this |
| # program in non-update mode and redirecting stdout to a file should |
| # yield the same result as using update mode, where new_data is |
| # written directly to a file. |
| sys.stdout.write(new_data) |
| |
| sys.exit(0) |
| |
| # Now let's update our files. |
| |
| b = BatchFileUpdater() |
| |
| for path in args: |
| dst_file = os.path.join(dst_dir, path) |
| src_file = os.path.join(src_dir, path) |
| new_data = cleanupFile(dst_file, src_file, path, no_update) |
| if not new_data: |
| continue |
| |
| b.readFile(dst_file) |
| r = b.editFile(dst_file, new_data) |
| if r == 0: |
| r = "unchanged" |
| elif r == 1: |
| r = "edited" |
| else: |
| r = "added" |
| |
| print("cleaning: %-*s -> %-*s (%s)" % (35, path, 35, path, r)) |
| |
| b.updateFiles() |
| |
| sys.exit(0) |