xref: /aosp_15_r20/external/icu/tools/icu4c_srcgen/generate_ndk.py (revision 0e209d3975ff4a8c132096b14b0e9364a753506e)
1*0e209d39SAndroid Build Coastguard Worker#!/usr/bin/env -S python3 -B
2*0e209d39SAndroid Build Coastguard Worker#
3*0e209d39SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project
4*0e209d39SAndroid Build Coastguard Worker#
5*0e209d39SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*0e209d39SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*0e209d39SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*0e209d39SAndroid Build Coastguard Worker#
9*0e209d39SAndroid Build Coastguard Worker#            http://www.apache.org/licenses/LICENSE-2.0
10*0e209d39SAndroid Build Coastguard Worker#
11*0e209d39SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*0e209d39SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*0e209d39SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*0e209d39SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*0e209d39SAndroid Build Coastguard Worker# limitations under the License.
16*0e209d39SAndroid Build Coastguard Worker#
17*0e209d39SAndroid Build Coastguard Worker"""Generate ICU stable C API wrapper source.
18*0e209d39SAndroid Build Coastguard Worker
19*0e209d39SAndroid Build Coastguard Worker
20*0e209d39SAndroid Build Coastguard WorkerThis script parses all the header files specified by the ICU module names. For
21*0e209d39SAndroid Build Coastguard Workereach function in the allowlist, it generates the NDK headers, and shim functions
22*0e209d39SAndroid Build Coastguard Workerto shim.cpp, which in turn calls the real implementation at runtime.
23*0e209d39SAndroid Build Coastguard WorkerThe tool relies on libclang to parse header files.
24*0e209d39SAndroid Build Coastguard Worker
25*0e209d39SAndroid Build Coastguard WorkerReference to ICU4C stable C APIs:
26*0e209d39SAndroid Build Coastguard Workerhttp://icu-project.org/apiref/icu4c/files.html
27*0e209d39SAndroid Build Coastguard Worker"""
28*0e209d39SAndroid Build Coastguard Workerfrom __future__ import absolute_import
29*0e209d39SAndroid Build Coastguard Workerfrom __future__ import print_function
30*0e209d39SAndroid Build Coastguard Worker
31*0e209d39SAndroid Build Coastguard Workerimport logging
32*0e209d39SAndroid Build Coastguard Workerimport os
33*0e209d39SAndroid Build Coastguard Workerimport re
34*0e209d39SAndroid Build Coastguard Workerimport shutil
35*0e209d39SAndroid Build Coastguard Workerimport subprocess
36*0e209d39SAndroid Build Coastguard Workerfrom pathlib import Path
37*0e209d39SAndroid Build Coastguard Workerfrom typing import Dict
38*0e209d39SAndroid Build Coastguard Worker
39*0e209d39SAndroid Build Coastguard Workerfrom genutil import (
40*0e209d39SAndroid Build Coastguard Worker    android_path,
41*0e209d39SAndroid Build Coastguard Worker    generate_shim,
42*0e209d39SAndroid Build Coastguard Worker    get_jinja_env,
43*0e209d39SAndroid Build Coastguard Worker    get_allowlisted_apis,
44*0e209d39SAndroid Build Coastguard Worker    AllowlistedDeclarationFilter,
45*0e209d39SAndroid Build Coastguard Worker    DeclaredFunctionsParser,
46*0e209d39SAndroid Build Coastguard Worker    StableDeclarationFilter,
47*0e209d39SAndroid Build Coastguard Worker    THIS_DIR,
48*0e209d39SAndroid Build Coastguard Worker)
49*0e209d39SAndroid Build Coastguard Worker
50*0e209d39SAndroid Build Coastguard Worker# No suffix for ndk shim
51*0e209d39SAndroid Build Coastguard WorkerSYMBOL_SUFFIX = ''
52*0e209d39SAndroid Build Coastguard Worker
53*0e209d39SAndroid Build Coastguard WorkerSECRET_PROCESSING_TOKEN = "@@@SECRET@@@"
54*0e209d39SAndroid Build Coastguard Worker
55*0e209d39SAndroid Build Coastguard WorkerDOC_BLOCK_COMMENT = r"\/\*\*(?:\*(?!\/)|[^*])*\*\/[ ]*\n"
56*0e209d39SAndroid Build Coastguard WorkerTILL_CLOSE_PARENTHESIS = r"[^)^;]*\)"
57*0e209d39SAndroid Build Coastguard WorkerSTABLE_MACRO = r"(?:U_STABLE|U_CAPI)"
58*0e209d39SAndroid Build Coastguard WorkerSTABLE_FUNCTION_DECLARATION = r"^(" + DOC_BLOCK_COMMENT + STABLE_MACRO \
59*0e209d39SAndroid Build Coastguard Worker                              + TILL_CLOSE_PARENTHESIS + ");$"
60*0e209d39SAndroid Build Coastguard WorkerNONSTABLE_FUNCTION_DECLARATION = r"^(" + DOC_BLOCK_COMMENT + r"(U_INTERNAL|U_DEPRECATED|U_DRAFT)" \
61*0e209d39SAndroid Build Coastguard Worker                                 + TILL_CLOSE_PARENTHESIS + ");$"
62*0e209d39SAndroid Build Coastguard Worker
63*0e209d39SAndroid Build Coastguard WorkerREGEX_STABLE_FUNCTION_DECLARATION = re.compile(STABLE_FUNCTION_DECLARATION, re.MULTILINE)
64*0e209d39SAndroid Build Coastguard WorkerREGEX_NONSTABLE_FUNCTION_DECLARATION = re.compile(NONSTABLE_FUNCTION_DECLARATION, re.MULTILINE)
65*0e209d39SAndroid Build Coastguard Worker
66*0e209d39SAndroid Build Coastguard WorkerAPI_LEVEL_MACRO_MAP = {
67*0e209d39SAndroid Build Coastguard Worker    '31': '31',
68*0e209d39SAndroid Build Coastguard Worker    'T': '__ANDROID_API_T__',
69*0e209d39SAndroid Build Coastguard Worker}
70*0e209d39SAndroid Build Coastguard Worker
71*0e209d39SAndroid Build Coastguard Workerdef get_allowlisted_regex_string(decl_names):
72*0e209d39SAndroid Build Coastguard Worker    """Return a regex in string to capture the C function declarations in the decl_names list"""
73*0e209d39SAndroid Build Coastguard Worker    tag = "|".join(decl_names)
74*0e209d39SAndroid Build Coastguard Worker    return r"(" + DOC_BLOCK_COMMENT + STABLE_MACRO + r"[^(]*(?=" + tag + r")(" + tag + ")" \
75*0e209d39SAndroid Build Coastguard Worker           + r"\("+ TILL_CLOSE_PARENTHESIS +");$"
76*0e209d39SAndroid Build Coastguard Worker
77*0e209d39SAndroid Build Coastguard Workerdef get_replacement_adding_api_level_macro(api_level: str):
78*0e209d39SAndroid Build Coastguard Worker    """Return the replacement string adding the NDK C macro
79*0e209d39SAndroid Build Coastguard Worker    guarding C function declaration by the api_level"""
80*0e209d39SAndroid Build Coastguard Worker    return r"\1 __INTRODUCED_IN({0});\n\n".format(api_level)
81*0e209d39SAndroid Build Coastguard Worker
82*0e209d39SAndroid Build Coastguard Workerdef modify_func_declarations(src_path: str, dst_path: str,
83*0e209d39SAndroid Build Coastguard Worker    exported_decl_api_map: Dict[str, str]):
84*0e209d39SAndroid Build Coastguard Worker    """Process the source file,
85*0e209d39SAndroid Build Coastguard Worker    remove the C function declarations not in the decl_names,
86*0e209d39SAndroid Build Coastguard Worker    add guard the functions listed in decl_names by the API level,
87*0e209d39SAndroid Build Coastguard Worker    and output to the dst_path """
88*0e209d39SAndroid Build Coastguard Worker    decl_names = list(exported_decl_api_map.keys())
89*0e209d39SAndroid Build Coastguard Worker    allowlist_regex_string = get_allowlisted_regex_string(decl_names)
90*0e209d39SAndroid Build Coastguard Worker    allowlist_decl_regex = re.compile('^' + allowlist_regex_string, re.MULTILINE)
91*0e209d39SAndroid Build Coastguard Worker    with open(src_path, "r") as file:
92*0e209d39SAndroid Build Coastguard Worker        src = file.read()
93*0e209d39SAndroid Build Coastguard Worker
94*0e209d39SAndroid Build Coastguard Worker    # Remove all non-stable function declarations
95*0e209d39SAndroid Build Coastguard Worker    modified = REGEX_NONSTABLE_FUNCTION_DECLARATION.sub('', src)
96*0e209d39SAndroid Build Coastguard Worker
97*0e209d39SAndroid Build Coastguard Worker    # Insert intermediate token to all functions in the allowlist
98*0e209d39SAndroid Build Coastguard Worker    if decl_names:
99*0e209d39SAndroid Build Coastguard Worker        modified = allowlist_decl_regex.sub(SECRET_PROCESSING_TOKEN + r"\1;", modified)
100*0e209d39SAndroid Build Coastguard Worker    # Remove all other stable declarations not in the allowlist
101*0e209d39SAndroid Build Coastguard Worker    modified = REGEX_STABLE_FUNCTION_DECLARATION.sub('', modified)
102*0e209d39SAndroid Build Coastguard Worker
103*0e209d39SAndroid Build Coastguard Worker    api_levels = list(set(exported_decl_api_map.values()))
104*0e209d39SAndroid Build Coastguard Worker    for api_level in api_levels:
105*0e209d39SAndroid Build Coastguard Worker        exported_decl_at_this_level = {key: value for key, value in
106*0e209d39SAndroid Build Coastguard Worker                                       exported_decl_api_map.items()
107*0e209d39SAndroid Build Coastguard Worker                                       if value == api_level }
108*0e209d39SAndroid Build Coastguard Worker
109*0e209d39SAndroid Build Coastguard Worker        # Insert C macro and annotation to indicate the API level to each functions
110*0e209d39SAndroid Build Coastguard Worker        macro = API_LEVEL_MACRO_MAP[api_level]
111*0e209d39SAndroid Build Coastguard Worker        decl_name_regex_string = get_allowlisted_regex_string(
112*0e209d39SAndroid Build Coastguard Worker            list(exported_decl_at_this_level.keys()))
113*0e209d39SAndroid Build Coastguard Worker        secret_allowlist_decl_regex = re.compile(
114*0e209d39SAndroid Build Coastguard Worker            '^' + SECRET_PROCESSING_TOKEN + decl_name_regex_string,
115*0e209d39SAndroid Build Coastguard Worker            re.MULTILINE)
116*0e209d39SAndroid Build Coastguard Worker        modified = secret_allowlist_decl_regex.sub(
117*0e209d39SAndroid Build Coastguard Worker            get_replacement_adding_api_level_macro(macro), modified)
118*0e209d39SAndroid Build Coastguard Worker
119*0e209d39SAndroid Build Coastguard Worker    with open(dst_path, "w") as out:
120*0e209d39SAndroid Build Coastguard Worker        out.write(modified)
121*0e209d39SAndroid Build Coastguard Worker
122*0e209d39SAndroid Build Coastguard Workerdef remove_ignored_includes(file_path, include_list):
123*0e209d39SAndroid Build Coastguard Worker    """
124*0e209d39SAndroid Build Coastguard Worker    Remove the included header, i.e. #include lines, listed in include_list from the file_path
125*0e209d39SAndroid Build Coastguard Worker    header.
126*0e209d39SAndroid Build Coastguard Worker    """
127*0e209d39SAndroid Build Coastguard Worker
128*0e209d39SAndroid Build Coastguard Worker    # Do nothing if the list is empty
129*0e209d39SAndroid Build Coastguard Worker    if not include_list:
130*0e209d39SAndroid Build Coastguard Worker        return
131*0e209d39SAndroid Build Coastguard Worker
132*0e209d39SAndroid Build Coastguard Worker    tag = "|".join(include_list)
133*0e209d39SAndroid Build Coastguard Worker
134*0e209d39SAndroid Build Coastguard Worker    with open(file_path, "r") as file:
135*0e209d39SAndroid Build Coastguard Worker        content = file.read()
136*0e209d39SAndroid Build Coastguard Worker
137*0e209d39SAndroid Build Coastguard Worker    regex = re.compile(r"^#include \"unicode\/(" + tag + ")\"\n", re.MULTILINE)
138*0e209d39SAndroid Build Coastguard Worker    content = regex.sub('', content)
139*0e209d39SAndroid Build Coastguard Worker
140*0e209d39SAndroid Build Coastguard Worker    with open(file_path, "w") as out:
141*0e209d39SAndroid Build Coastguard Worker        out.write(content)
142*0e209d39SAndroid Build Coastguard Worker
143*0e209d39SAndroid Build Coastguard Workerdef copy_header_only_files():
144*0e209d39SAndroid Build Coastguard Worker    """Copy required header only files"""
145*0e209d39SAndroid Build Coastguard Worker    base_src_path = android_path('external/icu/icu4c/source/')
146*0e209d39SAndroid Build Coastguard Worker    base_dest_path = android_path('external/icu/libicu/ndk_headers/unicode/')
147*0e209d39SAndroid Build Coastguard Worker    with open(android_path('external/icu/tools/icu4c_srcgen/libicu_required_header_only_files.txt'),
148*0e209d39SAndroid Build Coastguard Worker              'r') as in_file:
149*0e209d39SAndroid Build Coastguard Worker        header_only_files = [
150*0e209d39SAndroid Build Coastguard Worker            base_src_path + line.strip() for line in in_file.readlines() if not line.startswith('#')
151*0e209d39SAndroid Build Coastguard Worker        ]
152*0e209d39SAndroid Build Coastguard Worker
153*0e209d39SAndroid Build Coastguard Worker    for src_path in header_only_files:
154*0e209d39SAndroid Build Coastguard Worker        dest_path = base_dest_path + os.path.basename(src_path)
155*0e209d39SAndroid Build Coastguard Worker        cmd = ['sed',
156*0e209d39SAndroid Build Coastguard Worker               "s/U_SHOW_CPLUSPLUS_API/LIBICU_U_SHOW_CPLUSPLUS_API/g",
157*0e209d39SAndroid Build Coastguard Worker               src_path
158*0e209d39SAndroid Build Coastguard Worker               ]
159*0e209d39SAndroid Build Coastguard Worker
160*0e209d39SAndroid Build Coastguard Worker        with open(dest_path, "w") as destfile:
161*0e209d39SAndroid Build Coastguard Worker            subprocess.check_call(cmd, stdout=destfile)
162*0e209d39SAndroid Build Coastguard Worker
163*0e209d39SAndroid Build Coastguard Workerdef copy_cts_headers():
164*0e209d39SAndroid Build Coastguard Worker    """Copy headers from common/ and i18n/ to cts_headers/ for compiling cintltst as CTS."""
165*0e209d39SAndroid Build Coastguard Worker    dst_folder = android_path('external/icu/libicu/cts_headers')
166*0e209d39SAndroid Build Coastguard Worker    if os.path.exists(dst_folder):
167*0e209d39SAndroid Build Coastguard Worker        shutil.rmtree(dst_folder)
168*0e209d39SAndroid Build Coastguard Worker    os.mkdir(dst_folder)
169*0e209d39SAndroid Build Coastguard Worker    os.mkdir(os.path.join(dst_folder, 'unicode'))
170*0e209d39SAndroid Build Coastguard Worker
171*0e209d39SAndroid Build Coastguard Worker    shutil.copyfile(android_path('external/icu/android_icu4c/include/uconfig_local.h'),
172*0e209d39SAndroid Build Coastguard Worker                    android_path('external/icu/libicu/cts_headers/uconfig_local.h'))
173*0e209d39SAndroid Build Coastguard Worker
174*0e209d39SAndroid Build Coastguard Worker    header_subfolders = (
175*0e209d39SAndroid Build Coastguard Worker        'common',
176*0e209d39SAndroid Build Coastguard Worker        'common/unicode',
177*0e209d39SAndroid Build Coastguard Worker        'i18n',
178*0e209d39SAndroid Build Coastguard Worker        'i18n/unicode',
179*0e209d39SAndroid Build Coastguard Worker    )
180*0e209d39SAndroid Build Coastguard Worker    for subfolder in header_subfolders:
181*0e209d39SAndroid Build Coastguard Worker        path = android_path('external/icu/icu4c/source', subfolder)
182*0e209d39SAndroid Build Coastguard Worker        files = [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.h')]
183*0e209d39SAndroid Build Coastguard Worker
184*0e209d39SAndroid Build Coastguard Worker        for src_path in files:
185*0e209d39SAndroid Build Coastguard Worker            base_header_name = os.path.basename(src_path)
186*0e209d39SAndroid Build Coastguard Worker            dst_path = dst_folder
187*0e209d39SAndroid Build Coastguard Worker            if subfolder.endswith('unicode'):
188*0e209d39SAndroid Build Coastguard Worker                dst_path = os.path.join(dst_path, 'unicode')
189*0e209d39SAndroid Build Coastguard Worker            dst_path = os.path.join(dst_path, base_header_name)
190*0e209d39SAndroid Build Coastguard Worker
191*0e209d39SAndroid Build Coastguard Worker            shutil.copyfile(src_path, dst_path)
192*0e209d39SAndroid Build Coastguard Worker
193*0e209d39SAndroid Build Coastguard Workerdef get_rename_macro_regex(decl_names):
194*0e209d39SAndroid Build Coastguard Worker    """Return a regex in string to capture the C macro defining the name in the decl_names list"""
195*0e209d39SAndroid Build Coastguard Worker    tag = "|".join(decl_names)
196*0e209d39SAndroid Build Coastguard Worker    return re.compile(r"^(#define (?:" + tag + r") .*)$", re.MULTILINE)
197*0e209d39SAndroid Build Coastguard Worker
198*0e209d39SAndroid Build Coastguard Workerdef generate_cts_headers(decl_names):
199*0e209d39SAndroid Build Coastguard Worker    """Generate headers for compiling cintltst as CTS."""
200*0e209d39SAndroid Build Coastguard Worker    copy_cts_headers()
201*0e209d39SAndroid Build Coastguard Worker
202*0e209d39SAndroid Build Coastguard Worker    # Disable all C macro renaming the NDK functions in order to test the functions in the CTS
203*0e209d39SAndroid Build Coastguard Worker    urename_path = android_path('external/icu/libicu/cts_headers/unicode/urename.h')
204*0e209d39SAndroid Build Coastguard Worker    with open(urename_path, "r") as file:
205*0e209d39SAndroid Build Coastguard Worker        src = file.read()
206*0e209d39SAndroid Build Coastguard Worker
207*0e209d39SAndroid Build Coastguard Worker    regex = get_rename_macro_regex(decl_names)
208*0e209d39SAndroid Build Coastguard Worker    modified = regex.sub(r"// \1", src)
209*0e209d39SAndroid Build Coastguard Worker
210*0e209d39SAndroid Build Coastguard Worker    with open(urename_path, "w") as out:
211*0e209d39SAndroid Build Coastguard Worker        out.write(modified)
212*0e209d39SAndroid Build Coastguard Worker
213*0e209d39SAndroid Build Coastguard WorkerIGNORED_INCLUDE_DEPENDENCY = {
214*0e209d39SAndroid Build Coastguard Worker    "ubrk.h": ["parseerr.h", ],
215*0e209d39SAndroid Build Coastguard Worker    "ucol.h": ["uiter.h", "unorm.h", "uset.h", ],
216*0e209d39SAndroid Build Coastguard Worker    "ulocdata.h": ["ures.h", "uset.h", ],
217*0e209d39SAndroid Build Coastguard Worker    "unorm2.h": ["uset.h", ],
218*0e209d39SAndroid Build Coastguard Worker    "ustring.h": ["uiter.h", ],
219*0e209d39SAndroid Build Coastguard Worker    "utrans.h": ["uset.h", ],
220*0e209d39SAndroid Build Coastguard Worker}
221*0e209d39SAndroid Build Coastguard Worker
222*0e209d39SAndroid Build Coastguard WorkerIGNORED_HEADER_FOR_DOXYGEN_GROUPING = set([
223*0e209d39SAndroid Build Coastguard Worker    "ubidi.h", # ubidi.h has the @{ ... @} group block already.
224*0e209d39SAndroid Build Coastguard Worker    "uconfig.h", # pre-defined config that NDK users shouldn't change
225*0e209d39SAndroid Build Coastguard Worker    "platform.h", # pre-defined variable not to be changed by the NDK users
226*0e209d39SAndroid Build Coastguard Worker    "utf_old.h", # deprecated UTF macros
227*0e209d39SAndroid Build Coastguard Worker    "uvernum.h", # ICU version information not useful for version-independent usage in NDK
228*0e209d39SAndroid Build Coastguard Worker    "urename.h" # Renaming symbols, but not used in NDK
229*0e209d39SAndroid Build Coastguard Worker])
230*0e209d39SAndroid Build Coastguard Worker
231*0e209d39SAndroid Build Coastguard Worker"""
232*0e209d39SAndroid Build Coastguard WorkerThis map should mirror the mapping in external/icu/icu4c/source/Doxyfile.in.
233*0e209d39SAndroid Build Coastguard WorkerThis is needed because NDK doesn't allow per-module Doxyfile,
234*0e209d39SAndroid Build Coastguard Workerapart from the shared frameworks/native/docs/Doxyfile.
235*0e209d39SAndroid Build Coastguard Worker"""
236*0e209d39SAndroid Build Coastguard WorkerDOXYGEN_ALIASES = {
237*0e209d39SAndroid Build Coastguard Worker    "@memo": '\\par Note:\n',
238*0e209d39SAndroid Build Coastguard Worker    "@draft": '\\xrefitem draft "Draft" "Draft List" This API may be changed in the future versions and was introduced in',
239*0e209d39SAndroid Build Coastguard Worker    "@stable": '\\xrefitem stable "Stable" "Stable List"',
240*0e209d39SAndroid Build Coastguard Worker    "@deprecated": '\\xrefitem deprecated "Deprecated" "Deprecated List"',
241*0e209d39SAndroid Build Coastguard Worker    "@obsolete": '\\xrefitem obsolete "Obsolete" "Obsolete List"',
242*0e209d39SAndroid Build Coastguard Worker    "@system": '\\xrefitem system "System" "System List" Do not use unless you know what you are doing.',
243*0e209d39SAndroid Build Coastguard Worker    "@internal": '\\xrefitem internal "Internal"  "Internal List"  Do not use. This API is for internal use only.',
244*0e209d39SAndroid Build Coastguard Worker}
245*0e209d39SAndroid Build Coastguard Worker
246*0e209d39SAndroid Build Coastguard Workerdef add_ndk_required_doxygen_grouping():
247*0e209d39SAndroid Build Coastguard Worker    """Add @addtogroup annotation to the header files for NDK API docs"""
248*0e209d39SAndroid Build Coastguard Worker    path = android_path('external/icu/libicu/ndk_headers/unicode')
249*0e209d39SAndroid Build Coastguard Worker    files = Path(path).glob("*.h")
250*0e209d39SAndroid Build Coastguard Worker
251*0e209d39SAndroid Build Coastguard Worker    for src_path in files:
252*0e209d39SAndroid Build Coastguard Worker        header_content = src_path.read_text()
253*0e209d39SAndroid Build Coastguard Worker
254*0e209d39SAndroid Build Coastguard Worker        for old, new in DOXYGEN_ALIASES.items():
255*0e209d39SAndroid Build Coastguard Worker            header_content = header_content.replace(old, new)
256*0e209d39SAndroid Build Coastguard Worker
257*0e209d39SAndroid Build Coastguard Worker        src_path.write_text(header_content)
258*0e209d39SAndroid Build Coastguard Worker
259*0e209d39SAndroid Build Coastguard Worker        if os.path.basename(src_path) in IGNORED_HEADER_FOR_DOXYGEN_GROUPING:
260*0e209d39SAndroid Build Coastguard Worker            continue
261*0e209d39SAndroid Build Coastguard Worker
262*0e209d39SAndroid Build Coastguard Worker        cmd_add_addtogroup_annotation = ['sed',
263*0e209d39SAndroid Build Coastguard Worker               '-i',
264*0e209d39SAndroid Build Coastguard Worker               '0,/^\( *\)\(\* *\\\\file\)/s//\\1* @addtogroup icu4c ICU4C\\n\\1* @{\\n\\1\\2/',
265*0e209d39SAndroid Build Coastguard Worker               src_path
266*0e209d39SAndroid Build Coastguard Worker               ]
267*0e209d39SAndroid Build Coastguard Worker
268*0e209d39SAndroid Build Coastguard Worker        subprocess.check_call(cmd_add_addtogroup_annotation)
269*0e209d39SAndroid Build Coastguard Worker
270*0e209d39SAndroid Build Coastguard Worker        # Next iteration if the above sed regex doesn't add the text
271*0e209d39SAndroid Build Coastguard Worker        if not has_string_in_file(src_path, 'addtogroup'):
272*0e209d39SAndroid Build Coastguard Worker            basename = os.path.basename(src_path)
273*0e209d39SAndroid Build Coastguard Worker            print(f'Warning: unicode/{basename} has no "\\file" annotation')
274*0e209d39SAndroid Build Coastguard Worker            continue
275*0e209d39SAndroid Build Coastguard Worker
276*0e209d39SAndroid Build Coastguard Worker        # Add the closing bracket for @addtogroup
277*0e209d39SAndroid Build Coastguard Worker        with open(src_path, 'a') as header_file:
278*0e209d39SAndroid Build Coastguard Worker            header_file.write('\n/** @} */ // addtogroup\n')
279*0e209d39SAndroid Build Coastguard Worker
280*0e209d39SAndroid Build Coastguard Workerdef has_string_in_file(path, s):
281*0e209d39SAndroid Build Coastguard Worker    """Return True if the a string exists in the file"""
282*0e209d39SAndroid Build Coastguard Worker    with open(path, 'r') as file:
283*0e209d39SAndroid Build Coastguard Worker        return s in file.read()
284*0e209d39SAndroid Build Coastguard Worker
285*0e209d39SAndroid Build Coastguard Workerdef get_exported_symbol_map(export_file : str) -> Dict[str, str]:
286*0e209d39SAndroid Build Coastguard Worker    """Return a dictionary mapping from the symbol name to API level in the
287*0e209d39SAndroid Build Coastguard Worker    export_file"""
288*0e209d39SAndroid Build Coastguard Worker    result_map = {}
289*0e209d39SAndroid Build Coastguard Worker    with open(os.path.join(THIS_DIR, export_file), 'r') as file:
290*0e209d39SAndroid Build Coastguard Worker        for line in file:
291*0e209d39SAndroid Build Coastguard Worker            line = line.strip()
292*0e209d39SAndroid Build Coastguard Worker            if line and not line.startswith("#"):
293*0e209d39SAndroid Build Coastguard Worker                splits = line.split(',')
294*0e209d39SAndroid Build Coastguard Worker                if len(splits) < 2:
295*0e209d39SAndroid Build Coastguard Worker                    raise ValueError(f'line "{line}" has no , separator')
296*0e209d39SAndroid Build Coastguard Worker                result_map[splits[0]] = splits[1]
297*0e209d39SAndroid Build Coastguard Worker
298*0e209d39SAndroid Build Coastguard Worker    return result_map
299*0e209d39SAndroid Build Coastguard Worker
300*0e209d39SAndroid Build Coastguard Worker
301*0e209d39SAndroid Build Coastguard Workerdef main():
302*0e209d39SAndroid Build Coastguard Worker    """Parse the ICU4C headers and generate the shim libicu."""
303*0e209d39SAndroid Build Coastguard Worker    logging.basicConfig(level=logging.DEBUG)
304*0e209d39SAndroid Build Coastguard Worker
305*0e209d39SAndroid Build Coastguard Worker    exported_symbol_map = get_exported_symbol_map('libicu_export.txt')
306*0e209d39SAndroid Build Coastguard Worker    allowlisted_apis = set(exported_symbol_map.keys())
307*0e209d39SAndroid Build Coastguard Worker    decl_filters = [StableDeclarationFilter()]
308*0e209d39SAndroid Build Coastguard Worker    decl_filters.append(AllowlistedDeclarationFilter(allowlisted_apis))
309*0e209d39SAndroid Build Coastguard Worker    parser = DeclaredFunctionsParser(decl_filters, [])
310*0e209d39SAndroid Build Coastguard Worker    parser.set_ignored_include_dependency(IGNORED_INCLUDE_DEPENDENCY)
311*0e209d39SAndroid Build Coastguard Worker
312*0e209d39SAndroid Build Coastguard Worker    parser.parse()
313*0e209d39SAndroid Build Coastguard Worker
314*0e209d39SAndroid Build Coastguard Worker    includes = parser.header_includes
315*0e209d39SAndroid Build Coastguard Worker    functions = parser.declared_functions
316*0e209d39SAndroid Build Coastguard Worker    header_to_function_names = parser.header_to_function_names
317*0e209d39SAndroid Build Coastguard Worker
318*0e209d39SAndroid Build Coastguard Worker    # The shim has the allowlisted functions only
319*0e209d39SAndroid Build Coastguard Worker    functions = [f for f in functions if f.name in allowlisted_apis]
320*0e209d39SAndroid Build Coastguard Worker
321*0e209d39SAndroid Build Coastguard Worker    headers_folder = android_path('external/icu/libicu/ndk_headers/unicode')
322*0e209d39SAndroid Build Coastguard Worker    if os.path.exists(headers_folder):
323*0e209d39SAndroid Build Coastguard Worker        shutil.rmtree(headers_folder)
324*0e209d39SAndroid Build Coastguard Worker    os.mkdir(headers_folder)
325*0e209d39SAndroid Build Coastguard Worker
326*0e209d39SAndroid Build Coastguard Worker    with open(android_path('external/icu/libicu/src/shim.cpp'),
327*0e209d39SAndroid Build Coastguard Worker              'w') as out_file:
328*0e209d39SAndroid Build Coastguard Worker        out_file.write(generate_shim(functions, includes, SYMBOL_SUFFIX, 'libicu_shim.cpp.j2'))
329*0e209d39SAndroid Build Coastguard Worker
330*0e209d39SAndroid Build Coastguard Worker    with open(android_path('external/icu/libicu/libicu.map.txt'), 'w') as out_file:
331*0e209d39SAndroid Build Coastguard Worker        data = {
332*0e209d39SAndroid Build Coastguard Worker            'exported_symbol_map' : exported_symbol_map,
333*0e209d39SAndroid Build Coastguard Worker        }
334*0e209d39SAndroid Build Coastguard Worker        out_file.write(get_jinja_env().get_template('libicu.map.txt.j2').render(data))
335*0e209d39SAndroid Build Coastguard Worker
336*0e209d39SAndroid Build Coastguard Worker    # Process the C headers and put them into the ndk folder.
337*0e209d39SAndroid Build Coastguard Worker    for src_path in parser.header_paths_to_copy:
338*0e209d39SAndroid Build Coastguard Worker        basename = os.path.basename(src_path)
339*0e209d39SAndroid Build Coastguard Worker        dst_path = os.path.join(headers_folder, basename)
340*0e209d39SAndroid Build Coastguard Worker        exported_symbol_map_this_header = {
341*0e209d39SAndroid Build Coastguard Worker            key: value for key, value in exported_symbol_map.items()
342*0e209d39SAndroid Build Coastguard Worker            if key in header_to_function_names[basename]}
343*0e209d39SAndroid Build Coastguard Worker        modify_func_declarations(src_path, dst_path, exported_symbol_map_this_header)
344*0e209d39SAndroid Build Coastguard Worker        # Remove #include lines from the header files.
345*0e209d39SAndroid Build Coastguard Worker        if basename in IGNORED_INCLUDE_DEPENDENCY:
346*0e209d39SAndroid Build Coastguard Worker            remove_ignored_includes(dst_path, IGNORED_INCLUDE_DEPENDENCY[basename])
347*0e209d39SAndroid Build Coastguard Worker
348*0e209d39SAndroid Build Coastguard Worker    copy_header_only_files()
349*0e209d39SAndroid Build Coastguard Worker
350*0e209d39SAndroid Build Coastguard Worker    generate_cts_headers(allowlisted_apis)
351*0e209d39SAndroid Build Coastguard Worker
352*0e209d39SAndroid Build Coastguard Worker    add_ndk_required_doxygen_grouping()
353*0e209d39SAndroid Build Coastguard Worker
354*0e209d39SAndroid Build Coastguard Worker    # Apply documentation patches by the following shell script
355*0e209d39SAndroid Build Coastguard Worker    subprocess.check_call(
356*0e209d39SAndroid Build Coastguard Worker        [android_path('external/icu/tools/icu4c_srcgen/doc_patches/apply_patches.sh')])
357*0e209d39SAndroid Build Coastguard Worker
358*0e209d39SAndroid Build Coastguard Worker    print("Done. See the generated headers at libicu/ndk_headers/.")
359*0e209d39SAndroid Build Coastguard Worker
360*0e209d39SAndroid Build Coastguard Workerif __name__ == '__main__':
361*0e209d39SAndroid Build Coastguard Worker    main()
362