set up tools for building Skia on Mac semi hermetically

Reviewer notes:
PS1 and PS2 handle everything up to the linking stage of the build
PS6 and PS7 are trivial renamings and rebases, diff between Base->PS5 for a cleaner review

No-Try: true
Change-Id: Ib21ce2e8839ecd4b4dd57280e82f56a98194e476
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/532765
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Jorge Betancourt <jmbetancourt@google.com>
This commit is contained in:
Jorge Betancourt 2022-05-04 11:06:39 -04:00 committed by SkCQ
parent ec6cc1f422
commit b894c69abb
15 changed files with 494 additions and 26 deletions

View File

@ -1,13 +1,10 @@
# All of our clang builds should use the hermetic toolchain
build:clang --crosstool_top=//toolchain:clang_suite
# We don't have a great way to test the host system in order to use the right
# version of the toolchain. So we need to have the user specify what their host is.
build:clang_linux --config=clang
build:clang_linux --crosstool_top=//toolchain:clang_suite_linux
build:clang_linux --compiler=host_is_linux_amd64
build:clang_mac --config=clang
build:clang_mac --compiler=host_is_mac_M1
build:clang_mac --crosstool_top=//toolchain:clang_suite_mac
build:clang_mac --compiler=host_is_mac_m1
# =============================================================================
# Alias to build configurations below. This makes configuring things from
@ -95,7 +92,7 @@ build:remote --remote_instance_name projects/skia-rbe/instances/default_instance
# //bazel/rbe/BUILD.bazel.
build:linux-rbe --config=remote
# Use our hermetic toolchain instead of the clang in the toolchain.
build:linux-rbe --crosstool_top=//toolchain:clang_suite
build:linux-rbe --crosstool_top=//toolchain:clang_suite_linux
# We want to run on this RBE platform
build:linux-rbe --extra_execution_platforms=//bazel/rbe:gce_linux_platform
# On the RBE instances, this Java and C++ toolchain are available

View File

