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