skia2/toolchain/clang_toolchain_config.bzl
Kevin Lubick 1f8c31b101 [infra] Add initial Bazel rules and files
These rules can be used to build our GMs on WASM+WebGL and
libskia.a with just the CPU backend (and most other features
turned off).

This can be done with the following commands:
  - bazel build //modules/canvaskit:gm-bindings-wasm --gpu_backend=gl_backend --with_gl_standard=webgl_standard
  - bazel build :skia-core --config clang

This pivots slightly from http://review.skia.org/463517
by using config_settings [1] instead of platforms for
the optional features that we control. This pivot was
suggested in [2]

We have BUILD.bazel files in many of the subdirectories
that specify filegroups for the appropriate files. In
an effort to make //BUILD.bazel more readable, it is
the responsibility of these subfolders to deal with
conditionally including certain .h or .cpp files.
This is done using select statements and config_settings
or platform constraints as necessary.

For example, src/gpu/BUILD.bazel will different private
filegroups for each of the supported gpu backends [3]
and a more-visible filegroup called "srcs" that has
the right selection of the private files to be used
for compilation.

An effort has been made to avoid using glob() in our
BUILD.bazel files. These file lists were made by using
`ls -1` and some regex to add in quotes. We might want
to make a helper script to assist with that, if necessary.

To specify which options we have, the settings in
//bazel/common_config_settings/BUILD.bazel have been
redesigned. They make use of a macro `string_flag_with_values`
that removes the boilerplate. Patchset 36 shows what the
file looks like w/o the macro.

The top level BUILD.bazel file will still need to use
some logic to handle defines, because local_defines is
a list of strings, not a list of labels [4].