@ -52,6 +52,19 @@ If you want to pass one or more flags to `bazel run`, add them on the end after
bazel run //example:hello_world_gl --config=clang_linux -- --flag_one=apple --flag_two=cherry
```
### Mac Hosts (you are running Bazel on a Mac machine)
You can run a command like:
```
bazel build //example:bazel_test_exe --config=clang_mac
```
Similar to the Linux guide, this uses the `clang_mac` configuration (defined in `//.bazelrc`).
When building for Mac, we require the user to have Xcode installed on their device so that we can
use system headers and Mac-specific includes when compiling. Our Bazel toolchain assumes you have
`xcode-select` in your path so that we may symlink the user's current Xcode directory in the
toolchain's cache. Make sure `xcode-select -p` returns a valid path.
## .bazelrc Tips
You should make a [.bazelrc file](https://bazel.build/docs/bazelrc) in your home directory where
you can specify settings that apply only to you. These can augment or replace the ones we define

View File

@ -1,6 +1,7 @@
load("//bazel:macros.bzl", "exports_files_legacy")
load("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite")
load(":linux_amd64_toolchain_config.bzl", "provide_linux_amd64_toolchain_config")
load(":mac_m1_toolchain_config.bzl", "provide_mac_m1_toolchain_config")
licenses(["notice"])
@ -9,8 +10,11 @@ exports_files_legacy()
package(default_visibility = ["//visibility:public"])
# https://bazel.build/reference/be/c-cpp#cc_toolchain_suite
# cc_toolchain_suite will fetch deps for toolchains it will not use, which
# is why we split up the suites by OS. When attempting to fetch "@clang_linux_amd64//:all_files",
# a script compiled for linux to extract ar archives will break on other platforms.
cc_toolchain_suite(
name = "clang_suite",
name = "clang_suite_linux",
toolchains = {
# The key is target_cpu|compiler
# compiler appears to be a string we can choose arbitrarily
@ -18,8 +22,15 @@ cc_toolchain_suite(
"k8|host_is_linux_amd64": ":linux_amd64_host",
"x86_64|host_is_linux_amd64": ":linux_amd64_host",
"k8": ":linux_amd64_host",
# TODO(jmbetancourt)
# "arm64|host_is_mac_M1": ":mac_M1_host",
},
)
cc_toolchain_suite(
name = "clang_suite_mac",
toolchains = {
"arm64|host_is_mac_m1": ":mac_m1_host",
"darwin|host_is_mac_m1": ":mac_m1_host",
"darwin_arm64|host_is_mac_m1": ":mac_m1_host",
},
)
@ -28,18 +39,32 @@ filegroup(name = "not_implemented")
filegroup(
name = "all_linux_amd64_files",
srcs = [
"IWYU_mapping.imp",
"ar_trampoline.sh",
"clang_trampoline.sh",
"lld_trampoline.sh",
"linux_trampolines/IWYU_mapping.imp",
"linux_trampolines/ar_trampoline_linux.sh",
"linux_trampolines/clang_trampoline_linux.sh",
"linux_trampolines/lld_trampoline_linux.sh",
"@clang_linux_amd64//:all_files",
],
)
filegroup(
name = "all_mac_m1_files",
srcs = [
"mac_trampolines/ar_trampoline_mac.sh",
"mac_trampolines/clang_trampoline_mac.sh",
"mac_trampolines/lld_trampoline_mac.sh",
"@clang_mac_m1//:all_files",
],
)
provide_linux_amd64_toolchain_config(
name = "linux_amd64_toolchain_config",
)
provide_mac_m1_toolchain_config(
name = "mac_m1_toolchain_config",
)
# https://bazel.build/reference/be/c-cpp#cc_toolchain
cc_toolchain(
name = "linux_amd64_host",
@ -53,3 +78,16 @@ cc_toolchain(
supports_param_files = False,
toolchain_config = ":linux_amd64_toolchain_config",
)
cc_toolchain(
name = "mac_m1_host",
all_files = ":all_mac_m1_files",
ar_files = ":all_mac_m1_files",
compiler_files = ":all_mac_m1_files",
dwp_files = ":not_implemented",
linker_files = ":all_mac_m1_files",
objcopy_files = ":not_implemented",
strip_files = ":not_implemented",
supports_param_files = False,
toolchain_config = ":mac_m1_toolchain_config",
)

View File

@ -4,7 +4,7 @@ This file assembles a toolchain for an amd64 Linux host using the Clang Compiler
It downloads the necessary headers, executables, and pre-compiled static/shared libraries to
the external subfolder of the Bazel cache (the same place third party deps are downloaded with
http_archive or similar functions in WORKSPACE.bazel). These will be able to be used via our
custom c++ toolchain configuration (see //toolchain/clang_toolchain_config.bzl)
custom c++ toolchain configuration (see //toolchain/linux_amd64_toolchain_config.bzl)
Most files are downloaded as .deb files from packages.debian.org (with us acting as the dependency
resolver) and extracted to

View File

@ -4,17 +4,76 @@ This file assembles a toolchain for a Mac M1 host using the Clang Compiler and g
It downloads the necessary headers, executables, and pre-compiled static/shared libraries to
the external subfolder of the Bazel cache (the same place third party deps are downloaded with
http_archive or similar functions in WORKSPACE.bazel). These will be able to be used via our
custom c++ toolchain configuration (see //toolchain/clang_toolchain_config.bzl)
custom c++ toolchain configuration (see //toolchain/mac_m1_toolchain_config.bzl)
Clang files are downloaded in tars while the rest of the required files are found in the user's local
Xcode directory (with us acting as the dependency resolver) and extracted (or symlinked) to
[outputRoot (aka Bazel cache)]/[outputUserRoot]/[outputBase]/external/clang_mac_m1
(See https://bazel.build/docs/output_directories#layout-diagram)
which will act as our sysroot.
"""
def _download_mac_m1_toolchain(ctx):
# TODO(jmbetancourt)
pass
# From https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-apple-darwin.tar.xz
clang_prefix = "clang+llvm-13.0.0-x86_64-apple-darwin"
clang_sha256 = "d051234eca1db1f5e4bc08c64937c879c7098900f7a0370f3ceb7544816a8b09"
clang_url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-apple-darwin.tar.xz"
def _download_mac_m1_toolchain_impl(ctx):
# Download the clang toolchain (the extraction can take a while)
# https://bazel.build/rules/lib/repository_ctx#download_and_extract
ctx.download_and_extract(
url = clang_url,
output = "",
stripPrefix = clang_prefix,
sha256 = clang_sha256,
)
# Some std library headers use #include_next to include system specific headers, and
# some skia source files require Xcode headers when compiling, (see SkTypes.h and look
# for TargetedConditionals.h)) All of these are located in Xcode, stopping the Mac
# builds from being purely hermetic.
# For now, we can grab the user's Xcode path by calling xcode-select and create a symlink in
# our toolchain directory to refer to during compilation.
# https://developer.apple.com/library/archive/technotes/tn2339/_index.html
res = ctx.execute(["xcode-select", "-p"])
# https://bazel.build/rules/lib/actions#symlink
ctx.symlink(
# from =
res.stdout.rstrip() + "/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr",
# to =
"./symlinks/xcode/MacSDK/usr",
)
# Create a BUILD.bazel file that makes all the files in this subfolder
# except for those in the share directory. They are not necessary for building
# and create a symlink looping error when resolving the filegroup.
# available for use in rules, i.e. in the toolchain declaration.
# https://bazel.build/rules/lib/repository_ctx#file
ctx.file(
"BUILD.bazel",
content = """
# DO NOT EDIT THIS BAZEL FILE DIRECTLY
# Generated from ctx.file action in download_mac_m1_toolchain.bzl
filegroup(
name = "all_files",
srcs = glob(
["**"],
exclude = [
"symlinks/xcode/MacSDK/usr/share/**"
]),
visibility = ["//visibility:public"]
)
""",
executable = False,
)
# https://bazel.build/rules/repository_rules
download_mac_m1_toolchain = repository_rule(
implementation = _download_mac_m1_toolchain,
implementation = _download_mac_m1_toolchain_impl,
attrs = {},
doc = "Downloads clang, and all supporting headers, executables, " +
"and shared libraries required to build Skia on a Mac M1 host",
"and shared libraries required to build Skia on a Mac M1 host." +
"Assumes you have xcode located on your device and have" +
"xcode-select in your $PATH.",
)

View File

@ -3,9 +3,9 @@ This file exports the various toolchains for the hosts that we support building
Supported:
- Linux amd64
- Mac M1
Planned:
- Mac M1
- Windows amd64
"""
@ -22,6 +22,14 @@ name_toolchain = {
}
def download_toolchains_for_skia(*args):
"""
Point Bazel to the correct rules for downloading the different toolchains.
Args:
*args: multiple toolchains, see top of file for
list of supported toolchains.
"""
for toolchain_name in args:
if toolchain_name not in name_toolchain:
fail("unrecognized toolchain name " + toolchain_name)

View File

@ -2,7 +2,7 @@
This file specifies a clang toolchain that can run on a Linux host which doesn't depend on any
installed packages from the host machine.
See build_toolchain.bzl for more details on the creation of the toolchain.
See download_linux_amd64_toolchain.bzl for more details on the creation of the toolchain.
It uses the usr subfolder of the built toolchain as a sysroot
@ -21,6 +21,8 @@ load(
"tool",
"variable_with_value",
)
# https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
# The location of the created clang toolchain.
@ -80,9 +82,9 @@ def _make_action_configs():
"""
# 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")
clang_tool = tool(path = "linux_trampolines/clang_trampoline_linux.sh")
lld_tool = tool(path = "linux_trampolines/lld_trampoline_linux.sh")
ar_tool = tool(path = "linux_trampolines/ar_trampoline_linux.sh")
# https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=488;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
assemble_action = action_config(

View File

@ -0,0 +1,331 @@
"""
This file specifies a clang toolchain that can run on a Mac host.
Hermetic toolchains still need access to Xcode for sys headers included in Skia's codebase.
See download_mac_m1_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:
- lunix_amd64_toolchain_config.bzl
"""
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
"action_config",
"feature",
"flag_group",
"flag_set",
"tool",
"variable_with_value",
)
# https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
# The location of the created clang toolchain.
EXTERNAL_TOOLCHAIN = "external/clang_mac_m1"
# Symlink location.
# Must be the same as where the symlink points to in download_mac_m1_toolchain.bzl
XCODE_SYMLINK = EXTERNAL_TOOLCHAIN + "/symlinks/xcode/MacSDK/usr"
def _mac_m1_toolchain_info(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,
compiler = "clang",
host_system_name = "local",
target_cpu = "m1",
target_system_name = "local",
# does this matter?
target_libc = "glibc-2.31",
toolchain_identifier = "clang-toolchain",
)
provide_mac_m1_toolchain_config = rule(
attrs = {},
provides = [CcToolchainConfigInfo],
implementation = _mac_m1_toolchain_info,
)
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 = "mac_trampolines/clang_trampoline_mac.sh")
lld_tool = tool(path = "mac_trampolines/lld_trampoline_mac.sh")
ar_tool = tool(path = "mac_trampolines/ar_trampoline_mac.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",
XCODE_SYMLINK + "/include",
"-isystem",
EXTERNAL_TOOLCHAIN + "/lib/clang/13.0.0/include",
# 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 = [
"-std=c++17",
"-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++17",
# Tell the linker where to look for libraries.
"-L",
XCODE_SYMLINK + "/lib",
# 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,
],
),
]

View File

@ -0,0 +1,7 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
external/clang_mac_m1/bin/llvm-ar $@

View File

@ -0,0 +1,6 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
external/clang_mac_m1/bin/clang $@

View File

@ -0,0 +1,7 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
external/clang_mac_m1/bin/ld.lld $@