From 59a81633e7a5731aac6259e0c1f7443753f4f17d Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Nov 2021 21:39:09 +0000 Subject: [PATCH] Transition container image target platform Transition to the target platform associated with the `architecture` and `operating_system` attributes on container image rules. This change allows for container image rules to build the correct binary for the target platform, regardless of the host platform. Container image rules would require the use of the `target_compatible_with` attribute to prevent mismatching host and target platforms building dependencies incorrectly. Additionally, hosts which did not match the target platform had to explicitly specify the target platform with the `--platforms` command-line option. This change fixes the aforementioned issues and https://github.com/bazelbuild/rules_docker/issues/690. Massive thank you to @joneshf for the initial source. It has been adapted to automatically select the target platform associated with the container image, as opposed to always using `@io_bazel_rules_go//go/toolchain:linux_amd64`. --- lang/image.bzl | 34 +++++++++++++++++++++++++++++----- platforms/BUILD | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/lang/image.bzl b/lang/image.bzl index c2db8f495..8078c2424 100644 --- a/lang/image.bzl +++ b/lang/image.bzl @@ -34,8 +34,8 @@ def _binary_name(ctx): # /app/foo/bar/baz/blah return "/".join([ ctx.attr.directory, - ctx.attr.binary.label.package, - ctx.attr.binary.label.name, + ctx.attr.binary[0].label.package, + ctx.attr.binary[0].label.name, ]) def _runfiles_dir(ctx): @@ -148,7 +148,7 @@ def _app_layer_impl(ctx, runfiles = None, emptyfiles = None): parent_parts = _get_layers(ctx, ctx.attr.name, ctx.attr.base) filepath = _final_file_path if ctx.attr.binary else layer_file_path emptyfilepath = _final_emptyfile_path if ctx.attr.binary else _layer_emptyfile_path - dep = ctx.attr.dep or ctx.attr.binary + dep = (ctx.attr.dep or ctx.attr.binary)[0] top_layer = ctx.attr.binary and not ctx.attr.dep if ctx.attr.create_empty_workspace_dir: @@ -239,6 +239,27 @@ def _app_layer_impl(ctx, runfiles = None, emptyfiles = None): null_cmd = args == [], ) +def _image_transition_impl(settings, attr): + _ignore = (settings, attr) + + # Architecture aliases. + architecture = { + "386": "x86_32", + "amd64": "x86_64", + "ppc64le": "ppc", + }.get(attr.architecture, attr.architecture) + return { + "//command_line_option:platforms": "//platforms:{}_{}".format(attr.operating_system, architecture), + } + +image_transition = transition( + implementation = _image_transition_impl, + inputs = [], + outputs = [ + "//command_line_option:platforms", + ], +) + image = struct( attrs = dicts.add(_container.image.attrs, { # The base image on which to overlay the dependency layers. @@ -250,7 +271,7 @@ image = struct( # the runfiles dir. "binary": attr.label( executable = True, - cfg = "target", + cfg = image_transition, ), # Set this to true to create an empty workspace directory under the # app directory specified as the 'directory' attribute. @@ -263,11 +284,14 @@ image = struct( # The dependency whose runfiles we're appending. # If not specified, then the layer will be treated as the top layer, # and all remaining deps of "binary" will be added under runfiles. - "dep": attr.label(), + "dep": attr.label(cfg = image_transition), "directory": attr.string(default = "/app"), "entrypoint": attr.string_list(default = []), "legacy_run_behavior": attr.bool(default = False), "workdir": attr.string(default = ""), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), }), outputs = _container.image.outputs, toolchains = ["@io_bazel_rules_docker//toolchains/docker:toolchain_type"], diff --git a/platforms/BUILD b/platforms/BUILD index 50b8593ed..5d05829c4 100644 --- a/platforms/BUILD +++ b/platforms/BUILD @@ -56,3 +56,52 @@ platform( ], parents = ["@buildkite_config//config:platform"], ) + +# All OSs known to Bazel. +# +# curl -Lfs https://raw.githubusercontent.com/bazelbuild/platforms/main/os/BUILD | grep -Eo 'name = "\w+"' | grep -Eo '"\w+"' | grep -Ev 'srcs|os|none' | tr -d '"' | sort -u -f | xargs -I '{}' echo '"@platforms//os:{}",' +_OS_CONSTRAINTS = ( + "@platforms//os:android", + "@platforms//os:freebsd", + "@platforms//os:linux", + "@platforms//os:netbsd", + "@platforms//os:openbsd", + "@platforms//os:qnx", + "@platforms//os:wasi", + "@platforms//os:windows", +) + +# All CPUs known to Bazel. +# +# curl -Lfs https://raw.githubusercontent.com/bazelbuild/platforms/main/cpu/BUILD | grep -Eo 'name = "\w+"' | grep -Eo '"\w+"' | grep -Ev 'cpu|srcs' | tr -d '"' | sort -u -f | xargs -I '{}' echo '"@platforms//cpu:{}",' +_CPU_CONSTRAINTS = ( + "@platforms//cpu:aarch64", + "@platforms//cpu:arm", + "@platforms//cpu:arm64", + "@platforms//cpu:arm64e", + "@platforms//cpu:arm64_32", + "@platforms//cpu:armv7", + "@platforms//cpu:armv7k", + "@platforms//cpu:i386", + "@platforms//cpu:mips64", + "@platforms//cpu:ppc", + "@platforms//cpu:riscv32", + "@platforms//cpu:riscv64", + "@platforms//cpu:s390x", + "@platforms//cpu:wasm32", + "@platforms//cpu:wasm64", + "@platforms//cpu:x86_32", + "@platforms//cpu:x86_64", +) + +# Register all known Bazel platform combinations for use with transitions. +[platform( + name = "{}_{}".format( + os.rsplit(":", 1)[1], + cpu.rsplit(":", 1)[1], + ), + constraint_values = [ + os, + cpu, + ], +) for os in _OS_CONSTRAINTS for cpu in _CPU_CONSTRAINTS]