You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
7.0 KiB
219 lines
7.0 KiB
#!/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
|
|
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])
|
|
|
|
if arch and arch in kernel_arch_token_replacements:
|
|
blocks.replaceTokens(kernel_arch_token_replacements[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(kernel_disclaimer)
|
|
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)
|