Skip to content

Commit

Permalink
Transition container image target platform
Browse files Browse the repository at this point in the history
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
bazelbuild#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`.
  • Loading branch information
uhthomas committed Nov 19, 2021
1 parent 8f6a2aa commit 59a8163
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 5 deletions.
34 changes: 29 additions & 5 deletions lang/image.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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"],
Expand Down
49 changes: 49 additions & 0 deletions platforms/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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]

0 comments on commit 59a8163

Please sign in to comment.