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