skia2/toolchain/utils.bzl

41 lines
1.8 KiB
Python
Raw Normal View History

[bazel] Add support for Macs to make Linux RBE builds The big change here is having the C++ toolchain use Bazel platforms instead of the C++ specific flags/setup. In Bazel, platforms are a general purpose way to define things like os, cpu architecture, etc. We were not using platforms previously, because the best documentation at the time focused on the old ways. However, the old ways were clumsy/difficult when trying to manage cross-compilation, specifically when trying to have a Mac host trigger a build on our Linux RBE system targeting a Linux x64 system. Thus, rather than keep investing in the legacy system, this CL migrates us to using platforms where possible. Suggested background reading to better understand this CL: - https://bazel.build/concepts/platforms-intro - https://bazel.build/docs/platforms - https://bazel.build/docs/toolchains#registering-building-toolchains The hermetic toolchain itself is not changing in this CL (and likely does not need to), only how we tell Bazel about it (i.e. registering it) and how Bazel decides to use it (i.e. resolving toolchains). Here is my understanding of how platforms and toolchains interact (supported by some evidence from [1][2]) - Bazel needs to resolve platforms for the Host, Execution, and Target. - If not specified via flags, these are the machine from which Bazel is invoked, aka "@local_config_platform//:host". - With this CL, the Host could be a Mac laptop, the Execution platform is our Linux RBE pool, and the Target is "a Linux system with a x64 CPU" - To specify the Host, that is, describe to Bazel the capabilities of the system it is running on, one can set --host_platform [3] with a label pointing to a platform() containing the appropriate settings. Tip: have this platform inherit from @local_config_platform//:host so it can add to any of the constraint_settings and constraint_values that Bazel deduces automatically. - To specify the Target platform(s), that is, the system on which a final output resides and can execute, one can set the --platforms flag with a label referencing a platform(). - Bazel will then choose an execution platform to fulfill that request. Bazel will look through a list of available platforms, which can be augmented* with the --extra_execution_platforms. Platforms specified by this flag will be considered higher than the default platforms! - Having selected the appropriate platforms, Bazel now needs to select a toolchain to actually run the actions of the appropriate type. - Bazel looks through the list of available toolchains and finds one that "matches" the Execution and the Target platform. This means, the toolchain's exec_compatible_with is a strict subset of the Execution platform and the toolchain's target_compatible_with is a strict subset of the Target platform. To register toolchains* (i.e. add them to the resolution list), we use --extra_toolchains. Once Bazel finds a match, it stops looking. Using --toolchain_resolution_debug=".*" makes Bazel log how it is resolving these toolchains and what execution platform it picked. * We can also register execution platforms and toolchains in WORKSPACE.bazel [4], but the flags come with higher priority and that made resolution a bit tricky. Also, when we want to conditionally add them (e.g. --config=linux_rbe), we cannot remove them conditionally in the WORKSPACE.bazel file. The above resolution flow directly necessitated the changes in this CL. Example usage of the new configs and platforms: # Can be run on a x64 Linux host and uses the hermetic toolchain. bazel build //:skia_public # Can be run on Mac or Linux and uses the Linux RBE system along # with the hermetic toolchain to compile a binary for Linux x64. bazel build //:skia_public --config=linux_rbe --config=for_linux_x64 # Shorthand for above bazel build //:skia_public --config=for_linux_x64_with_rbe Notice we don't have to type out --config=clang_linux anymore! That was due to me reading the Bazel docs more carefully and realizing we can set options for *all* Bazel build commands. Current Limitations: - Targets which require a py_binary (e.g. Dawn's genrules) will not work on RBE when cross compiling because the python runtime we download is for the host machine, not the executor. This means //example:hello_world_dawn does not work on Mac when cross-compiling via linux_rbe. - Mac M1 linking not quite working with SkOpts settings. Probably need to set -target [5] Suggested Review order: - toolchain/BUILD.bazel Notice how we do away with cc_toolchain_suite for toolchain. These have the same role: giving Bazel the information about where a toolchain can run. The platforms one is more expressive (IMO), allowing us to say both where to run the toolchain and what it can make. In order to more easily force the use of our hermetic toolchain, but also allow the hermetic toolchain to be used on RBE, we specify "use_hermetic_toolchain" only on the target, because the RBE image does not have the hermetic toolchain on it by default (but can certainly run it). - bazel/platform/BUILD.bazel to see the custom constraint_setting and corresponding constraint_value. The names for both of these are completely arbitrary - they do not need to have any deeper meaning or relation to any file or Docker image or system or any other constraints. Think of the constraint_setting as an Enum and the constraint_value being the one and only member. We need to pass around a constant value, not a type, so we need to provide the constraint_value (e.g. in toolchain/BUILD.bazel) but not a constraint_setting. However we need a constraint_setting declared so we can make a constraint_value of that "type". Notice the platform declared here - it allows us to force Bazel to use the hermetic toolchain because of the extra constraint_value. - .bazelrc I set a few flags that will be on for all bazel build commands. Importantly, this causes the C++ build logic to use platforms and not the old, bespoke way. I also found a way to avoid using the local toolchain on the host, which will hopefully lead to clearer errors if platforms are mis-specified instead of odd compile errors because the host toolchain is too old or something. There are also a few RBE settings tweaked to be a bit more modern, as well the new shorthands for specifying target platforms (e.g. for_linux_x64). - bazel/buildrc where we have to turn off the platforms logic for emscripten https://github.com/emscripten-core/emsdk/issues/984 - bazel/rbe/BUILD.bazel for a fix in the platform description that makes it work on Mac. - Notice that _m1 has been removed from the mac-related toolchain files because the same toolchain should work on both architectures. - All other changes in any order. [1] https://bazel.build/docs/toolchains#debugging-toolchains [2] https://bazel.build/docs/toolchains#toolchain-resolution [3] https://bazel.build/reference/command-line-reference [4] https://bazel.build/docs/toolchains#registering-building-toolchains [5] https://github.com/google/skia/blob/17dc3f16fc78477503ba1ef484c3b47bc3aab893/gn/skia/BUILD.gn#L258-L271 Change-Id: I515c114099d659639a808f74e47d489a68b7af62 Bug: skia:12541 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/549737 Reviewed-by: Erik Rose <erikrose@google.com> Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
2022-06-22 19:28:10 +00:00
"""This module provides the gcs_mirror_url macro."""
# Set to True to force the macro to only return the mirror URL.
_TEST_GCS_MIRROR = False
# Must be kept in sync with the suffixes supported by gcs_mirror (e.g.
# https://skia.googlesource.com/skia/+/8ad66c2340713234df6b249e793415233337a103/bazel/gcs_mirror/gcs_mirror.go#140).
_SUPPORTED_SUFFIXES = [".tar.gz", ".tgz", ".tar.xz", ".deb", ".zip"]
_GCS_MIRROR_PREFIX = "https://storage.googleapis.com/skia-world-readable/bazel"
def gcs_mirror_url(url, sha256):
"""Takes the URL of an external resource and computes its GCS mirror URL.
We store backup copies of external resources in the skia-world-readable GCS bucket. This macro
returns a list with two elements: the original URL, and the mirrored URL.
Files are expected to be in the mirror location named after their sha256 hash. The files should
still have their file extension, as some of the Starlark functions sniff the file extension
(e.g. download_and_extract). See //bazel/gcs_mirror for an automated way to update this mirror.
To mirror a new URL, please use the `gcs_mirror` utility found at
https://skia.googlesource.com/skia/+/8ad66c2340713234df6b249e793415233337a103/bazel/gcs_mirror/gcs_mirror.go.
Args:
url: URL of the mirrored resource.
sha256: SHA256 hash of the mirrored resource.
Returns:
A list of the form [original URL, mirror URL].
"""
extension = ""
for suffix in _SUPPORTED_SUFFIXES:
if url.endswith(suffix):
extension = suffix
break
if extension == "":
fail("URL %s has an unsupported suffix." % url)
mirror_url = "%s/%s%s" % (_GCS_MIRROR_PREFIX, sha256, extension)
[bazel] Add support for Macs to make Linux RBE builds The big change here is having the C++ toolchain use Bazel platforms instead of the C++ specific flags/setup. In Bazel, platforms are a general purpose way to define things like os, cpu architecture, etc. We were not using platforms previously, because the best documentation at the time focused on the old ways. However, the old ways were clumsy/difficult when trying to manage cross-compilation, specifically when trying to have a Mac host trigger a build on our Linux RBE system targeting a Linux x64 system. Thus, rather than keep investing in the legacy system, this CL migrates us to using platforms where possible. Suggested background reading to better understand this CL: - https://bazel.build/concepts/platforms-intro - https://bazel.build/docs/platforms - https://bazel.build/docs/toolchains#registering-building-toolchains The hermetic toolchain itself is not changing in this CL (and likely does not need to), only how we tell Bazel about it (i.e. registering it) and how Bazel decides to use it (i.e. resolving toolchains). Here is my understanding of how platforms and toolchains interact (supported by some evidence from [1][2]) - Bazel needs to resolve platforms for the Host, Execution, and Target. - If not specified via flags, these are the machine from which Bazel is invoked, aka "@local_config_platform//:host". - With this CL, the Host could be a Mac laptop, the Execution platform is our Linux RBE pool, and the Target is "a Linux system with a x64 CPU" - To specify the Host, that is, describe to Bazel the capabilities of the system it is running on, one can set --host_platform [3] with a label pointing to a platform() containing the appropriate settings. Tip: have this platform inherit from @local_config_platform//:host so it can add to any of the constraint_settings and constraint_values that Bazel deduces automatically. - To specify the Target platform(s), that is, the system on which a final output resides and can execute, one can set the --platforms flag with a label referencing a platform(). - Bazel will then choose an execution platform to fulfill that request. Bazel will look through a list of available platforms, which can be augmented* with the --extra_execution_platforms. Platforms specified by this flag will be considered higher than the default platforms! - Having selected the appropriate platforms, Bazel now needs to select a toolchain to actually run the actions of the appropriate type. - Bazel looks through the list of available toolchains and finds one that "matches" the Execution and the Target platform. This means, the toolchain's exec_compatible_with is a strict subset of the Execution platform and the toolchain's target_compatible_with is a strict subset of the Target platform. To register toolchains* (i.e. add them to the resolution list), we use --extra_toolchains. Once Bazel finds a match, it stops looking. Using --toolchain_resolution_debug=".*" makes Bazel log how it is resolving these toolchains and what execution platform it picked. * We can also register execution platforms and toolchains in WORKSPACE.bazel [4], but the flags come with higher priority and that made resolution a bit tricky. Also, when we want to conditionally add them (e.g. --config=linux_rbe), we cannot remove them conditionally in the WORKSPACE.bazel file. The above resolution flow directly necessitated the changes in this CL. Example usage of the new configs and platforms: # Can be run on a x64 Linux host and uses the hermetic toolchain. bazel build //:skia_public # Can be run on Mac or Linux and uses the Linux RBE system along # with the hermetic toolchain to compile a binary for Linux x64. bazel build //:skia_public --config=linux_rbe --config=for_linux_x64 # Shorthand for above bazel build //:skia_public --config=for_linux_x64_with_rbe Notice we don't have to type out --config=clang_linux anymore! That was due to me reading the Bazel docs more carefully and realizing we can set options for *all* Bazel build commands. Current Limitations: - Targets which require a py_binary (e.g. Dawn's genrules) will not work on RBE when cross compiling because the python runtime we download is for the host machine, not the executor. This means //example:hello_world_dawn does not work on Mac when cross-compiling via linux_rbe. - Mac M1 linking not quite working with SkOpts settings. Probably need to set -target [5] Suggested Review order: - toolchain/BUILD.bazel Notice how we do away with cc_toolchain_suite for toolchain. These have the same role: giving Bazel the information about where a toolchain can run. The platforms one is more expressive (IMO), allowing us to say both where to run the toolchain and what it can make. In order to more easily force the use of our hermetic toolchain, but also allow the hermetic toolchain to be used on RBE, we specify "use_hermetic_toolchain" only on the target, because the RBE image does not have the hermetic toolchain on it by default (but can certainly run it). - bazel/platform/BUILD.bazel to see the custom constraint_setting and corresponding constraint_value. The names for both of these are completely arbitrary - they do not need to have any deeper meaning or relation to any file or Docker image or system or any other constraints. Think of the constraint_setting as an Enum and the constraint_value being the one and only member. We need to pass around a constant value, not a type, so we need to provide the constraint_value (e.g. in toolchain/BUILD.bazel) but not a constraint_setting. However we need a constraint_setting declared so we can make a constraint_value of that "type". Notice the platform declared here - it allows us to force Bazel to use the hermetic toolchain because of the extra constraint_value. - .bazelrc I set a few flags that will be on for all bazel build commands. Importantly, this causes the C++ build logic to use platforms and not the old, bespoke way. I also found a way to avoid using the local toolchain on the host, which will hopefully lead to clearer errors if platforms are mis-specified instead of odd compile errors because the host toolchain is too old or something. There are also a few RBE settings tweaked to be a bit more modern, as well the new shorthands for specifying target platforms (e.g. for_linux_x64). - bazel/buildrc where we have to turn off the platforms logic for emscripten https://github.com/emscripten-core/emsdk/issues/984 - bazel/rbe/BUILD.bazel for a fix in the platform description that makes it work on Mac. - Notice that _m1 has been removed from the mac-related toolchain files because the same toolchain should work on both architectures. - All other changes in any order. [1] https://bazel.build/docs/toolchains#debugging-toolchains [2] https://bazel.build/docs/toolchains#toolchain-resolution [3] https://bazel.build/reference/command-line-reference [4] https://bazel.build/docs/toolchains#registering-building-toolchains [5] https://github.com/google/skia/blob/17dc3f16fc78477503ba1ef484c3b47bc3aab893/gn/skia/BUILD.gn#L258-L271 Change-Id: I515c114099d659639a808f74e47d489a68b7af62 Bug: skia:12541 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/549737 Reviewed-by: Erik Rose <erikrose@google.com> Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
2022-06-22 19:28:10 +00:00
return [mirror_url] if _TEST_GCS_MIRROR else [url, mirror_url]