• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2This file specifies a clang toolchain that can run on a Linux host which does depend on any
3installed packages from the host machine.
4
5See build_toolchain.bzl for more details on the creation of the toolchain.
6
7It uses the usr subfolder of the built toolchain as a sysroot
8
9It follows the example of:
10 - https://docs.bazel.build/versions/4.2.1/tutorial/cc-toolchain-config.html
11 - https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl
12"""
13
14load(
15    "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
16    "action_config",
17    "feature",
18    "flag_group",
19    "flag_set",
20    "tool",
21    "variable_with_value",
22)
23load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
24
25# The location of the created clang toolchain.
26EXTERNAL_TOOLCHAIN = "external/clang_linux_amd64_musl"
27
28def _clang_impl(ctx):
29    action_configs = _make_action_configs()
30    features = []
31    features += _make_default_flags()
32    features += _make_diagnostic_flags()
33
34    # https://docs.bazel.build/versions/main/skylark/lib/cc_common.html#create_cc_toolchain_config_info
35    # Note, this rule is defined in Java code, not Starlark
36    # https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java
37    return cc_common.create_cc_toolchain_config_info(
38        ctx = ctx,
39        features = features,
40        abi_libc_version = "unknown",
41        abi_version = "unknown",
42        action_configs = action_configs,
43        builtin_sysroot = EXTERNAL_TOOLCHAIN + "/usr",
44        compiler = "clang",
45        host_system_name = "local",
46        target_cpu = "k8",
47        target_libc = "musl",
48        target_system_name = "local",
49        toolchain_identifier = "clang-toolchain",
50    )
51
52provide_clang_toolchain_config = rule(
53    attrs = {},
54    provides = [CcToolchainConfigInfo],
55    implementation = _clang_impl,
56)
57
58def _make_action_configs():
59    """
60    This function sets up the tools needed to perform the various compile/link actions.
61
62    Bazel normally restricts us to referring to (and therefore running) executables/scripts
63    that are in this directory (That is EXEC_ROOT/toolchain). However, the executables we want
64    to run are brought in via WORKSPACE.bazel and are located in EXEC_ROOT/external/clang....
65    Therefore, we make use of "trampoline scripts" that will call the binaries from the
66    toolchain directory.
67
68    These action_configs also let us dynamically specify arguments from the Bazel
69    environment if necessary (see cpp_link_static_library_action).
70    """
71
72    # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=435;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
73    clang_tool = tool(path = "clang_trampoline.sh")
74    lld_tool = tool(path = "lld_trampoline.sh")
75    ar_tool = tool(path = "ar_trampoline.sh")
76
77    # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=488;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
78    assemble_action = action_config(
79        action_name = ACTION_NAMES.assemble,
80        tools = [clang_tool],
81    )
82    c_compile_action = action_config(
83        action_name = ACTION_NAMES.c_compile,
84        tools = [clang_tool],
85    )
86    cpp_compile_action = action_config(
87        action_name = ACTION_NAMES.cpp_compile,
88        tools = [clang_tool],
89    )
90    linkstamp_compile_action = action_config(
91        action_name = ACTION_NAMES.linkstamp_compile,
92        tools = [clang_tool],
93    )
94    preprocess_assemble_action = action_config(
95        action_name = ACTION_NAMES.preprocess_assemble,
96        tools = [clang_tool],
97    )
98
99    cpp_link_dynamic_library_action = action_config(
100        action_name = ACTION_NAMES.cpp_link_dynamic_library,
101        tools = [lld_tool],
102    )
103    cpp_link_executable_action = action_config(
104        action_name = ACTION_NAMES.cpp_link_executable,
105        # Bazel assumes it is talking to clang when building an executable. There are
106        # "-Wl" flags on the command: https://releases.llvm.org/6.0.1/tools/clang/docs/ClangCommandLineReference.html#cmdoption-clang-Wl
107        tools = [clang_tool],
108    )
109    cpp_link_nodeps_dynamic_library_action = action_config(
110        action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library,
111        tools = [lld_tool],
112    )
113
114    # This is the same rule as
115    # https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl#L143
116    # By default, there are no flags or libraries passed to the llvm-ar tool, so
117    # we need to specify them. The variables mentioned by expand_if_available are defined
118    # https://docs.bazel.build/versions/main/cc-toolchain-config-reference.html#cctoolchainconfiginfo-build-variables
119    cpp_link_static_library_action = action_config(
120        action_name = ACTION_NAMES.cpp_link_static_library,
121        flag_sets = [
122            flag_set(
123                flag_groups = [
124                    flag_group(
125                        # https://llvm.org/docs/CommandGuide/llvm-ar.html
126                        # replace existing files or insert them if they already exist,
127                        # create the file if it doesn't already exist
128                        # symbol table should be added
129                        # Deterministic timestamps should be used
130                        flags = ["rcsD", "%{output_execpath}"],
131                        # Despite the name, output_execpath just refers to linker output,
132                        # e.g. libFoo.a
133                        expand_if_available = "output_execpath",
134                    ),
135                ],
136            ),
137            flag_set(
138                flag_groups = [
139                    flag_group(
140                        iterate_over = "libraries_to_link",
141                        flag_groups = [
142                            flag_group(
143                                flags = ["%{libraries_to_link.name}"],
144                                expand_if_equal = variable_with_value(
145                                    name = "libraries_to_link.type",
146                                    value = "object_file",
147                                ),
148                            ),
149                            flag_group(
150                                flags = ["%{libraries_to_link.object_files}"],
151                                iterate_over = "libraries_to_link.object_files",
152                                expand_if_equal = variable_with_value(
153                                    name = "libraries_to_link.type",
154                                    value = "object_file_group",
155                                ),
156                            ),
157                        ],
158                        expand_if_available = "libraries_to_link",
159                    ),
160                ],
161            ),
162            flag_set(
163                flag_groups = [
164                    flag_group(
165                        flags = ["@%{linker_param_file}"],
166                        expand_if_available = "linker_param_file",
167                    ),
168                ],
169            ),
170        ],
171        tools = [ar_tool],
172    )
173
174    action_configs = [
175        assemble_action,
176        c_compile_action,
177        cpp_compile_action,
178        cpp_link_dynamic_library_action,
179        cpp_link_executable_action,
180        cpp_link_nodeps_dynamic_library_action,
181        cpp_link_static_library_action,
182        linkstamp_compile_action,
183        preprocess_assemble_action,
184    ]
185    return action_configs
186
187def _make_default_flags():
188    """Here we define the flags for certain actions that are always applied."""
189    cxx_compile_includes = flag_set(
190        actions = [
191            ACTION_NAMES.c_compile,
192            ACTION_NAMES.cpp_compile,
193        ],
194        flag_groups = [
195            flag_group(
196                flags = [
197                    # THIS ORDER MATTERS GREATLY. If these are in the wrong order, the
198                    # #include_next directives will walk off the end of the specified system
199                    # folders here and look in the absolute path that happens to contain
200                    # the clang executable. Because that looks like an absolute path to
201                    # Bazel, it will declare the Build is not properly specified (that is,
202                    # it appears to use headers outside of Bazel's control/view) and fail.
203                    "-isystem",
204                    EXTERNAL_TOOLCHAIN + "/include/c++/v1",
205                    "-isystem",
206                    EXTERNAL_TOOLCHAIN + "/lib/clang/13.0.0/include",
207                    "-isystem",
208                    EXTERNAL_TOOLCHAIN + "/usr/include/x86_64-linux-musl",
209                ],
210            ),
211        ],
212    )
213
214    cpp_compile_includes = flag_set(
215        actions = [
216            ACTION_NAMES.cpp_compile,
217        ],
218        flag_groups = [
219            flag_group(
220                flags = [
221                    # http://g/skia-staff/bhPPBV4YdeU/5oyG5GRgBQAJ
222                    "-std=c++14",
223                    "-Wno-c++17-extensions",
224                    "-Wno-psabi",  # noisy
225                    # This define allows libc++ to work with musl. They were discovered by
226                    # trying to compile without them, reading errors and source code, e.g.
227                    # https://github.com/llvm/llvm-project/blob/f4c1258d5633fcf06385ff3fd1f4bf57ab971964/libcxx/include/__locale#L513
228                    # https://github.com/llvm/llvm-project/blob/f4c1258d5633fcf06385ff3fd1f4bf57ab971964/libcxx/include/__config#L1155
229                    # https://github.com/llvm/llvm-project/blob/f4c1258d5633fcf06385ff3fd1f4bf57ab971964/libcxx/include/__support/musl/xlocale.h
230                    "-D_LIBCPP_HAS_MUSL_LIBC",
231                ],
232            ),
233        ],
234    )
235
236    link_exe_flags = flag_set(
237        actions = [ACTION_NAMES.cpp_link_executable],
238        flag_groups = [
239            flag_group(
240                flags = [
241                    "-fuse-ld=lld",
242                    # This is the path to libc.a and other libraries.
243                    "-L" + EXTERNAL_TOOLCHAIN + "/usr/lib/x86_64-linux-musl",
244                    "-L" + EXTERNAL_TOOLCHAIN + "/lib",
245                    # We chose to use the llvm runtime, not the gcc one because it is already
246                    # included in the clang binary
247                    "--rtlib=compiler-rt",
248                    # In order to run our executables, they need to be statically linked,
249                    # otherwise, the libc++.so and friends will not be found if they are
250                    # not installed on the user's machine.
251                    "-static",
252                    "-std=c++14",
253                    "-lc++abi",
254                    "-lunwind",
255                    "-lc++",
256                ],
257            ),
258        ],
259    )
260    return [feature(
261        "default_flags",
262        enabled = True,
263        flag_sets = [
264            cxx_compile_includes,
265            cpp_compile_includes,
266            link_exe_flags,
267        ],
268    )]
269
270def _make_diagnostic_flags():
271    """Here we define the flags that can be turned on via features to yield debug info."""
272    cxx_diagnostic = flag_set(
273        actions = [
274            ACTION_NAMES.c_compile,
275            ACTION_NAMES.cpp_compile,
276        ],
277        flag_groups = [
278            flag_group(
279                flags = [
280                    "--trace-includes",
281                    "-v",
282                ],
283            ),
284        ],
285    )
286
287    link_diagnostic = flag_set(
288        actions = [ACTION_NAMES.cpp_link_executable],
289        flag_groups = [
290            flag_group(
291                flags = [
292                    "-Wl,--verbose",
293                    "-v",
294                ],
295            ),
296        ],
297    )
298
299    link_search_dirs = flag_set(
300        actions = [ACTION_NAMES.cpp_link_executable],
301        flag_groups = [
302            flag_group(
303                flags = [
304                    "--print-search-dirs",
305                ],
306            ),
307        ],
308    )
309    return [
310        # Running a Bazel command with --features diagnostic will cause the compilation and
311        # link steps to be more verbose.
312        feature(
313            "diagnostic",
314            enabled = False,
315            flag_sets = [
316                cxx_diagnostic,
317                link_diagnostic,
318            ],
319        ),
320        # Running a Bazel command with --features print_search_dirs will cause the link to fail
321        # but directories searched for libraries, etc will be displayed.
322        feature(
323            "print_search_dirs",
324            enabled = False,
325            flag_sets = [
326                link_search_dirs,
327            ],
328        ),
329    ]
330