Suggested Review Order:
  - WORKSPACE.bazel to see the new dependencies on the
    emsdk toolchain and bazel_skylib
  - bazel/common_config_settings/* to see the few settings
    defined (we have more to define, see BUILD.gn and
    //gn/skia.gni for ideas)
  - BUILD.bazel to see the "skia-core" cc_library rule.
    See also "gms" and "tests"
  - modules/canvaskit/BUILD.bazel to see the use of
    the emscripten "wasm_cc_binary" rule, which depends
    on the "skia-core", "gms", and "tests" rule. Note that
    it only builds some of the gms as a proof of concept.
  - The other BUILD.bazel files. Some of these are not
    platform or feature dependent (e.g. pathops). Others
    are (e.g. gpu).
  - All other files.

[1] https://docs.bazel.build/versions/4.2.1/skylark/config.html#user-defined-build-settings
[2] https://github.com/emscripten-core/emsdk/pull/920
[3] In this CL, that's just the webgl one.
[4] https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.local_defines

Change-Id: Ieecf9c106d5e3a6ae97d13d66be06b4b3c207089
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/458637
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
Owners-Override: Kevin Lubick <kjlubick@google.com>
2021-11-09 12:32:25 +00:00

330 lines
12 KiB
Python

"""
This file specifies a clang toolchain that can run on a Linux host which does depend on any
installed packages from the host machine.
See build_toolchain.bzl for more details on the creation of the toolchain.
It uses the usr subfolder of the built toolchain as a sysroot
It follows the example of:
- https://docs.bazel.build/versions/4.2.1/tutorial/cc-toolchain-config.html
- https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl
"""
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
"action_config",
"feature",
"flag_group",
"flag_set",
"tool",
"variable_with_value",
)
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
# The location of the created clang toolchain.
EXTERNAL_TOOLCHAIN = "external/clang_linux_amd64_musl"
def _clang_impl(ctx):
action_configs = _make_action_configs()
features = []
features += _make_default_flags()
features += _make_diagnostic_flags()
# https://docs.bazel.build/versions/main/skylark/lib/cc_common.html#create_cc_toolchain_config_info
# Note, this rule is defined in Java code, not Starlark
# https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java
return cc_common.create_cc_toolchain_config_info(
ctx = ctx,
features = features,
abi_libc_version = "unknown",
abi_version = "unknown",
action_configs = action_configs,
builtin_sysroot = EXTERNAL_TOOLCHAIN + "/usr",
compiler = "clang",
host_system_name = "local",
target_cpu = "k8",
target_libc = "musl",
target_system_name = "local",
toolchain_identifier = "clang-toolchain",
)
provide_clang_toolchain_config = rule(
attrs = {},
provides = [CcToolchainConfigInfo],
implementation = _clang_impl,
)
def _make_action_configs():
"""
This function sets up the tools needed to perform the various compile/link actions.
Bazel normally restricts us to referring to (and therefore running) executables/scripts
that are in this directory (That is EXEC_ROOT/toolchain). However, the executables we want
to run are brought in via WORKSPACE.bazel and are located in EXEC_ROOT/external/clang....
Therefore, we make use of "trampoline scripts" that will call the binaries from the
toolchain directory.
These action_configs also let us dynamically specify arguments from the Bazel
environment if necessary (see cpp_link_static_library_action).
"""
# https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=435;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
clang_tool = tool(path = "clang_trampoline.sh")
lld_tool = tool(path = "lld_trampoline.sh")
ar_tool = tool(path = "ar_trampoline.sh")
# https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=488;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
assemble_action = action_config(
action_name = ACTION_NAMES.assemble,
tools = [clang_tool],
)
c_compile_action = action_config(
action_name = ACTION_NAMES.c_compile,
tools = [clang_tool],
)
cpp_compile_action = action_config(
action_name = ACTION_NAMES.cpp_compile,
tools = [clang_tool],
)
linkstamp_compile_action = action_config(
action_name = ACTION_NAMES.linkstamp_compile,
tools = [clang_tool],
)
preprocess_assemble_action = action_config(
action_name = ACTION_NAMES.preprocess_assemble,
tools = [clang_tool],
)
cpp_link_dynamic_library_action = action_config(
action_name = ACTION_NAMES.cpp_link_dynamic_library,
tools = [lld_tool],
)
cpp_link_executable_action = action_config(
action_name = ACTION_NAMES.cpp_link_executable,
# Bazel assumes it is talking to clang when building an executable. There are
# "-Wl" flags on the command: https://releases.llvm.org/6.0.1/tools/clang/docs/ClangCommandLineReference.html#cmdoption-clang-Wl
tools = [clang_tool],
)
cpp_link_nodeps_dynamic_library_action = action_config(
action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library,
tools = [lld_tool],
)
# This is the same rule as
# https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl#L143
# By default, there are no flags or libraries passed to the llvm-ar tool, so
# we need to specify them. The variables mentioned by expand_if_available are defined
# https://docs.bazel.build/versions/main/cc-toolchain-config-reference.html#cctoolchainconfiginfo-build-variables
cpp_link_static_library_action = action_config(
action_name = ACTION_NAMES.cpp_link_static_library,
flag_sets = [
flag_set(
flag_groups = [
flag_group(
# https://llvm.org/docs/CommandGuide/llvm-ar.html
# replace existing files or insert them if they already exist,
# create the file if it doesn't already exist
# symbol table should be added
# Deterministic timestamps should be used
flags = ["rcsD", "%{output_execpath}"],
# Despite the name, output_execpath just refers to linker output,
# e.g. libFoo.a
expand_if_available = "output_execpath",
),
],
),
flag_set(
flag_groups = [
flag_group(
iterate_over = "libraries_to_link",
flag_groups = [
flag_group(
flags = ["%{libraries_to_link.name}"],
expand_if_equal = variable_with_value(
name = "libraries_to_link.type",
value = "object_file",
),
),
flag_group(
flags = ["%{libraries_to_link.object_files}"],
iterate_over = "libraries_to_link.object_files",
expand_if_equal = variable_with_value(
name = "libraries_to_link.type",
value = "object_file_group",
),
),
],
expand_if_available = "libraries_to_link",
),
],
),
flag_set(
flag_groups = [
flag_group(
flags = ["@%{linker_param_file}"],
expand_if_available = "linker_param_file",
),
],
),
],
tools = [ar_tool],
)
action_configs = [
assemble_action,
c_compile_action,
cpp_compile_action,
cpp_link_dynamic_library_action,
cpp_link_executable_action,
cpp_link_nodeps_dynamic_library_action,
cpp_link_static_library_action,
linkstamp_compile_action,
preprocess_assemble_action,
]
return action_configs
def _make_default_flags():
"""Here we define the flags for certain actions that are always applied."""
cxx_compile_includes = flag_set(
actions = [
ACTION_NAMES.c_compile,
ACTION_NAMES.cpp_compile,
],
flag_groups = [
flag_group(
flags = [
# THIS ORDER MATTERS GREATLY. If these are in the wrong order, the
# #include_next directives will walk off the end of the specified system
# folders here and look in the absolute path that happens to contain
# the clang executable. Because that looks like an absolute path to
# Bazel, it will declare the Build is not properly specified (that is,
# it appears to use headers outside of Bazel's control/view) and fail.
"-isystem",
EXTERNAL_TOOLCHAIN + "/include/c++/v1",
"-isystem",
EXTERNAL_TOOLCHAIN + "/lib/clang/13.0.0/include",
"-isystem",
EXTERNAL_TOOLCHAIN + "/usr/include/x86_64-linux-musl",
],
),
],
)
cpp_compile_includes = flag_set(
actions = [
ACTION_NAMES.cpp_compile,
],
flag_groups = [
flag_group(
flags = [
# http://g/skia-staff/bhPPBV4YdeU/5oyG5GRgBQAJ
"-std=c++14",
"-Wno-c++17-extensions",
"-Wno-psabi", # noisy
# This define allows libc++ to work with musl. They were discovered by
# trying to compile without them, reading errors and source code, e.g.
# https://github.com/llvm/llvm-project/blob/f4c1258d5633fcf06385ff3fd1f4bf57ab971964/libcxx/include/__locale#L513
# https://github.com/llvm/llvm-project/blob/f4c1258d5633fcf06385ff3fd1f4bf57ab971964/libcxx/include/__config#L1155
# https://github.com/llvm/llvm-project/blob/f4c1258d5633fcf06385ff3fd1f4bf57ab971964/libcxx/include/__support/musl/xlocale.h
"-D_LIBCPP_HAS_MUSL_LIBC",
],
),
],
)
link_exe_flags = flag_set(
actions = [ACTION_NAMES.cpp_link_executable],
flag_groups = [
flag_group(
flags = [
"-fuse-ld=lld",
# This is the path to libc.a and other libraries.
"-L" + EXTERNAL_TOOLCHAIN + "/usr/lib/x86_64-linux-musl",
"-L" + EXTERNAL_TOOLCHAIN + "/lib",
# We chose to use the llvm runtime, not the gcc one because it is already
# included in the clang binary
"--rtlib=compiler-rt",
# In order to run our executables, they need to be statically linked,
# otherwise, the libc++.so and friends will not be found if they are
# not installed on the user's machine.
"-static",
"-std=c++14",
"-lc++abi",
"-lunwind",
"-lc++",
],
),
],
)
return [feature(
"default_flags",
enabled = True,
flag_sets = [
cxx_compile_includes,
cpp_compile_includes,
link_exe_flags,
],
)]
def _make_diagnostic_flags():
"""Here we define the flags that can be turned on via features to yield debug info."""
cxx_diagnostic = flag_set(
actions = [
ACTION_NAMES.c_compile,
ACTION_NAMES.cpp_compile,
],
flag_groups = [
flag_group(
flags = [
"--trace-includes",
"-v",
],
),
],
)
link_diagnostic = flag_set(
actions = [ACTION_NAMES.cpp_link_executable],
flag_groups = [
flag_group(
flags = [
"-Wl,--verbose",
"-v",
],
),
],
)
link_search_dirs = flag_set(
actions = [ACTION_NAMES.cpp_link_executable],
flag_groups = [
flag_group(
flags = [
"--print-search-dirs",
],
),
],
)
return [
# Running a Bazel command with --features diagnostic will cause the compilation and
# link steps to be more verbose.
feature(
"diagnostic",
enabled = False,
flag_sets = [
cxx_diagnostic,
link_diagnostic,
],
),
# Running a Bazel command with --features print_search_dirs will cause the link to fail
# but directories searched for libraries, etc will be displayed.
feature(
"print_search_dirs",
enabled = False,
flag_sets = [
link_search_dirs,
],
),
]