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. Whitespace cleanup: 64# 65# The final pass removes any comments and empty lines from the final headers. 66# 67# 68# 4. Add a standard disclaimer: 69# 70# The message: 71# 72# /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 73# 74# Is prepended to each generated header. 75#------------------------------------------------------------------------------ 76 77import sys, cpp, kernel, glob, os, re, getopt 78from defaults import * 79from utils import * 80 81noUpdate = 1 82 83def cleanupFile(path, original_path): 84 """reads an original header and perform the cleanup operation on it 85 this functions returns the destination path and the clean header 86 as a single string""" 87 # check the header path 88 src_path = path 89 90 if not os.path.exists(src_path): 91 if noUpdate: 92 panic( "file does not exist: '%s'\n" % path ) 93 sys.stderr.write( "warning: file does not exit: %s\n" % path ) 94 return None, None 95 96 if not os.path.isfile(src_path): 97 if noUpdate: 98 panic( "path is not a file: '%s'\n" % path ) 99 sys.stderr.write( "warning: not a file: %s\n" % path ) 100 return None, None 101 102 if os.path.commonprefix( [ src_path, original_path ] ) != original_path: 103 if noUpdate: 104 panic( "file is not in 'original' directory: %s\n" % path ); 105 sys.stderr.write( "warning: file not in 'original' ignored: %s\n" % path ) 106 return None, None 107 108 src_path = src_path[len(original_path):] 109 if len(src_path) > 0 and src_path[0] == '/': 110 src_path = src_path[1:] 111 112 if len(src_path) == 0: 113 panic( "oops, internal error, can't extract correct relative path\n" ) 114 115 # convert into destination path, extracting architecture if needed 116 # and the corresponding list of known static functions 117 # 118 arch = None 119 statics = kernel_known_generic_statics 120 m = re.match(r"asm-([\w\d_\+\.\-]+)(/.*)", src_path) 121 if m and m.group(1) != 'generic': 122 dst_path = "arch-%s/asm/%s" % m.groups() 123 arch = m.group(1) 124 statics = statics.union( kernel_known_statics.get( arch, set() ) ) 125 else: 126 # process headers under the uapi directory 127 # note the "asm" level has been explicitly added in the original 128 # kernel header tree for architectural-dependent uapi headers 129 m_uapi = re.match(r"(uapi)/([\w\d_\+\.\-]+)(/.*)", src_path) 130 if m_uapi: 131 dst_path = src_path 132 m_uapi_arch = re.match(r"asm-([\w\d_\+\.\-]+)", m_uapi.group(2)) 133 if m_uapi_arch and m_uapi_arch.group(1) != 'generic': 134 arch = m_uapi_arch.group(1) 135 statics = statics.union( kernel_known_statics.get( arch, set() ) ) 136 # common headers (ie non-asm and non-uapi) 137 else: 138 dst_path = "common/" + src_path 139 140 dst_path = os.path.normpath( kernel_cleaned_path + "/" + dst_path ) 141 142 # now, let's parse the file 143 # 144 blocks = cpp.BlockParser().parseFile(path) 145 if not blocks: 146 sys.stderr.write( "error: can't parse '%s'" % path ) 147 sys.exit(1) 148 149 macros = kernel_known_macros.copy() 150 if arch and arch in kernel_default_arch_macros: 151 macros.update(kernel_default_arch_macros[arch]) 152 153 if arch and arch in kernel_arch_token_replacements: 154 blocks.replaceTokens( kernel_arch_token_replacements[arch] ) 155 156 blocks.optimizeMacros( macros ) 157 blocks.optimizeIf01() 158 blocks.removeVarsAndFuncs( statics ) 159 blocks.replaceTokens( kernel_token_replacements ) 160 blocks.removeComments() 161 blocks.removeMacroDefines( kernel_ignored_macros ) 162 blocks.removeWhiteSpace() 163 164 out = StringOutput() 165 out.write( kernel_disclaimer ) 166 blocks.writeWithWarning(out, kernel_warning, 4) 167 return dst_path, out.get() 168 169 170if __name__ == "__main__": 171 172 def usage(): 173 print """\ 174 usage: %s [options] <header_path> 175 176 options: 177 -v enable verbose mode 178 179 -u enabled update mode 180 this will try to update the corresponding 'clean header' 181 if the content has changed. with this, you can pass more 182 than one file on the command-line 183 184 -k<path> specify path of original kernel headers 185 -d<path> specify path of cleaned kernel headers 186 187 <header_path> must be in a subdirectory of 'original' 188 """ % os.path.basename(sys.argv[0]) 189 sys.exit(1) 190 191 try: 192 optlist, args = getopt.getopt( sys.argv[1:], 'uvk:d:' ) 193 except: 194 # unrecognized option 195 sys.stderr.write( "error: unrecognized option\n" ) 196 usage() 197 198 for opt, arg in optlist: 199 if opt == '-u': 200 noUpdate = 0 201 elif opt == '-v': 202 verbose = 1 203 D_setlevel(1) 204 elif opt == '-k': 205 kernel_original_path = arg 206 elif opt == '-d': 207 kernel_cleaned_path = arg 208 209 if len(args) == 0: 210 usage() 211 212 if noUpdate: 213 for path in args: 214 dst_path, newdata = cleanupFile(path,kernel_original_path) 215 print newdata 216 217 sys.exit(0) 218 219 # now let's update our files. 220 221 b = BatchFileUpdater() 222 223 for path in args: 224 dst_path, newdata = cleanupFile(path,kernel_original_path) 225 if not dst_path: 226 continue 227 228 b.readFile( dst_path ) 229 r = b.editFile( dst_path, newdata ) 230 if r == 0: 231 r = "unchanged" 232 elif r == 1: 233 r = "edited" 234 else: 235 r = "added" 236 237 print "cleaning: %-*s -> %-*s (%s)" % ( 35, path, 35, dst_path, r ) 238 239 240 b.updateGitFiles() 241 242 sys.exit(0) 243