xref: /aosp_15_r20/external/skia/toolchain/linux_amd64_toolchain_config.bzl (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1"""
2This file specifies a clang toolchain that can run on a Linux host which doesn't depend on any
3installed packages from the host machine.
4
5See download_linux_amd64_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
14# https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
15load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
16
17# https://github.com/bazelbuild/bazel/blob/master/tools/cpp/cc_toolchain_config_lib.bzl
18load(
19    "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
20    "action_config",
21    "feature",
22    "flag_group",
23    "flag_set",
24    "tool",
25    "variable_with_value",
26)
27load(":clang_layering_check.bzl", "make_layering_check_features")
28
29# The location of the created clang toolchain.
30EXTERNAL_TOOLCHAIN = "external/clang_linux_amd64"
31
32def _linux_amd64_toolchain_info(ctx):
33    action_configs = _make_action_configs()
34    features = []
35    features += _make_default_flags()
36    features += make_layering_check_features()
37    features += _make_diagnostic_flags()
38    features += _make_iwyu_flags()
39
40    # https://bazel.build/rules/lib/cc_common#create_cc_toolchain_config_info
41    # Note, this rule is defined in Java code, not Starlark
42    # https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java
43    return cc_common.create_cc_toolchain_config_info(
44        ctx = ctx,
45        features = features,
46        action_configs = action_configs,
47        # This is important because the linker will complain if the libc shared libraries are not
48        # under this directory. Because we extract the libc libraries to
49        # EXTERNAL_TOOLCHAIN/lib, and the various headers and shared libraries to
50        # EXTERNAL_TOOLCHAIN/usr, we make the top level folder the sysroot so the linker can
51        # find the referenced libraries (e.g. EXTERNAL_TOOLCHAIN/usr/lib/x86_64-linux-gnu/libc.so
52        # is just a text file that refers to "/lib/x86_64-linux-gnu/libc.so.6" and
53        # "/lib64/ld-linux-x86-64.so.2" which will use the sysroot as the root).
54        builtin_sysroot = EXTERNAL_TOOLCHAIN,
55        # These are required, but do nothing
56        compiler = "",
57        target_cpu = "",
58        target_libc = "",
59        target_system_name = "",
60        toolchain_identifier = "",
61    )
62
63provide_linux_amd64_toolchain_config = rule(
64    attrs = {},
65    provides = [CcToolchainConfigInfo],
66    implementation = _linux_amd64_toolchain_info,
67)
68
69def _make_action_configs():
70    """
71    This function sets up the tools needed to perform the various compile/link actions.
72
73    Bazel normally restricts us to referring to (and therefore running) executables/scripts
74    that are in this directory (That is EXEC_ROOT/toolchain). However, the executables we want
75    to run are brought in via WORKSPACE.bazel and are located in EXEC_ROOT/external/clang....
76    Therefore, we make use of "trampoline scripts" that will call the binaries from the
77    toolchain directory.
78
79    These action_configs also let us dynamically specify arguments from the Bazel
80    environment if necessary (see cpp_link_static_library_action).
81    """
82
83    # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=435;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
84    clang_tool = tool(path = "linux_trampolines/clang_trampoline_linux.sh")
85    ar_tool = tool(path = "linux_trampolines/ar_trampoline_linux.sh")
86
87    # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=488;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
88    assemble_action = action_config(
89        action_name = ACTION_NAMES.assemble,
90        tools = [clang_tool],
91    )
92    c_compile_action = action_config(
93        action_name = ACTION_NAMES.c_compile,
94        tools = [clang_tool],
95    )
96    cpp_compile_action = action_config(
97        action_name = ACTION_NAMES.cpp_compile,
98        tools = [clang_tool],
99    )
100    linkstamp_compile_action = action_config(
101        action_name = ACTION_NAMES.linkstamp_compile,
102        tools = [clang_tool],
103    )
104    preprocess_assemble_action = action_config(
105        action_name = ACTION_NAMES.preprocess_assemble,
106        tools = [clang_tool],
107    )
108
109    cpp_link_dynamic_library_action = action_config(
110        action_name = ACTION_NAMES.cpp_link_dynamic_library,
111        tools = [clang_tool],
112    )
113    cpp_link_executable_action = action_config(
114        action_name = ACTION_NAMES.cpp_link_executable,
115        # Bazel assumes it is talking to clang when building an executable. There are
116        # "-Wl" flags on the command: https://releases.llvm.org/6.0.1/tools/clang/docs/ClangCommandLineReference.html#cmdoption-clang-Wl
117        tools = [clang_tool],
118    )
119    cpp_link_nodeps_dynamic_library_action = action_config(
120        action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library,
121        tools = [clang_tool],
122    )
123
124    # This is the same rule as
125    # https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl#L143
126    # By default, there are no flags or libraries passed to the llvm-ar tool, so
127    # we need to specify them. The variables mentioned by expand_if_available are defined
128    # https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables
129    cpp_link_static_library_action = action_config(
130        action_name = ACTION_NAMES.cpp_link_static_library,
131        flag_sets = [
132            flag_set(
133                flag_groups = [
134                    flag_group(
135                        # https://llvm.org/docs/CommandGuide/llvm-ar.html
136                        # replace existing files or insert them if they already exist,
137                        # create the file if it doesn't already exist
138                        # symbol table should be added
139                        # Deterministic timestamps should be used
140                        flags = ["rcsD", "%{output_execpath}"],
141                        # Despite the name, output_execpath just refers to linker output,
142                        # e.g. libFoo.a
143                        expand_if_available = "output_execpath",
144                    ),
145                ],
146            ),
147            flag_set(
148                flag_groups = [
149                    flag_group(
150                        iterate_over = "libraries_to_link",
151                        flag_groups = [
152                            flag_group(
153                                flags = ["%{libraries_to_link.name}"],
154                                expand_if_equal = variable_with_value(
155                                    name = "libraries_to_link.type",
156                                    value = "object_file",
157                                ),
158                            ),
159                            flag_group(
160                                flags = ["%{libraries_to_link.object_files}"],
161                                iterate_over = "libraries_to_link.object_files",
162                                expand_if_equal = variable_with_value(
163                                    name = "libraries_to_link.type",
164                                    value = "object_file_group",
165                                ),
166                            ),
167                        ],
168                        expand_if_available = "libraries_to_link",
169                    ),
170                ],
171            ),
172            flag_set(
173                flag_groups = [
174                    flag_group(
175                        flags = ["@%{linker_param_file}"],
176                        expand_if_available = "linker_param_file",
177                    ),
178                ],
179            ),
180        ],
181        tools = [ar_tool],
182    )
183
184    action_configs = [
185        assemble_action,
186        c_compile_action,
187        cpp_compile_action,
188        cpp_link_dynamic_library_action,
189        cpp_link_executable_action,
190        cpp_link_nodeps_dynamic_library_action,
191        cpp_link_static_library_action,
192        linkstamp_compile_action,
193        preprocess_assemble_action,
194    ]
195    return action_configs
196
197def _make_default_flags():
198    """Here we define the flags for certain actions that are always applied.
199
200    For any flag that might be conditionally applied, it should be defined in //bazel/copts.bzl.
201
202    Flags that are set here will be unconditionally applied to everything we compile with
203    this toolchain, even third_party deps.
204    """
205
206    # Note: These values must be kept in sync with those defined in cmake_exporter.go.
207    cxx_compile_includes = flag_set(
208        actions = [
209            ACTION_NAMES.c_compile,
210            ACTION_NAMES.cpp_compile,
211        ],
212        flag_groups = [
213            flag_group(
214                flags = [
215                    # THIS ORDER MATTERS GREATLY. If these are in the wrong order, the
216                    # #include_next directives will fail to find the files, causing a compilation
217                    # error (or, without -no-canonical-prefixes, a mysterious case where files
218                    # are included with an absolute path and fail the build).
219                    "-isystem",
220                    EXTERNAL_TOOLCHAIN + "/include/c++/v1",
221                    # https://github.com/llvm/llvm-project/issues/57104
222                    "-isystem",
223                    EXTERNAL_TOOLCHAIN + "/include/x86_64-unknown-linux-gnu/c++/v1/",
224                    "-isystem",
225                    EXTERNAL_TOOLCHAIN + "/usr/include",
226                    "-isystem",
227                    EXTERNAL_TOOLCHAIN + "/lib/clang/15.0.1/include",
228                    "-isystem",
229                    EXTERNAL_TOOLCHAIN + "/usr/include/x86_64-linux-gnu",
230                    # We do not want clang to search in absolute paths for files. This makes
231                    # Bazel think we are using an outside resource and fail the compile.
232                    "-no-canonical-prefixes",
233                ],
234            ),
235        ],
236    )
237
238    cpp_compile_flags = flag_set(
239        actions = [
240            ACTION_NAMES.cpp_compile,
241        ],
242        flag_groups = [
243            flag_group(
244                flags = [
245                    "-std=c++17",
246                    "-stdlib=libc++",
247                ],
248            ),
249        ],
250    )
251
252    link_exe_flags = flag_set(
253        actions = [
254            ACTION_NAMES.cpp_link_executable,
255            ACTION_NAMES.cpp_link_dynamic_library,
256            ACTION_NAMES.cpp_link_nodeps_dynamic_library,
257        ],
258        flag_groups = [
259            flag_group(
260                flags = [
261                    "-fuse-ld=lld",
262                    # We chose to use the llvm runtime, not the gcc one because it is already
263                    # included in the clang binary
264                    "--rtlib=compiler-rt",
265                    "-std=c++17",
266                    "-stdlib=libc++",
267                    # We statically include these libc++ libraries so they do not need to be
268                    # on a developer's machine (they can be tricky to get).
269                    EXTERNAL_TOOLCHAIN + "/lib/x86_64-unknown-linux-gnu/libc++.a",
270                    EXTERNAL_TOOLCHAIN + "/lib/x86_64-unknown-linux-gnu/libc++abi.a",
271                    EXTERNAL_TOOLCHAIN + "/lib/x86_64-unknown-linux-gnu/libunwind.a",
272                    # Dynamically Link in the other parts of glibc (not needed in glibc 2.34+)
273                    "-lpthread",
274                    "-lm",
275                    "-ldl",
276                ],
277            ),
278        ],
279    )
280    return [feature(
281        "default_flags",
282        enabled = True,
283        flag_sets = [
284            cxx_compile_includes,
285            cpp_compile_flags,
286            link_exe_flags,
287        ],
288    )]
289
290def _make_diagnostic_flags():
291    """Here we define the flags that can be turned on via features to yield debug info."""
292    cxx_diagnostic = flag_set(
293        actions = [
294            ACTION_NAMES.c_compile,
295            ACTION_NAMES.cpp_compile,
296        ],
297        flag_groups = [
298            flag_group(
299                flags = [
300                    "--trace-includes",
301                    "-v",
302                ],
303            ),
304        ],
305    )
306
307    link_diagnostic = flag_set(
308        actions = [ACTION_NAMES.cpp_link_executable],
309        flag_groups = [
310            flag_group(
311                flags = [
312                    "-Wl,--verbose",
313                    "-v",
314                ],
315            ),
316        ],
317    )
318
319    link_search_dirs = flag_set(
320        actions = [ACTION_NAMES.cpp_link_executable],
321        flag_groups = [
322            flag_group(
323                flags = [
324                    "--print-search-dirs",
325                ],
326            ),
327        ],
328    )
329    return [
330        # Running a Bazel command with --features diagnostic will cause the compilation and
331        # link steps to be more verbose.
332        feature(
333            "diagnostic",
334            enabled = False,
335            flag_sets = [
336                cxx_diagnostic,
337                link_diagnostic,
338            ],
339        ),
340        feature(
341            "diagnostic_link",
342            enabled = False,
343            flag_sets = [
344                link_diagnostic,
345            ],
346        ),
347        # Running a Bazel command with --features print_search_dirs will cause the link to fail
348        # but directories searched for libraries, etc will be displayed.
349        feature(
350            "print_search_dirs",
351            enabled = False,
352            flag_sets = [
353                link_search_dirs,
354            ],
355        ),
356    ]
357
358def _make_iwyu_flags():
359    """Here we define the flags that signal whether or not to enforce IWYU."""
360
361    # https://bazel.build/docs/cc-toolchain-config-reference#features
362    opt_file_into_iwyu = flag_set(
363        actions = [
364            ACTION_NAMES.c_compile,
365            ACTION_NAMES.cpp_compile,
366        ],
367        flag_groups = [
368            flag_group(
369                flags = [
370                    # This define does not impact compilation, but it acts as a signal to the
371                    # clang_trampoline.sh whether to maybe check the file with include-what-you-use
372                    # A define was chosen because it is ignored by clang and IWYU, but can be
373                    # easily found with bash.
374                    # The clang_trampoline.sh file has a list of allowed subdirectories for which
375                    # IWYU should be enforced, allowing us to slowly opt more and more directories
376                    # in over time.
377                    "-DSKIA_ENFORCE_IWYU",
378                ],
379            ),
380        ],
381    )
382
383    return [
384        feature(
385            "skia_enforce_iwyu",
386            enabled = False,
387            flag_sets = [
388                opt_file_into_iwyu,
389            ],
390        ),
391    ]
392