| # 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) |