1# Copyright (C) 2023 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
15"""
16`ndk_headers` provides a CcInfo for building SDK variants of CC libraries.
17Unlike cc_library_headers, it has a `from` and `to` attribute that can be used to create an import path for headers that
18is different than the checked-in folder layout.
19"""
20
21load("@bazel_skylib//lib:paths.bzl", "paths")
22
23_VERSIONER_DEPS_DIR = "bionic/libc/versioner-dependencies"
24
25# Creates an action to copy NDK headers to out/ after handling {strip}_import_prefix
26# Return a tuple with the following elements
27# 1. root of the synthetic dir (this will become -isystem/-I)
28# 2. list of hdr files in out
29def _assemeble_headers(ctx):
30    out_dir = paths.join(
31        ctx.bin_dir.path,
32        ctx.label.package,
33        ctx.label.name,
34    )
35
36    outs = []
37    for hdr in ctx.files.hdrs:
38        rel_to_package = paths.relativize(
39            hdr.path,
40            ctx.label.package,
41        )
42        rel_after_strip_import = paths.relativize(
43            rel_to_package,
44            ctx.attr.strip_import_prefix,
45        )
46        out = ctx.actions.declare_file(
47            paths.join(
48                ctx.label.name,
49                ctx.attr.import_prefix,
50                rel_after_strip_import,
51            ),
52        )
53
54        ctx.actions.run_shell(
55            inputs = depset(direct = [hdr]),
56            outputs = [out],
57            command = "cp -f %s %s" % (hdr.path, out.path),
58            mnemonic = "CopyFile",
59            use_default_shell_env = True,
60        )
61        outs.append(out)
62
63    return out_dir, outs
64
65# Creates an action to run versioner on the assembled NDK headers
66# Used for libc
67# Return a tuple with the following elements
68# 1. root of the synthetic dir (this will become -isystem/-I)
69# 2. list of hdr files in out
70def _version_headers(ctx, assembled_out_dir, assembled_hdrs):
71    out_dir = assembled_out_dir + ".versioned"
72    outs = []
73
74    for assembled_hdr in assembled_hdrs:
75        rel = paths.relativize(
76            assembled_hdr.path,
77            assembled_out_dir,
78        )
79        out = ctx.actions.declare_file(
80            paths.join(
81                ctx.label.name + ".versioned",
82                rel,
83            ),
84        )
85        outs.append(out)
86
87    args = ctx.actions.args()
88    args.add_all(["-o", out_dir, assembled_out_dir, _VERSIONER_DEPS_DIR])
89
90    ctx.actions.run(
91        executable = ctx.executable._versioner,
92        arguments = [args],
93        inputs = assembled_hdrs,
94        outputs = outs,
95        mnemonic = "VersionBionicHeaders",
96        tools = ctx.files._versioner_include_deps,
97    )
98
99    return out_dir, outs
100
101# Keep in sync with
102# https://cs.android.com/android/_/android/platform/build/soong/+/main:cc/config/toolchain.go;l=120-127;drc=1717b3bb7a49c52b26c90469f331b55f7b681690;bpv=1;bpt=0
103def _ndk_triple(ctx):
104    if ctx.target_platform_has_constraint(ctx.attr._arm_constraint[platform_common.ConstraintValueInfo]):
105        return "arm-linux-androideabi"
106    if ctx.target_platform_has_constraint(ctx.attr._arm64_constraint[platform_common.ConstraintValueInfo]):
107        return "aarch64-linux-android"
108    if ctx.target_platform_has_constraint(ctx.attr._riscv64_constraint[platform_common.ConstraintValueInfo]):
109        return "riscv64-linux-android"
110    if ctx.target_platform_has_constraint(ctx.attr._x86_constraint[platform_common.ConstraintValueInfo]):
111        return "i686-linux-android"
112    if ctx.target_platform_has_constraint(ctx.attr._x86_64_constraint[platform_common.ConstraintValueInfo]):
113        return "x86_64-linux-android"
114    fail("Could not determine NDK triple: unrecognized arch")
115
116def _ndk_headers_impl(ctx):
117    # Copy the hdrs to a synthetic root
118    out_dir, outs = _assemeble_headers(ctx)
119    if ctx.attr.run_versioner:
120        # Version the copied headers
121        out_dir, outs = _version_headers(ctx, out_dir, outs)
122
123    compilation_context = cc_common.create_compilation_context(
124        headers = depset(outs),
125        # ndk_headers are provided as -isystem and not -I
126        # https://cs.android.com/android/_/android/platform/build/soong/+/main:cc/compiler.go;l=394-403;drc=e0202c4823d1b3cabf63206d3a6611868d1559e1;bpv=1;bpt=0
127        system_includes = depset([
128            out_dir,
129            paths.join(out_dir, _ndk_triple(ctx)),
130        ]),
131    )
132    return [
133        DefaultInfo(files = depset(outs)),
134        CcInfo(compilation_context = compilation_context),
135    ]
136
137ndk_headers = rule(
138    implementation = _ndk_headers_impl,
139    attrs = {
140        "strip_import_prefix": attr.string(
141            doc =
142                """
143            The prefix to strip from the .h files of this target.
144            e.g if the hdrs are `dir/foo.h` and strip_import_prefix is `dir`, then rdeps will include it as #include <foo.h>
145            """,
146            default = "",
147        ),
148        "import_prefix": attr.string(
149            doc =
150                """
151            The prefix to add to the .h files of this target.
152            e.g if the hdrs are `dir/foo.h` and import_prefix is `dir_prefix`, then rdeps will include it as #include <dir_prefix/dir/foo.h>
153            """,
154            default = "",
155        ),
156        "hdrs": attr.label_list(
157            doc = ".h files contributed by the library to the Public API surface (NDK)",
158            allow_files = True,
159        ),
160        "run_versioner": attr.bool(
161            doc = "Run versioner with bionic/libc/versioner-dependencies on the include path. Used only by libc",
162            default = False,
163        ),
164        "_versioner": attr.label(
165            executable = True,
166            cfg = "exec",
167            default = Label("//prebuilts/clang-tools:versioner"),
168        ),
169        "_versioner_include_deps": attr.label(
170            doc = "Filegroup containing the .h files placed on the include path when running versioner",
171            default = Label("//bionic/libc:versioner-dependencies"),
172        ),
173        "_arm_constraint": attr.label(
174            default = Label("//build/bazel_common_rules/platforms/arch:arm"),
175        ),
176        "_arm64_constraint": attr.label(
177            default = Label("//build/bazel_common_rules/platforms/arch:arm64"),
178        ),
179        "_riscv64_constraint": attr.label(
180            default = Label("//build/bazel_common_rules/platforms/arch:riscv64"),
181        ),
182        "_x86_constraint": attr.label(
183            default = Label("//build/bazel_common_rules/platforms/arch:x86"),
184        ),
185        "_x86_64_constraint": attr.label(
186            default = Label("//build/bazel_common_rules/platforms/arch:x86_64"),
187        ),
188    },
189    provides = [CcInfo],
190)
191