xref: /aosp_15_r20/external/bazelbuild-rules_go/go/private/rules/cross.bzl (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
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