1*0e209d39SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project 2*0e209d39SAndroid Build Coastguard Worker# 3*0e209d39SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*0e209d39SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*0e209d39SAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*0e209d39SAndroid Build Coastguard Worker# 7*0e209d39SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*0e209d39SAndroid Build Coastguard Worker# 9*0e209d39SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*0e209d39SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*0e209d39SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*0e209d39SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*0e209d39SAndroid Build Coastguard Worker# limitations under the License. 14*0e209d39SAndroid Build Coastguard Worker# 15*0e209d39SAndroid Build Coastguard Worker 16*0e209d39SAndroid Build Coastguard Worker"""Utility for ICU4C code generation""" 17*0e209d39SAndroid Build Coastguard Worker 18*0e209d39SAndroid Build Coastguard Workerfrom __future__ import absolute_import 19*0e209d39SAndroid Build Coastguard Workerfrom __future__ import division 20*0e209d39SAndroid Build Coastguard Workerfrom __future__ import print_function 21*0e209d39SAndroid Build Coastguard Worker 22*0e209d39SAndroid Build Coastguard Workerimport logging 23*0e209d39SAndroid Build Coastguard Workerimport os 24*0e209d39SAndroid Build Coastguard Workerimport site 25*0e209d39SAndroid Build Coastguard Workerimport sys 26*0e209d39SAndroid Build Coastguard Workerimport textwrap 27*0e209d39SAndroid Build Coastguard Workerfrom collections import deque 28*0e209d39SAndroid Build Coastguard Worker 29*0e209d39SAndroid Build Coastguard Workerimport jinja2 30*0e209d39SAndroid Build Coastguard Worker 31*0e209d39SAndroid Build Coastguard WorkerTHIS_DIR = os.path.dirname(os.path.realpath(__file__)) 32*0e209d39SAndroid Build Coastguard WorkerANDROID_TOP = os.path.realpath(os.path.join(THIS_DIR, '../../../..')) 33*0e209d39SAndroid Build Coastguard Worker 34*0e209d39SAndroid Build Coastguard WorkerJINJA_ENV = jinja2.Environment(loader=jinja2.FileSystemLoader( 35*0e209d39SAndroid Build Coastguard Worker os.path.join(THIS_DIR, 'jinja_templates'))) 36*0e209d39SAndroid Build Coastguard WorkerJINJA_ENV.trim_blocks = True 37*0e209d39SAndroid Build Coastguard WorkerJINJA_ENV.lstrip_blocks = True 38*0e209d39SAndroid Build Coastguard Worker 39*0e209d39SAndroid Build Coastguard Workerdef generate_shim(functions, includes, suffix, template_file): 40*0e209d39SAndroid Build Coastguard Worker """Generates the library source file from the given functions.""" 41*0e209d39SAndroid Build Coastguard Worker data = { 42*0e209d39SAndroid Build Coastguard Worker 'functions': functions, 43*0e209d39SAndroid Build Coastguard Worker 'icu_headers': includes, 44*0e209d39SAndroid Build Coastguard Worker 'suffix': suffix, 45*0e209d39SAndroid Build Coastguard Worker } 46*0e209d39SAndroid Build Coastguard Worker return JINJA_ENV.get_template(template_file).render(data) 47*0e209d39SAndroid Build Coastguard Worker 48*0e209d39SAndroid Build Coastguard Workerdef generate_symbol_txt(shim_functions, extra_function_names, template_file): 49*0e209d39SAndroid Build Coastguard Worker """Generates the symbol txt file from the given functions.""" 50*0e209d39SAndroid Build Coastguard Worker data = { 51*0e209d39SAndroid Build Coastguard Worker # Each shim_function is given a suffix. 52*0e209d39SAndroid Build Coastguard Worker 'shim_functions' : shim_functions, 53*0e209d39SAndroid Build Coastguard Worker # Each extra function name is included as given. 54*0e209d39SAndroid Build Coastguard Worker 'extra_function_names': extra_function_names, 55*0e209d39SAndroid Build Coastguard Worker } 56*0e209d39SAndroid Build Coastguard Worker return JINJA_ENV.get_template(template_file).render(data) 57*0e209d39SAndroid Build Coastguard Worker 58*0e209d39SAndroid Build Coastguard Workerdef get_jinja_env(): 59*0e209d39SAndroid Build Coastguard Worker """Return a jinja2 environment""" 60*0e209d39SAndroid Build Coastguard Worker return JINJA_ENV 61*0e209d39SAndroid Build Coastguard Worker 62*0e209d39SAndroid Build Coastguard Workerdef get_allowlisted_apis(allowlist_file): 63*0e209d39SAndroid Build Coastguard Worker """Return all allowlisted API in allowlist_file""" 64*0e209d39SAndroid Build Coastguard Worker allowlisted_apis = set() 65*0e209d39SAndroid Build Coastguard Worker with open(os.path.join(THIS_DIR, allowlist_file), 'r') as file: 66*0e209d39SAndroid Build Coastguard Worker for line in file: 67*0e209d39SAndroid Build Coastguard Worker line = line.strip() 68*0e209d39SAndroid Build Coastguard Worker if line and not line.startswith("#"): 69*0e209d39SAndroid Build Coastguard Worker allowlisted_apis.add(line) 70*0e209d39SAndroid Build Coastguard Worker return allowlisted_apis 71*0e209d39SAndroid Build Coastguard Worker 72*0e209d39SAndroid Build Coastguard Workerdef android_path(*args): 73*0e209d39SAndroid Build Coastguard Worker """Returns the absolute path to a directory within the Android tree.""" 74*0e209d39SAndroid Build Coastguard Worker return os.path.join(ANDROID_TOP, *args) 75*0e209d39SAndroid Build Coastguard Worker 76*0e209d39SAndroid Build Coastguard Worker 77*0e209d39SAndroid Build Coastguard Workerdef get_clang_path(): 78*0e209d39SAndroid Build Coastguard Worker """Find the latest clang version and return the full path""" 79*0e209d39SAndroid Build Coastguard Worker base_path = android_path('prebuilts/clang/host/linux-x86/') 80*0e209d39SAndroid Build Coastguard Worker files = [f for f in os.listdir(base_path) if f.startswith('clang-r')] 81*0e209d39SAndroid Build Coastguard Worker # TODO: Don't use sort() because it assumes the same number of digits in the version name 82*0e209d39SAndroid Build Coastguard Worker files.sort(reverse=True) 83*0e209d39SAndroid Build Coastguard Worker selected = files[0] 84*0e209d39SAndroid Build Coastguard Worker print("Using clang version %s" % selected) 85*0e209d39SAndroid Build Coastguard Worker path = os.path.join(base_path, selected) 86*0e209d39SAndroid Build Coastguard Worker return path 87*0e209d39SAndroid Build Coastguard Worker 88*0e209d39SAndroid Build Coastguard Worker 89*0e209d39SAndroid Build Coastguard Workerdef get_clang_lib_path(clang_path): 90*0e209d39SAndroid Build Coastguard Worker """Return the libclang.so path""" 91*0e209d39SAndroid Build Coastguard Worker base_path = os.path.join(clang_path, 'lib') 92*0e209d39SAndroid Build Coastguard Worker files = [f for f in os.listdir(base_path) if f.startswith('libclang.so')] 93*0e209d39SAndroid Build Coastguard Worker return os.path.join(base_path, files[0]) 94*0e209d39SAndroid Build Coastguard Worker 95*0e209d39SAndroid Build Coastguard Worker 96*0e209d39SAndroid Build Coastguard Workerdef get_clang_header_dir(clang_path): 97*0e209d39SAndroid Build Coastguard Worker """Return the path to clang header directory""" 98*0e209d39SAndroid Build Coastguard Worker base_path = os.path.join(clang_path, 'lib/clang/') 99*0e209d39SAndroid Build Coastguard Worker files = os.listdir(base_path) 100*0e209d39SAndroid Build Coastguard Worker return os.path.join(base_path, files[0], 'include/') 101*0e209d39SAndroid Build Coastguard Worker 102*0e209d39SAndroid Build Coastguard Worker 103*0e209d39SAndroid Build Coastguard WorkerCLANG_PATH = get_clang_path() 104*0e209d39SAndroid Build Coastguard WorkerCLANG_LIB_PATH = get_clang_lib_path(CLANG_PATH) 105*0e209d39SAndroid Build Coastguard WorkerCLANG_HEADER_PATH = get_clang_header_dir(CLANG_PATH) 106*0e209d39SAndroid Build Coastguard Worker 107*0e209d39SAndroid Build Coastguard Workersite.addsitedir(os.path.join(CLANG_PATH, 'lib/python3/site-packages/')) 108*0e209d39SAndroid Build Coastguard Workerimport clang.cindex # pylint: disable=import-error,wrong-import-position 109*0e209d39SAndroid Build Coastguard Worker 110*0e209d39SAndroid Build Coastguard Worker 111*0e209d39SAndroid Build Coastguard Workerclass Function: 112*0e209d39SAndroid Build Coastguard Worker """A visible function found in an ICU header.""" 113*0e209d39SAndroid Build Coastguard Worker 114*0e209d39SAndroid Build Coastguard Worker def __init__(self, name, result_type, params, is_variadic, module): 115*0e209d39SAndroid Build Coastguard Worker self.name = name 116*0e209d39SAndroid Build Coastguard Worker self.result_type = result_type 117*0e209d39SAndroid Build Coastguard Worker self.params = params 118*0e209d39SAndroid Build Coastguard Worker self.is_variadic = is_variadic 119*0e209d39SAndroid Build Coastguard Worker self.va_list_insert_position = -1 120*0e209d39SAndroid Build Coastguard Worker 121*0e209d39SAndroid Build Coastguard Worker # callee will be used in dlsym and may be identical to others for 122*0e209d39SAndroid Build Coastguard Worker # functions with variable argument lists. 123*0e209d39SAndroid Build Coastguard Worker self.callee = self.name 124*0e209d39SAndroid Build Coastguard Worker if self.is_variadic: 125*0e209d39SAndroid Build Coastguard Worker self.last_param = self.params[-1][1] 126*0e209d39SAndroid Build Coastguard Worker self.handle = 'handle_' + module 127*0e209d39SAndroid Build Coastguard Worker self.return_void = self.result_type == 'void' 128*0e209d39SAndroid Build Coastguard Worker 129*0e209d39SAndroid Build Coastguard Worker @property 130*0e209d39SAndroid Build Coastguard Worker def param_str(self): 131*0e209d39SAndroid Build Coastguard Worker """Returns a string usable as a parameter list in a function decl.""" 132*0e209d39SAndroid Build Coastguard Worker params = [] 133*0e209d39SAndroid Build Coastguard Worker for param_type, param_name in self.params: 134*0e209d39SAndroid Build Coastguard Worker if '[' in param_type: 135*0e209d39SAndroid Build Coastguard Worker # `int foo[42]` will be a param_type of `int [42]` and a 136*0e209d39SAndroid Build Coastguard Worker # param_name of `foo`. We need to put these back in the right 137*0e209d39SAndroid Build Coastguard Worker # order. 138*0e209d39SAndroid Build Coastguard Worker param_name += param_type[param_type.find('['):] 139*0e209d39SAndroid Build Coastguard Worker param_type = param_type[:param_type.find('[')] 140*0e209d39SAndroid Build Coastguard Worker params.append('{} {}'.format(param_type, param_name)) 141*0e209d39SAndroid Build Coastguard Worker if self.is_variadic: 142*0e209d39SAndroid Build Coastguard Worker params.append('...') 143*0e209d39SAndroid Build Coastguard Worker return ', '.join(params) 144*0e209d39SAndroid Build Coastguard Worker 145*0e209d39SAndroid Build Coastguard Worker @property 146*0e209d39SAndroid Build Coastguard Worker def arg_str(self): 147*0e209d39SAndroid Build Coastguard Worker """Returns a string usable as an argument list in a function call.""" 148*0e209d39SAndroid Build Coastguard Worker args = [] 149*0e209d39SAndroid Build Coastguard Worker for _, param_name in self.params: 150*0e209d39SAndroid Build Coastguard Worker args.append(param_name) 151*0e209d39SAndroid Build Coastguard Worker if self.is_variadic: 152*0e209d39SAndroid Build Coastguard Worker if self.va_list_insert_position >= 0: 153*0e209d39SAndroid Build Coastguard Worker args.insert(self.va_list_insert_position, 'args') 154*0e209d39SAndroid Build Coastguard Worker else: 155*0e209d39SAndroid Build Coastguard Worker raise ValueError(textwrap.dedent("""\ 156*0e209d39SAndroid Build Coastguard Worker {}({}) is variadic, but has no valid \ 157*0e209d39SAndroid Build Coastguard Worker inserted position""".format( 158*0e209d39SAndroid Build Coastguard Worker self.name, 159*0e209d39SAndroid Build Coastguard Worker self.param_str))) 160*0e209d39SAndroid Build Coastguard Worker return ', '.join(args) 161*0e209d39SAndroid Build Coastguard Worker 162*0e209d39SAndroid Build Coastguard Worker def set_variadic_callee(self, callee, inserted_position): 163*0e209d39SAndroid Build Coastguard Worker """Set variadic callee with callee name and inserted position""" 164*0e209d39SAndroid Build Coastguard Worker if self.is_variadic: 165*0e209d39SAndroid Build Coastguard Worker self.callee = callee 166*0e209d39SAndroid Build Coastguard Worker self.va_list_insert_position = inserted_position 167*0e209d39SAndroid Build Coastguard Worker 168*0e209d39SAndroid Build Coastguard Worker 169*0e209d39SAndroid Build Coastguard Workerdef logger(): 170*0e209d39SAndroid Build Coastguard Worker """Returns the module level logger.""" 171*0e209d39SAndroid Build Coastguard Worker return logging.getLogger(__name__) 172*0e209d39SAndroid Build Coastguard Worker 173*0e209d39SAndroid Build Coastguard Worker 174*0e209d39SAndroid Build Coastguard Workerclass DeclaredFunctionsParser: 175*0e209d39SAndroid Build Coastguard Worker """Parser to get declared functions from ICU4C headers. """ 176*0e209d39SAndroid Build Coastguard Worker 177*0e209d39SAndroid Build Coastguard Worker def __init__(self, decl_filters, allowlisted_decl_filter): 178*0e209d39SAndroid Build Coastguard Worker """ 179*0e209d39SAndroid Build Coastguard Worker Args: 180*0e209d39SAndroid Build Coastguard Worker decl_filters: A list of filters for declared functions. 181*0e209d39SAndroid Build Coastguard Worker allowlisted_decl_filter: A list of allowlisting filters for declared functions. 182*0e209d39SAndroid Build Coastguard Worker If the function is allowlisted here, the function will not filtered by the filter added 183*0e209d39SAndroid Build Coastguard Worker in decl_filters 184*0e209d39SAndroid Build Coastguard Worker """ 185*0e209d39SAndroid Build Coastguard Worker self.decl_filters = decl_filters 186*0e209d39SAndroid Build Coastguard Worker self.allowlisted_decl_filters = allowlisted_decl_filter 187*0e209d39SAndroid Build Coastguard Worker self.va_functions_mapping = {} 188*0e209d39SAndroid Build Coastguard Worker self.ignored_include_dependency = {} 189*0e209d39SAndroid Build Coastguard Worker 190*0e209d39SAndroid Build Coastguard Worker # properties to store the parsing result 191*0e209d39SAndroid Build Coastguard Worker self.all_headers = [] 192*0e209d39SAndroid Build Coastguard Worker self.all_header_paths_to_copy = set() 193*0e209d39SAndroid Build Coastguard Worker self.all_declared_functions = [] 194*0e209d39SAndroid Build Coastguard Worker self.seen_functions = set() 195*0e209d39SAndroid Build Coastguard Worker self.all_header_to_function_names = {} 196*0e209d39SAndroid Build Coastguard Worker 197*0e209d39SAndroid Build Coastguard Worker # Configures libclang to load in our environment 198*0e209d39SAndroid Build Coastguard Worker # Set up LD_LIBRARY_PATH to include libclang.so, libLLVM.so, etc. Note 199*0e209d39SAndroid Build Coastguard Worker # that setting LD_LIBRARY_PATH with os.putenv() sometimes doesn't help. 200*0e209d39SAndroid Build Coastguard Worker clang.cindex.Config.set_library_file(CLANG_LIB_PATH) 201*0e209d39SAndroid Build Coastguard Worker 202*0e209d39SAndroid Build Coastguard Worker def set_va_functions_mapping(self, mapping): 203*0e209d39SAndroid Build Coastguard Worker """Set mapping from a variable argument function to an implementation. 204*0e209d39SAndroid Build Coastguard Worker 205*0e209d39SAndroid Build Coastguard Worker Functions w/ variable argument lists (...) need special care to call 206*0e209d39SAndroid Build Coastguard Worker their corresponding v- versions that accept a va_list argument. Note that 207*0e209d39SAndroid Build Coastguard Worker although '...' will always appear as the last parameter, its v- version 208*0e209d39SAndroid Build Coastguard Worker may put the va_list arg in a different place. Hence we provide an index 209*0e209d39SAndroid Build Coastguard Worker to indicate the position. 210*0e209d39SAndroid Build Coastguard Worker e.g. 'umsg_format': ('umsg_vformat', 3) means in the wrapper function of 211*0e209d39SAndroid Build Coastguard Worker 'umsg_format', it will call 'umsg_vformat' instead, with the va_list arg 212*0e209d39SAndroid Build Coastguard Worker inserted as the 3rd argument.""" 213*0e209d39SAndroid Build Coastguard Worker self.va_functions_mapping = mapping 214*0e209d39SAndroid Build Coastguard Worker 215*0e209d39SAndroid Build Coastguard Worker def set_ignored_include_dependency(self, mapping): 216*0e209d39SAndroid Build Coastguard Worker """ 217*0e209d39SAndroid Build Coastguard Worker A sample mapping is { "ulocdata.h" : [ "uloc.h", "ures.h" ] }. 218*0e209d39SAndroid Build Coastguard Worker The include dependencies will explicitly be ignored when producing header_paths_to_copy. 219*0e209d39SAndroid Build Coastguard Worker """ 220*0e209d39SAndroid Build Coastguard Worker self.ignored_include_dependency = mapping 221*0e209d39SAndroid Build Coastguard Worker 222*0e209d39SAndroid Build Coastguard Worker @property 223*0e209d39SAndroid Build Coastguard Worker def header_includes(self): 224*0e209d39SAndroid Build Coastguard Worker """Return all headers declaring the functions returned in get_all_declared_functions. 225*0e209d39SAndroid Build Coastguard Worker 226*0e209d39SAndroid Build Coastguard Worker If all functions in the header are filtered, the header is not included in here.""" 227*0e209d39SAndroid Build Coastguard Worker return [DeclaredFunctionsParser.short_header_path(header) for header in self.all_headers] 228*0e209d39SAndroid Build Coastguard Worker 229*0e209d39SAndroid Build Coastguard Worker @property 230*0e209d39SAndroid Build Coastguard Worker def header_paths_to_copy(self): 231*0e209d39SAndroid Build Coastguard Worker """Return all headers needed to be copied""" 232*0e209d39SAndroid Build Coastguard Worker return self.all_header_paths_to_copy 233*0e209d39SAndroid Build Coastguard Worker 234*0e209d39SAndroid Build Coastguard Worker @property 235*0e209d39SAndroid Build Coastguard Worker def declared_functions(self): 236*0e209d39SAndroid Build Coastguard Worker """Return all declared functions after filtering""" 237*0e209d39SAndroid Build Coastguard Worker return self.all_declared_functions 238*0e209d39SAndroid Build Coastguard Worker 239*0e209d39SAndroid Build Coastguard Worker @property 240*0e209d39SAndroid Build Coastguard Worker def header_to_function_names(self): 241*0e209d39SAndroid Build Coastguard Worker """Return the mapping from the header file name to a list of function names in the file""" 242*0e209d39SAndroid Build Coastguard Worker return self.all_header_to_function_names 243*0e209d39SAndroid Build Coastguard Worker 244*0e209d39SAndroid Build Coastguard Worker @staticmethod 245*0e209d39SAndroid Build Coastguard Worker def get_cflags(): 246*0e209d39SAndroid Build Coastguard Worker """Returns the cflags that should be used for parsing.""" 247*0e209d39SAndroid Build Coastguard Worker clang_flags = [ 248*0e209d39SAndroid Build Coastguard Worker '-x', 249*0e209d39SAndroid Build Coastguard Worker 'c', 250*0e209d39SAndroid Build Coastguard Worker '-std=c99', 251*0e209d39SAndroid Build Coastguard Worker '-DU_DISABLE_RENAMING=1', 252*0e209d39SAndroid Build Coastguard Worker '-DU_SHOW_CPLUSPLUS_API=0', 253*0e209d39SAndroid Build Coastguard Worker '-DU_HIDE_DRAFT_API', 254*0e209d39SAndroid Build Coastguard Worker '-DU_HIDE_DEPRECATED_API', 255*0e209d39SAndroid Build Coastguard Worker '-DU_HIDE_INTERNAL_API', 256*0e209d39SAndroid Build Coastguard Worker '-DANDROID_LINK_SHARED_ICU4C', 257*0e209d39SAndroid Build Coastguard Worker ] 258*0e209d39SAndroid Build Coastguard Worker 259*0e209d39SAndroid Build Coastguard Worker include_dirs = [ 260*0e209d39SAndroid Build Coastguard Worker CLANG_HEADER_PATH, 261*0e209d39SAndroid Build Coastguard Worker android_path('bionic/libc/include'), 262*0e209d39SAndroid Build Coastguard Worker android_path('external/icu/android_icu4c/include'), 263*0e209d39SAndroid Build Coastguard Worker android_path('external/icu/icu4c/source/common'), 264*0e209d39SAndroid Build Coastguard Worker android_path('external/icu/icu4c/source/i18n'), 265*0e209d39SAndroid Build Coastguard Worker ] 266*0e209d39SAndroid Build Coastguard Worker 267*0e209d39SAndroid Build Coastguard Worker for include_dir in include_dirs: 268*0e209d39SAndroid Build Coastguard Worker clang_flags.append('-I' + include_dir) 269*0e209d39SAndroid Build Coastguard Worker return clang_flags 270*0e209d39SAndroid Build Coastguard Worker 271*0e209d39SAndroid Build Coastguard Worker @staticmethod 272*0e209d39SAndroid Build Coastguard Worker def get_all_cpp_headers(): 273*0e209d39SAndroid Build Coastguard Worker """Return all C++ header names in icu4c/source/test/hdrtst/cxxfiles.txt""" 274*0e209d39SAndroid Build Coastguard Worker cpp_headers = [] 275*0e209d39SAndroid Build Coastguard Worker with open(android_path('external/icu/tools/icu4c_srcgen/cxxfiles.txt'), 'r') as file: 276*0e209d39SAndroid Build Coastguard Worker for line in file: 277*0e209d39SAndroid Build Coastguard Worker line = line.strip() 278*0e209d39SAndroid Build Coastguard Worker if not line.startswith("#"): 279*0e209d39SAndroid Build Coastguard Worker cpp_headers.append(line) 280*0e209d39SAndroid Build Coastguard Worker return cpp_headers 281*0e209d39SAndroid Build Coastguard Worker 282*0e209d39SAndroid Build Coastguard Worker def parse(self): 283*0e209d39SAndroid Build Coastguard Worker """Parse the headers and collect the declared functions after filtering 284*0e209d39SAndroid Build Coastguard Worker and the headers containing the functions.""" 285*0e209d39SAndroid Build Coastguard Worker index = clang.cindex.Index.create() 286*0e209d39SAndroid Build Coastguard Worker 287*0e209d39SAndroid Build Coastguard Worker icu_modules = ( 288*0e209d39SAndroid Build Coastguard Worker 'common', 289*0e209d39SAndroid Build Coastguard Worker 'i18n', 290*0e209d39SAndroid Build Coastguard Worker ) 291*0e209d39SAndroid Build Coastguard Worker header_dependencies = {} 292*0e209d39SAndroid Build Coastguard Worker for module in icu_modules: 293*0e209d39SAndroid Build Coastguard Worker path = android_path(android_path('external/icu/icu4c/source', module, 'unicode')) 294*0e209d39SAndroid Build Coastguard Worker files = [os.path.join(path, f) 295*0e209d39SAndroid Build Coastguard Worker for f in os.listdir(path) if f.endswith('.h')] 296*0e209d39SAndroid Build Coastguard Worker 297*0e209d39SAndroid Build Coastguard Worker for file_path in files: 298*0e209d39SAndroid Build Coastguard Worker base_header_name = os.path.basename(file_path) 299*0e209d39SAndroid Build Coastguard Worker # Ignore C++ headers. 300*0e209d39SAndroid Build Coastguard Worker if base_header_name in DeclaredFunctionsParser.get_all_cpp_headers(): 301*0e209d39SAndroid Build Coastguard Worker continue 302*0e209d39SAndroid Build Coastguard Worker 303*0e209d39SAndroid Build Coastguard Worker tunit = index.parse(file_path, DeclaredFunctionsParser.get_cflags()) 304*0e209d39SAndroid Build Coastguard Worker DeclaredFunctionsParser.handle_diagnostics(tunit) 305*0e209d39SAndroid Build Coastguard Worker header_dependencies[file_path] = [file_inclusion.include.name for file_inclusion 306*0e209d39SAndroid Build Coastguard Worker in tunit.get_includes()] 307*0e209d39SAndroid Build Coastguard Worker visible_functions = self.get_visible_functions( 308*0e209d39SAndroid Build Coastguard Worker tunit.cursor, module, file_path) 309*0e209d39SAndroid Build Coastguard Worker self.all_header_to_function_names[base_header_name] = \ 310*0e209d39SAndroid Build Coastguard Worker [f.name for f in visible_functions] 311*0e209d39SAndroid Build Coastguard Worker for function in visible_functions: 312*0e209d39SAndroid Build Coastguard Worker self.seen_functions.add(function.name) 313*0e209d39SAndroid Build Coastguard Worker self.all_declared_functions.append(function) 314*0e209d39SAndroid Build Coastguard Worker if visible_functions: 315*0e209d39SAndroid Build Coastguard Worker self.all_headers.append(file_path) 316*0e209d39SAndroid Build Coastguard Worker 317*0e209d39SAndroid Build Coastguard Worker # Sort to produce an deterministic output 318*0e209d39SAndroid Build Coastguard Worker self.all_declared_functions = sorted(self.all_declared_functions, key=lambda f: f.name) 319*0e209d39SAndroid Build Coastguard Worker self.all_headers = sorted(self.all_headers) 320*0e209d39SAndroid Build Coastguard Worker 321*0e209d39SAndroid Build Coastguard Worker # Build the headers required for using your restricted API set, and put the set into 322*0e209d39SAndroid Build Coastguard Worker # all_header_files_to_copy. 323*0e209d39SAndroid Build Coastguard Worker # header_dependencies is a map from icu4c header file path to a list of included headers. 324*0e209d39SAndroid Build Coastguard Worker # The key must be a ICU4C header, but the value could contain non-ICU4C headers, e.g. 325*0e209d39SAndroid Build Coastguard Worker # { 326*0e209d39SAndroid Build Coastguard Worker # ".../icu4c/source/common/unicode/utype.h": [ 327*0e209d39SAndroid Build Coastguard Worker # ".../icu4c/source/common/unicode/uversion.h", 328*0e209d39SAndroid Build Coastguard Worker # ".../bionic/libc/include/ctype.h", 329*0e209d39SAndroid Build Coastguard Worker # ], 330*0e209d39SAndroid Build Coastguard Worker # ... 331*0e209d39SAndroid Build Coastguard Worker # } 332*0e209d39SAndroid Build Coastguard Worker file_queue = deque() 333*0e209d39SAndroid Build Coastguard Worker file_processed = set() 334*0e209d39SAndroid Build Coastguard Worker for header in self.all_headers: 335*0e209d39SAndroid Build Coastguard Worker file_queue.appendleft(header) 336*0e209d39SAndroid Build Coastguard Worker self.all_header_paths_to_copy.add(header) 337*0e209d39SAndroid Build Coastguard Worker while file_queue: 338*0e209d39SAndroid Build Coastguard Worker file = file_queue.pop() 339*0e209d39SAndroid Build Coastguard Worker file_basename = os.path.basename(file) 340*0e209d39SAndroid Build Coastguard Worker if file in file_processed: 341*0e209d39SAndroid Build Coastguard Worker continue 342*0e209d39SAndroid Build Coastguard Worker file_processed.add(file) 343*0e209d39SAndroid Build Coastguard Worker for header in header_dependencies[file]: 344*0e209d39SAndroid Build Coastguard Worker header_basename = os.path.basename(header) 345*0e209d39SAndroid Build Coastguard Worker # Skip this header if this dependency is explicitly ignored 346*0e209d39SAndroid Build Coastguard Worker if file_basename in self.ignored_include_dependency and \ 347*0e209d39SAndroid Build Coastguard Worker header_basename in self.ignored_include_dependency[file_basename]: 348*0e209d39SAndroid Build Coastguard Worker continue 349*0e209d39SAndroid Build Coastguard Worker if header in header_dependencies: # Do not care non-icu4c headers 350*0e209d39SAndroid Build Coastguard Worker self.all_header_paths_to_copy.add(header) 351*0e209d39SAndroid Build Coastguard Worker file_queue.appendleft(header) 352*0e209d39SAndroid Build Coastguard Worker 353*0e209d39SAndroid Build Coastguard Worker @staticmethod 354*0e209d39SAndroid Build Coastguard Worker def handle_diagnostics(tunit): 355*0e209d39SAndroid Build Coastguard Worker """Prints compiler diagnostics to stdout. Exits if errors occurred.""" 356*0e209d39SAndroid Build Coastguard Worker errors = 0 357*0e209d39SAndroid Build Coastguard Worker for diag in tunit.diagnostics: 358*0e209d39SAndroid Build Coastguard Worker if diag.severity == clang.cindex.Diagnostic.Fatal: 359*0e209d39SAndroid Build Coastguard Worker level = logging.CRITICAL 360*0e209d39SAndroid Build Coastguard Worker errors += 1 361*0e209d39SAndroid Build Coastguard Worker elif diag.severity == clang.cindex.Diagnostic.Error: 362*0e209d39SAndroid Build Coastguard Worker level = logging.ERROR 363*0e209d39SAndroid Build Coastguard Worker errors += 1 364*0e209d39SAndroid Build Coastguard Worker elif diag.severity == clang.cindex.Diagnostic.Warning: 365*0e209d39SAndroid Build Coastguard Worker level = logging.WARNING 366*0e209d39SAndroid Build Coastguard Worker elif diag.severity == clang.cindex.Diagnostic.Note: 367*0e209d39SAndroid Build Coastguard Worker level = logging.INFO 368*0e209d39SAndroid Build Coastguard Worker logger().log( 369*0e209d39SAndroid Build Coastguard Worker level, '%s:%s:%s %s', diag.location.file, diag.location.line, 370*0e209d39SAndroid Build Coastguard Worker diag.location.column, diag.spelling) 371*0e209d39SAndroid Build Coastguard Worker if errors: 372*0e209d39SAndroid Build Coastguard Worker sys.exit('Errors occurred during parsing. Exiting.') 373*0e209d39SAndroid Build Coastguard Worker 374*0e209d39SAndroid Build Coastguard Worker def get_visible_functions(self, cursor, module, file_name): 375*0e209d39SAndroid Build Coastguard Worker """Returns a list of all visible functions in a header file.""" 376*0e209d39SAndroid Build Coastguard Worker functions = [] 377*0e209d39SAndroid Build Coastguard Worker for child in cursor.get_children(): 378*0e209d39SAndroid Build Coastguard Worker if self.should_process_decl(child, file_name): 379*0e209d39SAndroid Build Coastguard Worker functions.append(self.from_cursor(child, module)) 380*0e209d39SAndroid Build Coastguard Worker return functions 381*0e209d39SAndroid Build Coastguard Worker 382*0e209d39SAndroid Build Coastguard Worker def should_process_decl(self, decl, file_name): 383*0e209d39SAndroid Build Coastguard Worker """Returns True if this function needs to be processed.""" 384*0e209d39SAndroid Build Coastguard Worker if decl.kind != clang.cindex.CursorKind.FUNCTION_DECL: 385*0e209d39SAndroid Build Coastguard Worker return False 386*0e209d39SAndroid Build Coastguard Worker if decl.location.file.name != file_name: 387*0e209d39SAndroid Build Coastguard Worker return False 388*0e209d39SAndroid Build Coastguard Worker if decl.spelling in self.seen_functions: 389*0e209d39SAndroid Build Coastguard Worker return False 390*0e209d39SAndroid Build Coastguard Worker if not DeclaredFunctionsParser.is_function_visible(decl): 391*0e209d39SAndroid Build Coastguard Worker return False 392*0e209d39SAndroid Build Coastguard Worker for allowlisted_decl_filter in self.allowlisted_decl_filters: 393*0e209d39SAndroid Build Coastguard Worker if allowlisted_decl_filter(decl): 394*0e209d39SAndroid Build Coastguard Worker return True 395*0e209d39SAndroid Build Coastguard Worker for decl_filter in self.decl_filters: 396*0e209d39SAndroid Build Coastguard Worker if not decl_filter(decl): 397*0e209d39SAndroid Build Coastguard Worker return False 398*0e209d39SAndroid Build Coastguard Worker return True 399*0e209d39SAndroid Build Coastguard Worker 400*0e209d39SAndroid Build Coastguard Worker @staticmethod 401*0e209d39SAndroid Build Coastguard Worker def is_function_visible(decl): 402*0e209d39SAndroid Build Coastguard Worker """Returns True if the function has default visibility.""" 403*0e209d39SAndroid Build Coastguard Worker visible = False 404*0e209d39SAndroid Build Coastguard Worker vis_attrs = DeclaredFunctionsParser.get_children_by_kind( 405*0e209d39SAndroid Build Coastguard Worker decl, clang.cindex.CursorKind.VISIBILITY_ATTR) 406*0e209d39SAndroid Build Coastguard Worker for child in vis_attrs: 407*0e209d39SAndroid Build Coastguard Worker visible = child.spelling == 'default' 408*0e209d39SAndroid Build Coastguard Worker return visible 409*0e209d39SAndroid Build Coastguard Worker 410*0e209d39SAndroid Build Coastguard Worker @staticmethod 411*0e209d39SAndroid Build Coastguard Worker def get_children_by_kind(cursor, kind): 412*0e209d39SAndroid Build Coastguard Worker """Returns a generator of cursor's children of a specific kind.""" 413*0e209d39SAndroid Build Coastguard Worker for child in cursor.get_children(): 414*0e209d39SAndroid Build Coastguard Worker if child.kind == kind: 415*0e209d39SAndroid Build Coastguard Worker yield child 416*0e209d39SAndroid Build Coastguard Worker 417*0e209d39SAndroid Build Coastguard Worker @staticmethod 418*0e209d39SAndroid Build Coastguard Worker def short_header_path(name): 419*0e209d39SAndroid Build Coastguard Worker """Trim the given file name to 'unicode/xyz.h'.""" 420*0e209d39SAndroid Build Coastguard Worker return name[name.rfind('unicode/'):] 421*0e209d39SAndroid Build Coastguard Worker 422*0e209d39SAndroid Build Coastguard Worker def from_cursor(self, cursor, module): 423*0e209d39SAndroid Build Coastguard Worker """Creates a Function object from the decl at the cursor.""" 424*0e209d39SAndroid Build Coastguard Worker if cursor.type.kind != clang.cindex.TypeKind.FUNCTIONPROTO: 425*0e209d39SAndroid Build Coastguard Worker raise ValueError(textwrap.dedent("""\ 426*0e209d39SAndroid Build Coastguard Worker {}'s type kind is {}, expected TypeKind.FUNCTIONPROTO. 427*0e209d39SAndroid Build Coastguard Worker {} Line {} Column {}""".format( 428*0e209d39SAndroid Build Coastguard Worker cursor.spelling, 429*0e209d39SAndroid Build Coastguard Worker cursor.type.kind, 430*0e209d39SAndroid Build Coastguard Worker cursor.location.file, 431*0e209d39SAndroid Build Coastguard Worker cursor.location.line, 432*0e209d39SAndroid Build Coastguard Worker cursor.location.column))) 433*0e209d39SAndroid Build Coastguard Worker 434*0e209d39SAndroid Build Coastguard Worker name = cursor.spelling 435*0e209d39SAndroid Build Coastguard Worker result_type = cursor.result_type.spelling 436*0e209d39SAndroid Build Coastguard Worker is_variadic = cursor.type.is_function_variadic() 437*0e209d39SAndroid Build Coastguard Worker params = [] 438*0e209d39SAndroid Build Coastguard Worker for arg in cursor.get_arguments(): 439*0e209d39SAndroid Build Coastguard Worker params.append((arg.type.spelling, arg.spelling)) 440*0e209d39SAndroid Build Coastguard Worker function = Function(name, result_type, params, is_variadic, module) 441*0e209d39SAndroid Build Coastguard Worker # For variadic function, set the callee and va_list position 442*0e209d39SAndroid Build Coastguard Worker if function.is_variadic and function.name in self.va_functions_mapping: 443*0e209d39SAndroid Build Coastguard Worker va_func = self.va_functions_mapping[function.name] 444*0e209d39SAndroid Build Coastguard Worker function.set_variadic_callee(va_func[0], va_func[1]) 445*0e209d39SAndroid Build Coastguard Worker return function 446*0e209d39SAndroid Build Coastguard Worker 447*0e209d39SAndroid Build Coastguard Worker 448*0e209d39SAndroid Build Coastguard Workerclass StableDeclarationFilter: 449*0e209d39SAndroid Build Coastguard Worker """Return true if it's @stable API""" 450*0e209d39SAndroid Build Coastguard Worker def __call__(self, decl): 451*0e209d39SAndroid Build Coastguard Worker """Returns True if the given decl has a doxygen stable tag.""" 452*0e209d39SAndroid Build Coastguard Worker if not decl.raw_comment: 453*0e209d39SAndroid Build Coastguard Worker return False 454*0e209d39SAndroid Build Coastguard Worker if '@stable' in decl.raw_comment: 455*0e209d39SAndroid Build Coastguard Worker return True 456*0e209d39SAndroid Build Coastguard Worker return False 457*0e209d39SAndroid Build Coastguard Worker 458*0e209d39SAndroid Build Coastguard Worker 459*0e209d39SAndroid Build Coastguard Workerclass AllowlistedDeclarationFilter: 460*0e209d39SAndroid Build Coastguard Worker """A filter for allowlisting function declarations.""" 461*0e209d39SAndroid Build Coastguard Worker def __init__(self, allowlisted_function_names): 462*0e209d39SAndroid Build Coastguard Worker self.allowlisted_function_names = allowlisted_function_names 463*0e209d39SAndroid Build Coastguard Worker 464*0e209d39SAndroid Build Coastguard Worker def __call__(self, decl): 465*0e209d39SAndroid Build Coastguard Worker """Returns True if the given decl is allowlisted""" 466*0e209d39SAndroid Build Coastguard Worker return decl.spelling in self.allowlisted_function_names 467*0e209d39SAndroid Build Coastguard Worker 468*0e209d39SAndroid Build Coastguard Worker 469*0e209d39SAndroid Build Coastguard Workerclass BlocklistedlistedDeclarationFilter: 470*0e209d39SAndroid Build Coastguard Worker """A filter for blocklisting function declarations.""" 471*0e209d39SAndroid Build Coastguard Worker def __init__(self, blocklisted_function_names): 472*0e209d39SAndroid Build Coastguard Worker self.blocklisted_function_names = blocklisted_function_names 473*0e209d39SAndroid Build Coastguard Worker 474*0e209d39SAndroid Build Coastguard Worker def __call__(self, decl): 475*0e209d39SAndroid Build Coastguard Worker """Returns True if the given decl is nor blocklisted""" 476*0e209d39SAndroid Build Coastguard Worker return decl.spelling not in self.blocklisted_function_names 477*0e209d39SAndroid Build Coastguard Worker 478*0e209d39SAndroid Build Coastguard Worker 479*0e209d39SAndroid Build Coastguard Worker# Functions w/ variable argument lists (...) need special care to call 480*0e209d39SAndroid Build Coastguard Worker# their corresponding v- versions that accept a va_list argument. Note that 481*0e209d39SAndroid Build Coastguard Worker# although '...' will always appear as the last parameter, its v- version 482*0e209d39SAndroid Build Coastguard Worker# may put the va_list arg in a different place. Hence we provide an index 483*0e209d39SAndroid Build Coastguard Worker# to indicate the position. 484*0e209d39SAndroid Build Coastguard Worker# 485*0e209d39SAndroid Build Coastguard Worker# e.g. 'umsg_format': ('umsg_vformat', 3) means in the wrapper function of 486*0e209d39SAndroid Build Coastguard Worker# 'umsg_format', it will call 'umsg_vformat' instead, with the va_list arg 487*0e209d39SAndroid Build Coastguard Worker# inserted as the 3rd argument. 488*0e209d39SAndroid Build Coastguard Worker 489*0e209d39SAndroid Build Coastguard Worker# We need to insert the va_list (named args) at the position 490*0e209d39SAndroid Build Coastguard Worker# indicated by the KNOWN_VA_FUNCTIONS map. 491*0e209d39SAndroid Build Coastguard WorkerKNOWN_VA_FUNCTIONS = { 492*0e209d39SAndroid Build Coastguard Worker 'u_formatMessage': ('u_vformatMessage', 5), 493*0e209d39SAndroid Build Coastguard Worker 'u_parseMessage': ('u_vparseMessage', 5), 494*0e209d39SAndroid Build Coastguard Worker 'u_formatMessageWithError': ('u_vformatMessageWithError', 6), 495*0e209d39SAndroid Build Coastguard Worker 'u_parseMessageWithError': ('u_vparseMessageWithError', 5), 496*0e209d39SAndroid Build Coastguard Worker 'umsg_format': ('umsg_vformat', 3), 497*0e209d39SAndroid Build Coastguard Worker 'umsg_parse': ('umsg_vparse', 4), 498*0e209d39SAndroid Build Coastguard Worker 'utrace_format': ('utrace_vformat', 4), 499*0e209d39SAndroid Build Coastguard Worker} 500*0e209d39SAndroid Build Coastguard Worker 501*0e209d39SAndroid Build Coastguard Worker# The following functions are not @stable 502*0e209d39SAndroid Build Coastguard WorkerALLOWLISTED_FUNCTION_NAMES = ( 503*0e209d39SAndroid Build Coastguard Worker # Not intended to be called directly, but are used by @stable macros. 504*0e209d39SAndroid Build Coastguard Worker 'utf8_nextCharSafeBody', 505*0e209d39SAndroid Build Coastguard Worker 'utf8_appendCharSafeBody', 506*0e209d39SAndroid Build Coastguard Worker 'utf8_prevCharSafeBody', 507*0e209d39SAndroid Build Coastguard Worker 'utf8_back1SafeBody', 508*0e209d39SAndroid Build Coastguard Worker) 509