skia2/toolchain/clang_toolchain_config.bzl
Kevin Lubick 7a14f783bd [bazel] Sketching out HelloWorld sk_app using GL
bazel run //example:hello_world --config=clang
causes a window to open and draws a circle and a square.
Text to follow in a future CL.

To make this work, I had to get rid of musl and use glibc.
All the shared libraries (.so files) that were pre-built
and available for download (e.g. from https://packages.debian.org/bullseye/amd64/libgl1/download)
were compiled against glibc. When I tried to run a
program statically linked with musl and dynamically linked
against things using glibc, I got a segmentation fault
on things like calloc().

Initial attempts to use glibc had failed because it was thought
that the libc.so.6 file could only be referred to by absolute
path (and thus Bazel would not be happy about it). As it turns out,
that was simply a misconfiguration of the builtin_sysroot
parameter to cc_common.create_cc_toolchain_config_info
(see //toolchain/clang_toolchain_config.bzl). By setting that
to `external/clang_linux_amd64` and not
`external/clang_linux_amd64/usr`, the libc binary which had
been extracted to `external/clang_linux_amd64/lib/x86_64-linux-gnu`
was perfectly reachable from
`external/clang_linux_amd64/usr/usr/lib/x86_64-linux-gnu/libc.so`

To bring in the shared libraries to link against (e.g. X11, GL)
I made build_toolchain.bzl easier to modify in that we simply need
to add a debian download url and sha256 hash to a list (rather than
having to plumb this through via arguments).

Recommended Review Order:
 - example/BUILD.bazel (not sure if we always want to set bare
   link arguments like that or if we want to use "features" to
   pass those along to the toolchain).
 - tools/sk_app/BUILD.bazel to see initial cc_library for
   wrapping sk_app code.
 - toolchain/build_toolchain.bzl to see removal of musl and
   new list of debs.
 - toolchain/clang_toolchain_config.bzl (where use of the
   no-canonical-prefixes was key to compilation success).
   Notice also that we statically linked libc++ (I did not
   have any shared libraries for it locally, so I guessed
   a typical developer might not either).
 - Rest of toolchain/ for trivial renames.
 - bazel/Makefile to see extra docs on those targets and
   a new target that compiles all the exes so far for a
   quick way to test the build.
 - third_party/BUILD.bazel and src/gpu/BUILD.bazel which have
   non-generated changes. (all other BUILD.bazel files do).
 - go.mod, which needed to update the infra repo version in
   order to pick up http://review.skia.org/491736).

Change-Id: I8687bd227353040eca2dffa9465798d8bd395027
Bug: skia:12541
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/492117
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
Commit-Queue: Kevin Lubick <kjlubick@google.com>
2022-01-11 13:06:19 +00:00

334 lines
13 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"
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,
# This is important because the linker will complain if the libc shared libraries are not
# under this directory. Because we extract the libc libraries to
# EXTERNAL_TOOLCHAIN/lib, and the various headers and shared libraries to
# EXTERNAL_TOOLCHAIN/usr, we make the top level folder the sysroot so the linker can
# find the referenced libraries (e.g. EXTERNAL_TOOLCHAIN/usr/lib/x86_64-linux-gnu/libc.so
# is just a text file that refers to "/lib/x86_64-linux-gnu/libc.so.6" and
# "/lib64/ld-linux-x86-64.so.2" which will use the sysroot as the root).
builtin_sysroot = EXTERNAL_TOOLCHAIN,
compiler = "clang",
host_system_name = "local",
target_cpu = "k8",
# It is unclear if target_libc matters.
target_libc = "glibc-2.31",
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 fail to find the files, causing a compilation
# error (or, without -no-canonical-prefixes, a mysterious case where files
# are included with an absolute path and fail the build).
"-isystem",
EXTERNAL_TOOLCHAIN + "/include/c++/v1",
"-isystem",
EXTERNAL_TOOLCHAIN + "/usr/include",
"-isystem",
EXTERNAL_TOOLCHAIN + "/lib/clang/13.0.0/include",
"-isystem",
EXTERNAL_TOOLCHAIN + "/usr/include/x86_64-linux-gnu",
# We do not want clang to search in absolute paths for files. This makes
# Bazel think we are using an outside resource and fail the compile.
"-no-canonical-prefixes",
],
),
],
)
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
],
),
],
)
link_exe_flags = flag_set(
actions = [ACTION_NAMES.cpp_link_executable],
flag_groups = [
flag_group(
flags = [
"-fuse-ld=lld",
# We chose to use the llvm runtime, not the gcc one because it is already
# included in the clang binary
"--rtlib=compiler-rt",
"-std=c++14",
# We statically include these libc++ libraries so they do not need to be
# on a developer's machine (they can be tricky to get).
EXTERNAL_TOOLCHAIN + "/lib/libc++.a",
EXTERNAL_TOOLCHAIN + "/lib/libc++abi.a",
EXTERNAL_TOOLCHAIN + "/lib/libunwind.a",
# Dynamically Link in the other parts of glibc (not needed in glibc 2.34+)
"-lpthread",
"-lm",
"-ldl",
],
),
],
)
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,
],
),
]