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"""Defines a cc toolchain for kernel build, based on clang."""
16
17load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "CPP_TOOLCHAIN_TYPE")
18load(
19    "@kernel_toolchain_info//:dict.bzl",
20    "VARS",
21)
22load(":clang_config.bzl", "clang_config")
23
24def _clang_toolchain_internal(
25        name,
26        clang_version,
27        arch,
28        clang_pkg,
29        linker_files = None,
30        sysroot_label = None,
31        sysroot_dir = None,
32        bin_files = None,
33        bin_dirs = None,
34        lib_files = None,
35        lib_dirs = None,
36        target = None,
37        extra_compatible_with = None,
38        extra_features = None,
39        dynamic_runtime_lib = None,
40        static_runtime_lib = None,
41        static_link_cpp_runtimes = None):
42    """Defines a cc toolchain for kernel build, based on clang.
43
44    Args:
45        name: name of the toolchain
46        clang_version: value of `CLANG_VERSION`, e.g. `r475365b`.
47        arch: an ArchInfo object to look up extra kwargs.
48        clang_pkg: Label to any target in the clang toolchain package.
49
50            This is used as an anchor to locate other targets in the package.
51            Name of the label is ignored.
52        linker_files: Additional dependencies to the linker
53        sysroot_label: Label to a list of files from sysroot
54        sysroot_dir: Label containing a single directory to sysroot.
55        bin_files: Files for `-B`
56        bin_dirs: Directory to be set in `-B`
57        lib_files: Files for `-L`
58        lib_dirs: Directory to be set in `-L`
59        target: The `--target` option provided to clang. This is usually `NDK_TRIPLE`.
60        extra_compatible_with: Extra `exec_compatible_with` / `target_compatible_with`.
61        extra_features: Extra features enabled on this toolchain
62        dynamic_runtime_lib: pass to cc_toolchain
63        static_runtime_lib: pass to cc_toolchain
64        static_link_cpp_runtimes: If true, enable "static_link_cpp_runtimes" feature but
65            disable "static-libgcc".
66    """
67
68    sysroot_labels = []
69    if sysroot_label != None:
70        sysroot_labels.append(sysroot_label)
71
72    if linker_files == None:
73        linker_files = []
74
75    if bin_files == None:
76        bin_files = []
77
78    if lib_files == None:
79        lib_files = []
80
81    if extra_compatible_with == None:
82        extra_compatible_with = []
83
84    clang_pkg = native.package_relative_label(clang_pkg)
85    clang_includes = clang_pkg.relative(":includes")
86
87    # Technically we can split the binaries into those for compiler, linker
88    # etc., but since these binaries are usually updated together, it is okay
89    # to use a superset here.
90    clang_all_binaries = clang_pkg.relative(":binaries")
91
92    # Individual binaries
93    # From _setup_env.sh
94    #  HOSTCC=clang
95    #  HOSTCXX=clang++
96    #  CC=clang
97    #  LD=ld.lld
98    #  AR=llvm-ar
99    #  NM=llvm-nm
100    #  OBJCOPY=llvm-objcopy
101    #  OBJDUMP=llvm-objdump
102    #  OBJSIZE=llvm-size
103    #  READELF=llvm-readelf
104    #  STRIP=llvm-strip
105    #
106    # Note: ld.lld does not recognize --target etc. from android.bzl,
107    # so just use clang directly
108    clang = clang_pkg.relative(":bin/clang")
109    clang_plus_plus = clang_pkg.relative(":bin/clang++")
110
111    # TODO: Support building C++ device binaries.
112    #   This requires adding "-static-libstdc++" to avoid dynamic linkage.
113    ld = clang_plus_plus if arch.target_os == "linux" else clang
114    strip = clang_pkg.relative(":bin/llvm-strip")
115    ar = clang_pkg.relative(":bin/llvm-ar")
116    objcopy = clang_pkg.relative(":bin/llvm-objcopy")
117    # cc_* rules doesn't seem to need nm, obj-dump, size, and readelf
118
119    native.filegroup(
120        name = name + "_compiler_files",
121        srcs = [
122            clang_all_binaries,
123            clang_includes,
124        ] + sysroot_labels + bin_files,
125    )
126
127    native.filegroup(
128        name = name + "_linker_files",
129        srcs = [clang_all_binaries] + sysroot_labels + linker_files + bin_files + lib_files,
130    )
131
132    native.filegroup(
133        name = name + "_all_files",
134        srcs = [
135            clang_all_binaries,
136            name + "_compiler_files",
137            name + "_linker_files",
138        ],
139    )
140
141    clang_config(
142        name = name + "_clang_config",
143        clang_version = clang_version,
144        sysroot_dir = sysroot_dir,
145        bin_dirs = bin_dirs,
146        lib_dirs = lib_dirs,
147        target_cpu = arch.target_cpu,
148        target_os = arch.target_os,
149        target = target,
150        toolchain_identifier = name + "_clang_id",
151        clang = clang,
152        ld = ld,
153        clang_plus_plus = clang_plus_plus,
154        strip = strip,
155        ar = ar,
156        objcopy = objcopy,
157        extra_features = extra_features,
158        static_link_cpp_runtimes = static_link_cpp_runtimes,
159    )
160
161    native.cc_toolchain(
162        name = name + "_cc_toolchain",
163        all_files = name + "_all_files",
164        ar_files = clang_all_binaries,
165        compiler_files = name + "_compiler_files",
166        dwp_files = clang_all_binaries,
167        linker_files = name + "_linker_files",
168        objcopy_files = clang_all_binaries,
169        strip_files = clang_all_binaries,
170        dynamic_runtime_lib = dynamic_runtime_lib,
171        static_runtime_lib = static_runtime_lib,
172        supports_param_files = False,
173        toolchain_config = name + "_clang_config",
174        toolchain_identifier = name + "_clang_id",
175    )
176
177    target_compatible_with = [
178        "@platforms//os:{}".format(arch.target_os),
179        "@platforms//cpu:{}".format(arch.target_cpu),
180    ] + extra_compatible_with
181    if arch.target_libc != None:
182        target_compatible_with.append(Label("//build/kernel/kleaf/platforms/libc:{}".format(arch.target_libc)))
183
184    native.toolchain(
185        name = name,
186        exec_compatible_with = [
187            "@platforms//os:linux",
188            "@platforms//cpu:x86_64",
189        ] + extra_compatible_with,
190        target_compatible_with = target_compatible_with,
191        toolchain = name + "_cc_toolchain",
192        toolchain_type = CPP_TOOLCHAIN_TYPE,
193        visibility = ["@kleaf_clang_toolchain//:__subpackages__"],
194    )
195
196def clang_toolchain(
197        name,
198        clang_version,
199        clang_pkg,
200        arch,
201        extra_compatible_with = None):
202    """Declare a clang toolchain for the given OS-architecture.
203
204    The toolchain should be under `prebuilts/clang/host/linux-x86`.
205
206    Args:
207        name: name of the toolchain
208        clang_version: nonconfigurable. version of the toolchain
209        clang_pkg: Label to any target in the clang toolchain package.
210
211            This is used as an anchor to locate other targets in the package.
212            Name of the label is ignored.
213        arch: key to look up extra kwargs.
214        extra_compatible_with: nonconfigurable. extra `exec_compatible_with` and `target_compatible_with`
215    """
216
217    extra_kwargs = _get_extra_kwargs(arch)
218
219    _clang_toolchain_internal(
220        name = name,
221        clang_version = clang_version,
222        arch = arch,
223        clang_pkg = clang_pkg,
224        extra_compatible_with = extra_compatible_with,
225        **extra_kwargs
226    )
227
228_GCC_PKG = Label("//prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8")
229
230def _get_extra_kwargs(arch):
231    if arch.target_os == "linux" and arch.target_libc == "musl":
232        return dict(
233            target = "x86_64-unknown-linux-musl",
234            sysroot_label = Label("//prebuilts/kernel-build-tools:musl_sysroot_files"),
235            sysroot_dir = Label("//prebuilts/kernel-build-tools:musl_sysroot_dir"),
236            linker_files = [
237                Label("//prebuilts/kernel-build-tools:libs"),
238            ],
239            extra_features = [
240                "kleaf-lld-compiler-rt",
241                "kleaf-host-musl",
242            ],
243            static_runtime_lib = Label(":empty_filegroup"),
244            dynamic_runtime_lib = Label("//prebuilts/kernel-build-tools:libc_musl_file"),
245            static_link_cpp_runtimes = True,
246        )
247
248    if arch.target_os == "linux":
249        return dict(
250            linker_files = [
251                Label("//prebuilts/kernel-build-tools:linux-x86-libs"),
252            ],
253            target = "x86_64-unknown-linux-gnu",
254            sysroot_label = Label("//build/kernel:sysroot"),
255            sysroot_dir = Label("//build/kernel:sysroot_dir"),
256            bin_files = [_GCC_PKG.relative(":bin_files")],
257            bin_dirs = [_GCC_PKG.relative(":bin_dirs")],
258            lib_files = [_GCC_PKG.relative(":lib_files")],
259            lib_dirs = [_GCC_PKG.relative(":lib_dirs")],
260            extra_features = [
261                "kleaf-lld",
262            ],
263        )
264
265    if arch.target_os != "android":
266        fail("Unsupported {}".format(arch))
267
268    if arch.target_cpu == "arm64":
269        return dict(
270            target = VARS.get("AARCH64_NDK_TRIPLE"),
271            sysroot_label = "@prebuilt_ndk//:sysroot_{}_files".format(VARS.get("AARCH64_NDK_TRIPLE")) if "AARCH64_NDK_TRIPLE" in VARS else None,
272            sysroot_dir = "@prebuilt_ndk//:sysroot_dir" if "AARCH64_NDK_TRIPLE" in VARS else None,
273        )
274
275    if arch.target_cpu == "arm":
276        return dict(
277            target = VARS.get("ARM_NDK_TRIPLE"),
278            sysroot_label = "@prebuilt_ndk//:sysroot_{}_files".format(VARS.get("ARM_NDK_TRIPLE")) if "ARM_NDK_TRIPLE" in VARS else None,
279            sysroot_dir = "@prebuilt_ndk//:sysroot_dir" if "ARM_NDK_TRIPLE" in VARS else None,
280        )
281
282    if arch.target_cpu == "x86_64":
283        return dict(
284            target = VARS.get("X86_64_NDK_TRIPLE"),
285            sysroot_label = "@prebuilt_ndk//:sysroot_{}_files".format(VARS.get("X86_64_NDK_TRIPLE")) if "X86_64_NDK_TRIPLE" in VARS else None,
286            sysroot_dir = "@prebuilt_ndk//:sysroot_dir" if "X86_64_NDK_TRIPLE" in VARS else None,
287        )
288
289    if arch.target_cpu == "i386":
290        return dict(
291            # i386 uses the same NDK_TRIPLE as x86_64
292            target = VARS.get("X86_64_NDK_TRIPLE"),
293            sysroot_label = "@prebuilt_ndk//:sysroot_{}_files".format(VARS.get("X86_64_NDK_TRIPLE")) if "X86_64_NDK_TRIPLE" in VARS else None,
294            sysroot_dir = "@prebuilt_ndk//:sysroot_dir" if "X86_64_NDK_TRIPLE" in VARS else None,
295        )
296
297    if arch.target_cpu == "riscv64":
298        return dict(
299            # TODO(b/271919464): We need NDK_TRIPLE for riscv
300        )
301
302    fail("Unsupported {}".format(arch))
303