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