1#!/usr/bin/env python
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#            http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""Generate ICU stable C API wrapper source.
18
19This script parses all the header files specified by the ICU module names. For
20each function in the allowlist, it generates a wrapper function to be
21called by libandroidicu, which in turn calls the available function at runtime. The tool
22relies on libclang to parse header files, which is a component provided by
23Clang.
24
25Reference to ICU4C stable C APIs:
26http://icu-project.org/apiref/icu4c/files.html
27"""
28from __future__ import absolute_import
29from __future__ import print_function
30
31import logging
32import os
33import shutil
34import subprocess
35
36from genutil import (
37    android_path,
38    generate_shim,
39    generate_symbol_txt,
40    get_allowlisted_apis,
41    DeclaredFunctionsParser,
42    StableDeclarationFilter,
43)
44
45SYMBOL_SUFFIX = '_android'
46
47def copy_header_only_files():
48    """Copy required header only files"""
49    base_src_path = android_path('external/icu/icu4c/source/')
50    base_dest_path = android_path('external/icu/libandroidicu/include/unicode/')
51    with open(android_path('external/icu/tools/icu4c_srcgen/required_header_only_files.txt'),
52              'r') as in_file:
53        header_only_files = [
54            base_src_path + line.strip() for line in in_file.readlines() if not line.startswith('#')
55        ]
56
57    for src_path in header_only_files:
58        dest_path = base_dest_path + os.path.basename(src_path)
59        cmd = ['sed',
60               "s/U_SHOW_CPLUSPLUS_API/LIBANDROIDICU_U_SHOW_CPLUSPLUS_API/g",
61               src_path
62               ]
63
64        with open(dest_path, "w") as destfile:
65            subprocess.check_call(cmd, stdout=destfile)
66
67def main():
68    """Parse the ICU4C headers and generate the shim libandroidicu."""
69    logging.basicConfig(level=logging.DEBUG)
70
71    decl_filters = [StableDeclarationFilter()]
72    parser = DeclaredFunctionsParser(decl_filters, [])
73
74    parser.parse()
75
76    includes = parser.header_includes
77    functions = parser.declared_functions
78
79    # The shim has the allowlisted functions only
80    allowlisted_apis = get_allowlisted_apis('libandroidicu_allowlisted_api.txt')
81    functions = [f for f in functions if f.name in allowlisted_apis]
82
83    headers_folder = android_path('external/icu/libandroidicu/include/unicode')
84    if os.path.exists(headers_folder):
85        shutil.rmtree(headers_folder)
86    os.mkdir(headers_folder)
87
88    with open(android_path('external/icu/libandroidicu/static_shim/shim.cpp'),
89              'w') as out_file:
90        out_file.write(generate_shim(functions, includes, SYMBOL_SUFFIX,
91                                     'libandroidicu_shim.cpp.j2')
92                       .encode('utf8'))
93
94    with open(android_path('external/icu/libandroidicu/libandroidicu.map.txt'),
95              'w') as out_file:
96        out_file.write(generate_symbol_txt(functions, [], 'libandroidicu.map.txt.j2')
97                       .encode('utf8'))
98
99    for path in parser.header_paths_to_copy:
100        basename = os.path.basename(path)
101        shutil.copyfile(path, os.path.join(headers_folder, basename))
102
103    copy_header_only_files()
104
105if __name__ == '__main__':
106    main()
107