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"""
16This file contains rules and logic to initialize GBL workspace.
17"""
18
19def _abs_path(repo_ctx, path):
20    return repo_ctx.execute(["readlink", "-f", path]).stdout.strip("\n")
21
22def _dir_of(repo_ctx, path):
23    return repo_ctx.execute(["dirname", _abs_path(repo_ctx, path)]).stdout.strip("\n")
24
25def _gbl_llvm_prebuilts_impl(repo_ctx):
26    """Implementation for gbl_llvm_prebuilts
27
28    The repository rule sets up a repo for hosting LLVM distribution and Linux sysroot. They can
29    be provided manually via the `GBL_LLVM_PREBUILTS` and `GBL_LINUX_SYSROOT` environment
30    variables. If they don't exist, the top-level workspace needs to define them in repo
31    `@llvm_linux_x86_64_prebuilts` and `@linux_x86_64_sysroot` respectively.
32
33    Only Linux x86_64 platform is supported.
34    """
35    prebuilts = repo_ctx.os.environ.get("GBL_LLVM_PREBUILTS")
36    if prebuilts:
37        repo_ctx.symlink(prebuilts, "llvm-linux-x86")
38    else:
39        path = _dir_of(repo_ctx, repo_ctx.path(Label("@llvm_linux_x86_64_prebuilts//:BUILD.bazel")))
40        repo_ctx.symlink(path, "llvm-linux-x86")
41
42    # Linux host toolchain additionally needs a sysroot
43    linux_glibc = repo_ctx.os.environ.get("GBL_LINUX_SYSROOT")
44    if linux_glibc:
45        repo_ctx.symlink(linux_glibc, "linux_glibc")
46    else:
47        path = _dir_of(repo_ctx, repo_ctx.path(Label("@linux_x86_64_sysroot//:BUILD.bazel")))
48        repo_ctx.symlink(path, "linux_glibc")
49
50    # Get the bin directory so that we can access other LLVM tools by path.
51    gbl_llvm_bin_dir = _abs_path(repo_ctx, "llvm-linux-x86/bin")
52
53    # Create a info.bzl file in the assembled repo to export header/library/tool paths.
54    info_bzl_content = """
55def gbl_llvm_tool_path(tool_name):
56    return "{}/" + tool_name
57""".format(gbl_llvm_bin_dir)
58
59    # Get the builtin include directories.
60    clang = _abs_path(repo_ctx, "llvm-linux-x86/bin/clang")
61    llvm_resource_dir = repo_ctx.execute([clang, "--print-resource-dir"]).stdout.strip("\n")
62    info_bzl_content += """
63LLVM_PREBUILTS_C_INCLUDE = "{}"
64LLVM_PREBUILTS_CPP_INCLUDE = "{}"
65""".format(
66        "{}/include".format(llvm_resource_dir),
67        _abs_path(repo_ctx, "llvm-linux-x86/include/c++/v1"),
68    )
69
70    # Linux sysroot headers
71    sysroot_includes = [
72        _abs_path(repo_ctx, "linux_glibc/sysroot/usr/include"),
73        _abs_path(repo_ctx, "linux_glibc/sysroot/usr/include/x86_64-linux-gnu"),
74    ]
75    info_bzl_content += """
76LINUX_SYSROOT_INCLUDES = [{}]
77""".format(",".join(["\"{}\"".format(inc) for inc in sysroot_includes]))
78
79    repo_ctx.file("info.bzl", info_bzl_content)
80
81    # The following files are needed for defining bindgen toolchain, we symlink them out to the
82    # top level directory in case the the distribution repo has its own BUILD file which blocks
83    # direct access.
84    repo_ctx.symlink("llvm-linux-x86/bin/clang", "clang")
85
86    # In some prebuilt versions, "libc++.so" is a symlink to "libc++.so.1" etc. We need to use the
87    # same name as the actual library file name in cc_import(). Otherwise it complains it can't
88    # find the shared object.
89    libcpp_sharelib_path = _abs_path(repo_ctx, "llvm-linux-x86/lib/libc++.so")
90    libcpp_base_name = repo_ctx.execute(["basename", libcpp_sharelib_path]).stdout.strip("\n")
91    repo_ctx.symlink(libcpp_sharelib_path, libcpp_base_name)
92
93    # Do the same for libclang.so in case it's a symlink.
94    libclang_sharelib_path = _abs_path(repo_ctx, "llvm-linux-x86/lib/libclang.so")
95    libclang_basename = repo_ctx.execute(["basename", libclang_sharelib_path]).stdout.strip("\n")
96    repo_ctx.symlink(libclang_sharelib_path, libclang_basename)
97
98    # Add a BUILD file to make it a package
99    repo_ctx.file("BUILD", """
100package(
101    default_visibility = ["//visibility:public"],
102)
103
104exports_files(glob(["**/*"]))
105
106sh_binary(
107    name = "clang-bin",
108    srcs = [":clang"],
109)
110
111cc_import(
112    name = "libc++",
113    shared_library = ":{}",
114)
115
116cc_import(
117    name = "libclang",
118    shared_library = ":{}",
119)
120""".format(libcpp_base_name, libclang_basename))
121
122gbl_llvm_prebuilts = repository_rule(
123    implementation = _gbl_llvm_prebuilts_impl,
124    local = True,
125    environ = ["GBL_LLVM_PREBUILTS", "GBL_LINUX_SYSROOT"],
126)
127
128# The current rust version used by GBL. This needs to be manually udpated when new version of
129# prebuilts is uploaded to https://android.googlesource.com/platform/prebuilts/rust/
130GBL_RUST_VERSION = "1.77.1.p1"
131
132def _android_rust_prebuilts_impl(repo_ctx):
133    """Assemble a rust toolchain repo from the Android rust prebuilts repo.
134
135    The Android rust prebuilts repo is expected to be from
136    https://android.googlesource.com/platform/prebuilts/rust/.
137
138    Attributes:
139        path (String): Relative path to the Android rust prebuilts repo.
140        build_file (Label): Label of the build file to use.
141    """
142
143    # We only support linux x86 platform.
144    path = repo_ctx.workspace_root.get_child(repo_ctx.attr.path).get_child("linux-x86")
145
146    # Symlink everything into the assembled repo.
147    path = path.get_child(GBL_RUST_VERSION)
148    for entry in path.readdir():
149        # Ignore native BUILD file as we'll use override from `ctx.attr.build_file` instead.
150        if entry.basename == "BUILD" or entry.basename == "BUILD.bazel":
151            continue
152        repo_ctx.symlink(entry.realpath, entry.basename)
153
154    # Symlink the provided build file
155    repo_ctx.symlink(repo_ctx.attr.build_file, "BUILD")
156
157android_rust_prebuilts = repository_rule(
158    implementation = _android_rust_prebuilts_impl,
159    attrs = {
160        "path": attr.string(mandatory = True),
161        "build_file": attr.label(mandatory = True),
162    },
163)
164