blob: d98e2f656dbba48fa78915e2251768afff767b31 [file] [log] [blame]
# Copyright 2022 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load(
"//go/private/rules:transition.bzl",
"go_cross_transition",
)
load(
"//go/private:providers.bzl",
"GoArchive",
"GoLibrary",
"GoSource",
)
def _is_windows(ctx):
return ctx.configuration.host_path_separator == ";"
WINDOWS_ERR_SCRIPT = """
@echo off
>&2 echo {}
exit /b 1
"""
UNIX_ERR_SCRIPT = """
>&2 echo '{}'
exit 1
"""
def _error_script(ctx):
errmsg = 'cannot run go_cross target "{}": underlying target "{}" is not executable'.format(
ctx.attr.name,
ctx.attr.target.label,
)
if _is_windows(ctx):
error_script = ctx.actions.declare_file("error.bat")
ctx.actions.write(error_script, WINDOWS_ERR_SCRIPT.format(errmsg), is_executable = True)
return error_script
error_script = ctx.actions.declare_file("error")
ctx.actions.write(error_script, UNIX_ERR_SCRIPT.format(errmsg), is_executable = True)
return error_script
def _go_cross_impl(ctx):
old_default_info = ctx.attr.target[DefaultInfo]
old_executable = old_default_info.files_to_run.executable
new_default_info = None
if old_executable:
# Bazel requires executable rules to created the executable themselves,
# so we create a symlink in this rule so that it appears this rule created its executable.
new_executable = ctx.actions.declare_file(ctx.attr.name)
ctx.actions.symlink(output = new_executable, target_file = old_executable)
new_default_info = DefaultInfo(
files = depset([new_executable]),
runfiles = old_default_info.default_runfiles,
executable = new_executable,
)
else:
# There's no way to determine if the underlying `go_binary` target is executable at loading time
# so we must set the `go_cross` rule to be always executable. If the `go_binary` target is not
# executable, we set the `go_cross` executable to a simple script that prints an error message
# when executed. This way users can still run a `go_cross` target using `bazel run` as long as
# the underlying `go_binary` target is executable.
error_script = _error_script(ctx)
# See the implementation of `go_binary` for an explanation of the need for default vs data runfiles here.
new_default_info = DefaultInfo(
files = depset([error_script] + old_default_info.files.to_list()),
default_runfiles = old_default_info.default_runfiles,
data_runfiles = old_default_info.data_runfiles.merge(ctx.runfiles([error_script])),
executable = error_script,
)
providers = [
ctx.attr.target[provider]
for provider in [
GoLibrary,
GoSource,
GoArchive,
OutputGroupInfo,
CcInfo,
]
if provider in ctx.attr.target
]
return [new_default_info] + providers
_go_cross_kwargs = {
"implementation": _go_cross_impl,
"attrs": {
"target": attr.label(
doc = """Go binary target to transition to the given platform and/or sdk_version.
""",
providers = [GoLibrary, GoSource, GoArchive],
mandatory = True,
),
"platform": attr.label(
doc = """The platform to cross compile the `target` for.
If unspecified, the `target` will be compiled with the
same platform as it would've with the original `go_binary` rule.
""",
),
"sdk_version": attr.string(
doc = """The golang SDK version to use for compiling the `target`.
Supports specifying major, minor, and/or patch versions, eg. `"1"`,
`"1.17"`, or `"1.17.1"`. The first Go SDK provider installed in the
repo's workspace (via `go_download_sdk`, `go_wrap_sdk`, etc) that
matches the specified version will be used for compiling the given
`target`. If unspecified, the `target` will be compiled with the same
SDK as it would've with the original `go_binary` rule.
Transitions `target` by changing the `--@io_bazel_rules_go//go/toolchain:sdk_version`
build flag to the value provided for `sdk_version` here.
""",
),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
"cfg": go_cross_transition,
"doc": """This wraps an executable built by `go_binary` to cross compile it
for a different platform, and/or compile it using a different version
of the golang SDK.<br><br>
**Providers:**
<ul>
<li>[GoLibrary]</li>
<li>[GoSource]</li>
<li>[GoArchive]</li>
</ul>
""",
}
go_cross_binary = rule(executable = True, **_go_cross_kwargs)