xref: /aosp_15_r20/build/bazel/rules/cc/cc_aidl_code_gen.bzl (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1# Copyright (C) 2022 The Android Open Source Project
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
15load("@bazel_skylib//lib:paths.bzl", "paths")
16load("//build/bazel/rules/aidl:aidl_library.bzl", "AidlGenInfo")
17load(":cc_library_common.bzl", "create_ccinfo_for_includes")
18
19_SOURCES = "sources"
20_HEADERS = "headers"
21_INCLUDE_DIR = "include_dir"
22
23def _cc_aidl_code_gen_impl(ctx):
24    """
25    Generate stub C++ code from direct aidl srcs using transitive deps
26
27    Args:
28        ctx: (RuleContext)
29    Returns:
30        (DefaultInfo) Generated .cpp and .cpp.d files
31        (CcInfo)      Generated headers and their include dirs
32    """
33    generated_srcs, generated_hdrs, include_dirs = [], [], []
34
35    for aidl_info in [d[AidlGenInfo] for d in ctx.attr.deps]:
36        stub = _compile_aidl_srcs(ctx, aidl_info, ctx.attr.lang)
37        generated_srcs.extend(stub[_SOURCES])
38        generated_hdrs.extend(stub[_HEADERS])
39        include_dirs.extend([stub[_INCLUDE_DIR]])
40
41    return [
42        DefaultInfo(files = depset(direct = generated_srcs + generated_hdrs)),
43        create_ccinfo_for_includes(
44            ctx,
45            hdrs = generated_hdrs,
46            includes = include_dirs,
47        ),
48    ]
49
50def _declare_stub_files(ctx, aidl_file, direct_include_dir, lang):
51    """
52    Declare stub files that AIDL compiles to for cc
53
54    Args:
55      ctx:                   (Context) Used to register declare_file actions.
56      aidl_file:             (File) The aidl file
57      direct_include_dir:     (String) The path to given aidl file minus the aidl package namespace
58      lang:                   (String) AIDL backend
59    Returns:
60      (list[File]) List of declared stub files
61    """
62    ret = {}
63    ret[_SOURCES], ret[_HEADERS] = [], []
64    short_basename = paths.replace_extension(aidl_file.basename, "")
65
66    # aidl file path relative to direct include dir
67    short_path = paths.relativize(aidl_file.path, direct_include_dir)
68
69    ret[_SOURCES] = [
70        ctx.actions.declare_file(
71            paths.join(
72                ctx.label.name,
73                paths.dirname(short_path),
74                short_basename + ".cpp",
75            ),
76        ),
77    ]
78
79    headers = [short_basename + ".h"]
80
81    # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/gen.go;bpv=1;bpt=1?q=cc%2Fgen.go
82    # Strip prefix I before creating basenames for bp and bn headers
83    if len(short_basename) > 2 and short_basename.startswith("I") and short_basename[1].upper() == short_basename[1]:
84        short_basename = short_basename.removeprefix("I")
85
86    headers.extend([
87        "Bp" + short_basename + ".h",
88        "Bn" + short_basename + ".h",
89    ])
90
91    # Headers for ndk backend are nested under aidl directory to prevent
92    # c++ namespaces collision with cpp backend
93    # Context: https://android.googlesource.com/platform/system/tools/aidl/+/7c93337add97ce36f0a35c6705f3a67a441f2ae7
94    out_dir_prefix = ""
95    if lang == "ndk":
96        out_dir_prefix = "aidl"
97
98    for basename in headers:
99        ret[_HEADERS].append(ctx.actions.declare_file(
100            paths.join(ctx.label.name, out_dir_prefix, paths.dirname(short_path), basename),
101        ))
102
103    return ret
104
105def _compile_aidl_srcs(ctx, aidl_info, lang):
106    """
107    Compile AIDL stub code for direct AIDL srcs
108
109    Args:
110      ctx:        (Context) Used to register declare_file actions
111      aidl_info:  (AidlGenInfo) aidl_info from an aidl library
112      lang:       (String) AIDL backend
113
114    Returns:
115      (Dict)      A dict of where the the values are generated headers (.h) and their boilerplate implementation (.cpp)
116    """
117
118    ret = {}
119    ret[_SOURCES], ret[_HEADERS] = [], []
120
121    # transitive_include_dirs is traversed in preorder
122    direct_include_dir = aidl_info.transitive_include_dirs.to_list()[0]
123
124    # Given AIDL file a/b/c/d/Foo.aidl with direct_include_dir a/b
125    # The outputs paths are
126    #  cpp backend:
127    #   <package-dir>/<target-name>/c/d/*Foo.h
128    #   <package-dir>/<target-name>/c/d/Foo.cpp
129    #  ndk backend:
130    #   <package-dir>/<target-name>/aidl/c/d/*Foo.h
131    #   <package-dir>/<target-name>/c/d/Foo.cpp
132    #
133    # where <package-dir> is bazel-bin/<path-to-cc_aidl_library-target>
134    # and   <target-name> is <cc_aidl_library-name>_aidl_code_gen
135    # cpp and ndk are created in separate cc_aidl-library targets, so
136    # <target-name> are unique among cpp and ndk backends
137
138    # include dir, relative to package dir, to the generated headers
139    ret[_INCLUDE_DIR] = ctx.label.name
140
141    # AIDL needs to know the full path to outputs
142    # <bazel-bin>/<package-dir>/<target-name>
143    out_dir = paths.join(
144        ctx.bin_dir.path,
145        ctx.label.package,
146        ret[_INCLUDE_DIR],
147    )
148
149    outputs = []
150    for aidl_file in aidl_info.srcs.to_list():
151        files = _declare_stub_files(ctx, aidl_file, direct_include_dir, lang)
152        outputs.extend(files[_SOURCES] + files[_HEADERS])
153        ret[_SOURCES].extend(files[_SOURCES])
154        ret[_HEADERS].extend(files[_HEADERS])
155
156    args = ctx.actions.args()
157
158    # Add flags from lang-agnostic aidl_library target
159    args.add_all(aidl_info.flags)
160
161    # Add flags specific for cpp and ndk lang
162    if ctx.attr.min_sdk_version != "":
163        args.add("--min_sdk_version={}".format(ctx.attr.min_sdk_version))
164
165    if aidl_info.hash_file == None:
166        args.add("--hash=notfrozen")
167    else:
168        args.add("--hash=$(tail -1 {})".format(aidl_info.hash_file))
169
170    args.add_all([
171        "--ninja",
172        "--lang={}".format(lang),
173        "--out={}".format(out_dir),
174        "--header_out={}".format(out_dir),
175    ])
176    args.add_all(["-I {}".format(i) for i in aidl_info.transitive_include_dirs.to_list()])
177    args.add_all(["{}".format(aidl_file.path) for aidl_file in aidl_info.srcs.to_list()])
178
179    ctx.actions.run(
180        inputs = aidl_info.transitive_srcs,
181        outputs = outputs,
182        executable = ctx.executable._aidl,
183        arguments = [args],
184        progress_message = "Compiling AIDL binding",
185        mnemonic = "CcAidlCodeGen",
186    )
187
188    return ret
189
190cc_aidl_code_gen = rule(
191    implementation = _cc_aidl_code_gen_impl,
192    doc = "This rule generates AIDL stub C++ code for the direct srcs in each " +
193          "AidlGenInfo in deps attribute using transitive srcs and transitive_include_dirs.",
194    attrs = {
195        "deps": attr.label_list(
196            providers = [AidlGenInfo],
197        ),
198        "lang": attr.string(
199            mandatory = True,
200            values = ["cpp", "ndk"],
201        ),
202        "min_sdk_version": attr.string(),
203        "_aidl": attr.label(
204            allow_single_file = True,
205            executable = True,
206            cfg = "exec",
207            default = Label("//system/tools/aidl"),
208        ),
209    },
210    provides = [CcInfo],
211)
212