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