1*9bb1b549SSpandan Das# Copyright 2022 The Bazel Authors. All rights reserved. 2*9bb1b549SSpandan Das# 3*9bb1b549SSpandan Das# Licensed under the Apache License, Version 2.0 (the "License"); 4*9bb1b549SSpandan Das# you may not use this file except in compliance with the License. 5*9bb1b549SSpandan Das# You may obtain a copy of the License at 6*9bb1b549SSpandan Das# 7*9bb1b549SSpandan Das# http://www.apache.org/licenses/LICENSE-2.0 8*9bb1b549SSpandan Das# 9*9bb1b549SSpandan Das# Unless required by applicable law or agreed to in writing, software 10*9bb1b549SSpandan Das# distributed under the License is distributed on an "AS IS" BASIS, 11*9bb1b549SSpandan Das# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9bb1b549SSpandan Das# See the License for the specific language governing permissions and 13*9bb1b549SSpandan Das# limitations under the License. 14*9bb1b549SSpandan Das 15*9bb1b549SSpandan Dasload( 16*9bb1b549SSpandan Das "//go/private/rules:transition.bzl", 17*9bb1b549SSpandan Das "go_cross_transition", 18*9bb1b549SSpandan Das) 19*9bb1b549SSpandan Dasload( 20*9bb1b549SSpandan Das "//go/private:providers.bzl", 21*9bb1b549SSpandan Das "GoArchive", 22*9bb1b549SSpandan Das "GoLibrary", 23*9bb1b549SSpandan Das "GoSource", 24*9bb1b549SSpandan Das) 25*9bb1b549SSpandan Das 26*9bb1b549SSpandan Dasdef _is_windows(ctx): 27*9bb1b549SSpandan Das return ctx.configuration.host_path_separator == ";" 28*9bb1b549SSpandan Das 29*9bb1b549SSpandan DasWINDOWS_ERR_SCRIPT = """ 30*9bb1b549SSpandan Das@echo off 31*9bb1b549SSpandan Das>&2 echo {} 32*9bb1b549SSpandan Dasexit /b 1 33*9bb1b549SSpandan Das""" 34*9bb1b549SSpandan DasUNIX_ERR_SCRIPT = """ 35*9bb1b549SSpandan Das>&2 echo '{}' 36*9bb1b549SSpandan Dasexit 1 37*9bb1b549SSpandan Das""" 38*9bb1b549SSpandan Das 39*9bb1b549SSpandan Dasdef _error_script(ctx): 40*9bb1b549SSpandan Das errmsg = 'cannot run go_cross target "{}": underlying target "{}" is not executable'.format( 41*9bb1b549SSpandan Das ctx.attr.name, 42*9bb1b549SSpandan Das ctx.attr.target.label, 43*9bb1b549SSpandan Das ) 44*9bb1b549SSpandan Das if _is_windows(ctx): 45*9bb1b549SSpandan Das error_script = ctx.actions.declare_file("fake_executable_for_bazel_run.bat") 46*9bb1b549SSpandan Das ctx.actions.write(error_script, WINDOWS_ERR_SCRIPT.format(errmsg), is_executable = True) 47*9bb1b549SSpandan Das return error_script 48*9bb1b549SSpandan Das 49*9bb1b549SSpandan Das error_script = ctx.actions.declare_file("fake_executable_for_bazel_run") 50*9bb1b549SSpandan Das ctx.actions.write(error_script, UNIX_ERR_SCRIPT.format(errmsg), is_executable = True) 51*9bb1b549SSpandan Das return error_script 52*9bb1b549SSpandan Das 53*9bb1b549SSpandan Dasdef _go_cross_impl(ctx): 54*9bb1b549SSpandan Das old_default_info = ctx.attr.target[DefaultInfo] 55*9bb1b549SSpandan Das old_executable = old_default_info.files_to_run.executable 56*9bb1b549SSpandan Das 57*9bb1b549SSpandan Das new_default_info = None 58*9bb1b549SSpandan Das if old_executable: 59*9bb1b549SSpandan Das # Bazel requires executable rules to created the executable themselves, 60*9bb1b549SSpandan Das # so we create a symlink in this rule so that it appears this rule created its executable. 61*9bb1b549SSpandan Das new_executable = ctx.actions.declare_file(ctx.attr.name) 62*9bb1b549SSpandan Das ctx.actions.symlink(output = new_executable, target_file = old_executable) 63*9bb1b549SSpandan Das new_default_info = DefaultInfo( 64*9bb1b549SSpandan Das files = depset([new_executable]), 65*9bb1b549SSpandan Das runfiles = old_default_info.default_runfiles, 66*9bb1b549SSpandan Das executable = new_executable, 67*9bb1b549SSpandan Das ) 68*9bb1b549SSpandan Das else: 69*9bb1b549SSpandan Das # There's no way to determine if the underlying `go_binary` target is executable at loading time 70*9bb1b549SSpandan Das # so we must set the `go_cross` rule to be always executable. If the `go_binary` target is not 71*9bb1b549SSpandan Das # executable, we set the `go_cross` executable to a simple script that prints an error message 72*9bb1b549SSpandan Das # when executed. This way users can still run a `go_cross` target using `bazel run` as long as 73*9bb1b549SSpandan Das # the underlying `go_binary` target is executable. 74*9bb1b549SSpandan Das error_script = _error_script(ctx) 75*9bb1b549SSpandan Das 76*9bb1b549SSpandan Das # See the implementation of `go_binary` for an explanation of the need for default vs data runfiles here. 77*9bb1b549SSpandan Das new_default_info = DefaultInfo( 78*9bb1b549SSpandan Das files = depset([error_script] + old_default_info.files.to_list()), 79*9bb1b549SSpandan Das default_runfiles = old_default_info.default_runfiles, 80*9bb1b549SSpandan Das data_runfiles = old_default_info.data_runfiles.merge(ctx.runfiles([error_script])), 81*9bb1b549SSpandan Das executable = error_script, 82*9bb1b549SSpandan Das ) 83*9bb1b549SSpandan Das 84*9bb1b549SSpandan Das providers = [ 85*9bb1b549SSpandan Das ctx.attr.target[provider] 86*9bb1b549SSpandan Das for provider in [ 87*9bb1b549SSpandan Das GoLibrary, 88*9bb1b549SSpandan Das GoSource, 89*9bb1b549SSpandan Das GoArchive, 90*9bb1b549SSpandan Das OutputGroupInfo, 91*9bb1b549SSpandan Das CcInfo, 92*9bb1b549SSpandan Das ] 93*9bb1b549SSpandan Das if provider in ctx.attr.target 94*9bb1b549SSpandan Das ] 95*9bb1b549SSpandan Das return [new_default_info] + providers 96*9bb1b549SSpandan Das 97*9bb1b549SSpandan Das_go_cross_kwargs = { 98*9bb1b549SSpandan Das "implementation": _go_cross_impl, 99*9bb1b549SSpandan Das "attrs": { 100*9bb1b549SSpandan Das "target": attr.label( 101*9bb1b549SSpandan Das doc = """Go binary target to transition to the given platform and/or sdk_version. 102*9bb1b549SSpandan Das """, 103*9bb1b549SSpandan Das providers = [GoLibrary, GoSource, GoArchive], 104*9bb1b549SSpandan Das mandatory = True, 105*9bb1b549SSpandan Das ), 106*9bb1b549SSpandan Das "platform": attr.label( 107*9bb1b549SSpandan Das doc = """The platform to cross compile the `target` for. 108*9bb1b549SSpandan Das If unspecified, the `target` will be compiled with the 109*9bb1b549SSpandan Das same platform as it would've with the original `go_binary` rule. 110*9bb1b549SSpandan Das """, 111*9bb1b549SSpandan Das ), 112*9bb1b549SSpandan Das "sdk_version": attr.string( 113*9bb1b549SSpandan Das doc = """The golang SDK version to use for compiling the `target`. 114*9bb1b549SSpandan Das Supports specifying major, minor, and/or patch versions, eg. `"1"`, 115*9bb1b549SSpandan Das `"1.17"`, or `"1.17.1"`. The first Go SDK provider installed in the 116*9bb1b549SSpandan Das repo's workspace (via `go_download_sdk`, `go_wrap_sdk`, etc) that 117*9bb1b549SSpandan Das matches the specified version will be used for compiling the given 118*9bb1b549SSpandan Das `target`. If unspecified, the `target` will be compiled with the same 119*9bb1b549SSpandan Das SDK as it would've with the original `go_binary` rule. 120*9bb1b549SSpandan Das Transitions `target` by changing the `--@io_bazel_rules_go//go/toolchain:sdk_version` 121*9bb1b549SSpandan Das build flag to the value provided for `sdk_version` here. 122*9bb1b549SSpandan Das """, 123*9bb1b549SSpandan Das ), 124*9bb1b549SSpandan Das "_allowlist_function_transition": attr.label( 125*9bb1b549SSpandan Das default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 126*9bb1b549SSpandan Das ), 127*9bb1b549SSpandan Das }, 128*9bb1b549SSpandan Das "cfg": go_cross_transition, 129*9bb1b549SSpandan Das "doc": """This wraps an executable built by `go_binary` to cross compile it 130*9bb1b549SSpandan Das for a different platform, and/or compile it using a different version 131*9bb1b549SSpandan Das of the golang SDK.<br><br> 132*9bb1b549SSpandan Das **Providers:** 133*9bb1b549SSpandan Das <ul> 134*9bb1b549SSpandan Das <li>[GoLibrary]</li> 135*9bb1b549SSpandan Das <li>[GoSource]</li> 136*9bb1b549SSpandan Das <li>[GoArchive]</li> 137*9bb1b549SSpandan Das </ul> 138*9bb1b549SSpandan Das """, 139*9bb1b549SSpandan Das} 140*9bb1b549SSpandan Das 141*9bb1b549SSpandan Dasgo_cross_binary = rule(executable = True, **_go_cross_kwargs) 142