diff options
Diffstat (limited to 'go/private/rules/cross.bzl')
-rw-r--r-- | go/private/rules/cross.bzl | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/go/private/rules/cross.bzl b/go/private/rules/cross.bzl new file mode 100644 index 00000000..961e3651 --- /dev/null +++ b/go/private/rules/cross.bzl @@ -0,0 +1,141 @@ +# 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("fake_executable_for_bazel_run.bat") + ctx.actions.write(error_script, WINDOWS_ERR_SCRIPT.format(errmsg), is_executable = True) + return error_script + + error_script = ctx.actions.declare_file("fake_executable_for_bazel_run") + 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) |