xref: /aosp_15_r20/external/bazelbuild-rules_android/rules/idl.bzl (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1# Copyright 2018 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Bazel Android IDL library for the Android rules."""
16
17load(":java.bzl", _java = "java")
18load(":path.bzl", _path = "path")
19load(":utils.bzl", "ANDROID_TOOLCHAIN_TYPE", _log = "log")
20
21_AIDL_TOOLCHAIN_MISSING_ERROR = (
22    "IDL sources provided without the Android IDL toolchain."
23)
24
25_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR = (
26    "Cannot determine java/javatests root for import %s."
27)
28
29IDLContextInfo = provider(
30    doc = "Contains data from processing Android IDL.",
31    fields = dict(
32        idl_srcs = "List of IDL sources",
33        idl_import_root = "IDL import root",
34        idl_java_srcs = "List of IDL Java sources",
35        idl_deps =
36            "List of IDL targets required for Java compilation, Proguard, etc.",
37        providers = "The list of all providers to propagate.",
38    ),
39)
40
41def _gen_java_from_idl(
42        ctx,
43        out_idl_java_src = None,
44        idl_src = None,
45        transitive_idl_import_roots = [],
46        transitive_idl_imports = [],
47        transitive_idl_preprocessed = [],
48        aidl = None,
49        aidl_lib = None,
50        aidl_framework = None,
51        uses_aosp_compiler = False,
52        idlopts = []):
53    args = ctx.actions.args()
54
55    # Note: at the moment (2022/11/07), the flags that the AOSP compiler accepts is a superset of
56    # the Google3 compiler, but that might not be true in the future.
57    if uses_aosp_compiler:
58        args.add("--use-aosp-compiler")
59
60    for opt in idlopts:
61        args.add(opt)
62
63    args.add("-b")  # fail on parcelable
64    args.add_all(transitive_idl_import_roots, format_each = "-I%s")
65    args.add(aidl_framework, format = "-p%s")
66    args.add_all(transitive_idl_preprocessed, format_each = "-p%s")
67    args.add(idl_src)
68    args.add(out_idl_java_src)
69
70    aidl_lib_files = [aidl_lib.files] if aidl_lib else []
71
72    ctx.actions.run(
73        executable = aidl,
74        arguments = [args],
75        inputs = depset(
76            [aidl_framework],
77            transitive = aidl_lib_files + [
78                transitive_idl_imports,
79                transitive_idl_preprocessed,
80            ],
81        ),
82        outputs = [out_idl_java_src],
83        mnemonic = "AndroidIDLGenerate",
84        progress_message = "Android IDL generation %s" % idl_src.path,
85        toolchain = ANDROID_TOOLCHAIN_TYPE,
86    )
87
88def _get_idl_import_root_path(
89        package,
90        idl_import_root,
91        idl_file_root_path):
92    package_path = _path.relative(
93        idl_file_root_path,
94        package,
95    )
96    return _path.relative(
97        package_path,
98        idl_import_root,
99    )
100
101def _collect_unique_idl_import_root_paths(
102        package,
103        idl_import_root,
104        idl_imports):
105    idl_import_roots = dict()
106    for idl_import in idl_imports:
107        idl_import_roots[_get_idl_import_root_path(
108            package,
109            idl_import_root,
110            idl_import.root.path,
111        )] = True
112    return sorted(idl_import_roots.keys())
113
114def _collect_unique_java_roots(idl_imports):
115    idl_import_roots = dict()
116    for idl_import in idl_imports:
117        java_root = _java.root(idl_import.path)
118        if not java_root:
119            _log.error(_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR % idl_import.path)
120        idl_import_roots[java_root] = True
121    return sorted(idl_import_roots.keys())
122
123def _determine_idl_import_roots(
124        package,
125        idl_import_root = None,
126        idl_imports = []):
127    if idl_import_root == None:
128        return _collect_unique_java_roots(idl_imports)
129    return _collect_unique_idl_import_root_paths(
130        package,
131        idl_import_root,
132        idl_imports,
133    )
134
135def _process(
136        ctx,
137        idl_srcs = [],
138        idl_parcelables = [],
139        idl_import_root = None,
140        idl_preprocessed = [],
141        deps = [],
142        exports = [],
143        aidl = None,
144        aidl_lib = None,
145        aidl_framework = None,
146        uses_aosp_compiler = False,
147        idlopts = []):
148    """Processes Android IDL.
149
150    Args:
151      ctx: The context.
152      idl_srcs: sequence of Files. A list of the aidl source files to be
153        processed into Java source files and then compiled. Optional.
154      idl_parcelables: sequence of Files. A list of Android IDL definitions to
155        supply as imports. These files will be made available as imports for any
156        android_library target that depends on this library, directly or via its
157        transitive closure, but will not be translated to Java or compiled.
158
159        Only .aidl files that correspond directly to .java sources in this library
160        should be included (e.g. custom implementations of Parcelable), otherwise
161        idl_srcs should be used.
162
163        These files must be placed appropriately for the aidl compiler to find
164        them. See the description of idl_import_root for information about what
165        this means. Optional.
166      idl_import_root: string. Package-relative path to the root of the java
167        package tree containing idl sources included in this library. This path
168        will be used as the import root when processing idl sources that depend on
169        this library.
170
171        When idl_import_root is specified, both idl_parcelables and idl_srcs must
172        be at the path specified by the java package of the object they represent
173        under idl_import_root. When idl_import_root is not specified, both
174        idl_parcelables and idl_srcs must be at the path specified by their
175        package under a Java root. Optional.
176      idl_preprocessed: sequence of Files. A list of preprocessed Android IDL
177        definitions to supply as imports. These files will be made available as
178        imports for any android_library target that depends on this library,
179        directly or via its transitive closure, but will not be translated to
180        Java or compiled.
181
182        Only preprocessed .aidl files that correspond directly to .java sources
183        in this library should be included (e.g. custom implementations of
184        Parcelable), otherwise use idl_srcs for Android IDL definitions that
185        need to be translated to Java interfaces and use idl_parcelable for
186        non-preprcessed AIDL files. Optional.
187      deps: sequence of Targets. A list of dependencies. Optional.
188      exports: sequence of Targets. A list of exports. Optional.
189      aidl: Target. A target pointing to the aidl executable to be used for
190        Java code generation from *.idl source files. Optional, unless idl_srcs
191        are supplied.
192      aidl_lib: Target. A target pointing to the aidl_lib library required
193        during Java compilation when Java code is generated from idl sources.
194        Optional.
195      aidl_framework: Target. A target pointing to the aidl framework. Optional,
196        unless idl_srcs are supplied.
197      uses_aosp_compiler: boolean. If True, the upstream AOSP AIDL compiler is
198        used instead of the Google3-only AIDL compiler. This allows wider range
199        of AIDL language features including the structured parcelable, enum,
200        union, and many more. On the other hand, using this may cause noticeable
201        regression in terms of code size and performance as the compiler doesn't
202        implement several optimization techniques that the Google3 compiler has.
203      idlopts: list of string. Additional flags to add to the AOSP AIDL compiler
204        invocation.
205
206    Returns:
207      A IDLContextInfo provider.
208    """
209    if idl_srcs and not (aidl and aidl_framework):
210        _log.error(_AIDL_TOOLCHAIN_MISSING_ERROR)
211
212    transitive_idl_import_roots = []
213    transitive_idl_imports = []
214    transitive_idl_preprocessed = []
215    for dep in deps + exports:
216        transitive_idl_import_roots.append(dep.transitive_idl_import_roots)
217        transitive_idl_imports.append(dep.transitive_idl_imports)
218        transitive_idl_preprocessed.append(dep.transitive_idl_preprocessed)
219
220    idl_java_srcs = []
221    for idl_src in idl_srcs:
222        idl_java_src = ctx.actions.declare_file(
223            ctx.label.name + "_aidl/" + idl_src.path.replace(".aidl", ".java"),
224        )
225        idl_java_srcs.append(idl_java_src)
226        _gen_java_from_idl(
227            ctx,
228            out_idl_java_src = idl_java_src,
229            idl_src = idl_src,
230            transitive_idl_import_roots = depset(
231                _determine_idl_import_roots(
232                    ctx.label.package,
233                    idl_import_root,
234                    idl_parcelables + idl_srcs,
235                ),
236                transitive = transitive_idl_import_roots,
237                order = "preorder",
238            ),
239            transitive_idl_imports = depset(
240                idl_parcelables + idl_srcs,
241                transitive = transitive_idl_imports,
242                order = "preorder",
243            ),
244            transitive_idl_preprocessed = depset(
245                transitive = transitive_idl_preprocessed,
246            ),
247            aidl = aidl,
248            aidl_lib = aidl_lib,
249            aidl_framework = aidl_framework,
250            uses_aosp_compiler = uses_aosp_compiler,
251            idlopts = idlopts,
252        )
253
254    return IDLContextInfo(
255        idl_srcs = idl_srcs,
256        idl_import_root = idl_import_root,
257        idl_java_srcs = idl_java_srcs,
258        idl_deps = [aidl_lib] if (idl_java_srcs and aidl_lib) else [],
259        providers = [
260            # TODO(b/146216105): Make this a Starlark provider.
261            AndroidIdlInfo(
262                depset(
263                    _determine_idl_import_roots(
264                        ctx.label.package,
265                        idl_import_root,
266                        idl_parcelables + idl_srcs + idl_preprocessed,
267                    ),
268                    transitive = transitive_idl_import_roots,
269                    order = "preorder",
270                ),
271                depset(
272                    idl_parcelables + idl_srcs + idl_preprocessed,
273                    transitive = transitive_idl_imports,
274                    order = "preorder",
275                ),
276                depset(),  # TODO(b/146216105): Delete this field once in Starlark.
277                depset(idl_preprocessed, transitive = transitive_idl_preprocessed),
278            ),
279        ],
280    )
281
282idl = struct(
283    process = _process,
284)
285
286# Visible for testing.
287testing = struct(
288    get_idl_import_root_path = _get_idl_import_root_path,
289)
290