• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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", _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    )
86
87def _get_idl_import_root_path(
88        package,
89        idl_import_root,
90        idl_file_root_path):
91    package_path = _path.relative(
92        idl_file_root_path,
93        package,
94    )
95    return _path.relative(
96        package_path,
97        idl_import_root,
98    )
99
100def _collect_unique_idl_import_root_paths(
101        package,
102        idl_import_root,
103        idl_imports):
104    idl_import_roots = dict()
105    for idl_import in idl_imports:
106        idl_import_roots[_get_idl_import_root_path(
107            package,
108            idl_import_root,
109            idl_import.root.path,
110        )] = True
111    return sorted(idl_import_roots.keys())
112
113def _collect_unique_java_roots(idl_imports):
114    idl_import_roots = dict()
115    for idl_import in idl_imports:
116        java_root = _java.root(idl_import.path)
117        if not java_root:
118            _log.error(_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR % idl_import.path)
119        idl_import_roots[java_root] = True
120    return sorted(idl_import_roots.keys())
121
122def _determine_idl_import_roots(
123        package,
124        idl_import_root = None,
125        idl_imports = []):
126    if idl_import_root == None:
127        return _collect_unique_java_roots(idl_imports)
128    return _collect_unique_idl_import_root_paths(
129        package,
130        idl_import_root,
131        idl_imports,
132    )
133
134def _process(
135        ctx,
136        idl_srcs = [],
137        idl_parcelables = [],
138        idl_import_root = None,
139        idl_preprocessed = [],
140        deps = [],
141        exports = [],
142        aidl = None,
143        aidl_lib = None,
144        aidl_framework = None,
145        uses_aosp_compiler = False,
146        idlopts = []):
147    """Processes Android IDL.
148
149    Args:
150      ctx: The context.
151      idl_srcs: sequence of Files. A list of the aidl source files to be
152        processed into Java source files and then compiled. Optional.
153      idl_parcelables: sequence of Files. A list of Android IDL definitions to
154        supply as imports. These files will be made available as imports for any
155        android_library target that depends on this library, directly or via its
156        transitive closure, but will not be translated to Java or compiled.
157
158        Only .aidl files that correspond directly to .java sources in this library
159        should be included (e.g. custom implementations of Parcelable), otherwise
160        idl_srcs should be used.
161
162        These files must be placed appropriately for the aidl compiler to find
163        them. See the description of idl_import_root for information about what
164        this means. Optional.
165      idl_import_root: string. Package-relative path to the root of the java
166        package tree containing idl sources included in this library. This path
167        will be used as the import root when processing idl sources that depend on
168        this library.
169
170        When idl_import_root is specified, both idl_parcelables and idl_srcs must
171        be at the path specified by the java package of the object they represent
172        under idl_import_root. When idl_import_root is not specified, both
173        idl_parcelables and idl_srcs must be at the path specified by their
174        package under a Java root. Optional.
175      idl_preprocessed: sequence of Files. A list of preprocessed Android IDL
176        definitions to supply as imports. These files will be made available as
177        imports for any android_library target that depends on this library,
178        directly or via its transitive closure, but will not be translated to
179        Java or compiled.
180
181        Only preprocessed .aidl files that correspond directly to .java sources
182        in this library should be included (e.g. custom implementations of
183        Parcelable), otherwise use idl_srcs for Android IDL definitions that
184        need to be translated to Java interfaces and use idl_parcelable for
185        non-preprcessed AIDL files. Optional.
186      deps: sequence of Targets. A list of dependencies. Optional.
187      exports: sequence of Targets. A list of exports. Optional.
188      aidl: Target. A target pointing to the aidl executable to be used for
189        Java code generation from *.idl source files. Optional, unless idl_srcs
190        are supplied.
191      aidl_lib: Target. A target pointing to the aidl_lib library required
192        during Java compilation when Java code is generated from idl sources.
193        Optional.
194      aidl_framework: Target. A target pointing to the aidl framework. Optional,
195        unless idl_srcs are supplied.
196      uses_aosp_compiler: boolean. If True, the upstream AOSP AIDL compiler is
197        used instead of the Google3-only AIDL compiler. This allows wider range
198        of AIDL language features including the structured parcelable, enum,
199        union, and many more. On the other hand, using this may cause noticeable
200        regression in terms of code size and performance as the compiler doesn't
201        implement several optimization techniques that the Google3 compiler has.
202      idlopts: list of string. Additional flags to add to the AOSP AIDL compiler
203        invocation.
204
205    Returns:
206      A IDLContextInfo provider.
207    """
208    if idl_srcs and not (aidl and aidl_framework):
209        _log.error(_AIDL_TOOLCHAIN_MISSING_ERROR)
210
211    transitive_idl_import_roots = []
212    transitive_idl_imports = []
213    transitive_idl_preprocessed = []
214    for dep in deps + exports:
215        transitive_idl_import_roots.append(dep.transitive_idl_import_roots)
216        transitive_idl_imports.append(dep.transitive_idl_imports)
217        transitive_idl_preprocessed.append(dep.transitive_idl_preprocessed)
218
219    idl_java_srcs = []
220    for idl_src in idl_srcs:
221        idl_java_src = ctx.actions.declare_file(
222            ctx.label.name + "_aidl/" + idl_src.path.replace(".aidl", ".java"),
223        )
224        idl_java_srcs.append(idl_java_src)
225        _gen_java_from_idl(
226            ctx,
227            out_idl_java_src = idl_java_src,
228            idl_src = idl_src,
229            transitive_idl_import_roots = depset(
230                _determine_idl_import_roots(
231                    ctx.label.package,
232                    idl_import_root,
233                    idl_parcelables + idl_srcs,
234                ),
235                transitive = transitive_idl_import_roots,
236                order = "preorder",
237            ),
238            transitive_idl_imports = depset(
239                idl_parcelables + idl_srcs,
240                transitive = transitive_idl_imports,
241                order = "preorder",
242            ),
243            transitive_idl_preprocessed = depset(
244                transitive = transitive_idl_preprocessed,
245            ),
246            aidl = aidl,
247            aidl_lib = aidl_lib,
248            aidl_framework = aidl_framework,
249            uses_aosp_compiler = uses_aosp_compiler,
250            idlopts = idlopts,
251        )
252
253    return IDLContextInfo(
254        idl_srcs = idl_srcs,
255        idl_import_root = idl_import_root,
256        idl_java_srcs = idl_java_srcs,
257        idl_deps = [aidl_lib] if (idl_java_srcs and aidl_lib) else [],
258        providers = [
259            # TODO(b/146216105): Make this a Starlark provider.
260            AndroidIdlInfo(
261                depset(
262                    _determine_idl_import_roots(
263                        ctx.label.package,
264                        idl_import_root,
265                        idl_parcelables + idl_srcs + idl_preprocessed,
266                    ),
267                    transitive = transitive_idl_import_roots,
268                    order = "preorder",
269                ),
270                depset(
271                    idl_parcelables + idl_srcs + idl_preprocessed,
272                    transitive = transitive_idl_imports,
273                    order = "preorder",
274                ),
275                depset(),  # TODO(b/146216105): Delete this field once in Starlark.
276                depset(idl_preprocessed, transitive = transitive_idl_preprocessed),
277            ),
278        ],
279    )
280
281idl = struct(
282    process = _process,
283)
284
285# Visible for testing.
286testing = struct(
287    get_idl_import_root_path = _get_idl_import_root_path,
288)
289