xref: /aosp_15_r20/external/bazelbuild-rules_go/go/private/rules/binary.bzl (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1# Copyright 2014 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:context.bzl",
17    "go_context",
18)
19load(
20    "//go/private:common.bzl",
21    "asm_exts",
22    "cgo_exts",
23    "go_exts",
24)
25load(
26    "//go/private:go_toolchain.bzl",
27    "GO_TOOLCHAIN",
28)
29load(
30    "//go/private:providers.bzl",
31    "GoLibrary",
32    "GoSDK",
33)
34load(
35    "//go/private/rules:transition.bzl",
36    "go_transition",
37)
38load(
39    "//go/private:mode.bzl",
40    "LINKMODES_EXECUTABLE",
41    "LINKMODE_C_ARCHIVE",
42    "LINKMODE_C_SHARED",
43    "LINKMODE_NORMAL",
44    "LINKMODE_PLUGIN",
45    "LINKMODE_SHARED",
46)
47
48_EMPTY_DEPSET = depset([])
49
50def _include_path(hdr):
51    if not hdr.root.path:
52        fail("Expected hdr to be a generated file, got source file: " + hdr.path)
53
54    root_relative_path = hdr.path[len(hdr.root.path + "/"):]
55    if not root_relative_path.startswith("external/"):
56        return hdr.root.path
57
58    # All headers should be includeable via a path relative to their repository
59    # root, regardless of whether the repository is external or not. If it is,
60    # we thus need to append "external/<external repo name>" to the path.
61    return "/".join([hdr.root.path] + root_relative_path.split("/")[0:2])
62
63def new_cc_import(
64        go,
65        hdrs = _EMPTY_DEPSET,
66        defines = _EMPTY_DEPSET,
67        local_defines = _EMPTY_DEPSET,
68        dynamic_library = None,
69        static_library = None,
70        alwayslink = False,
71        linkopts = []):
72    return CcInfo(
73        compilation_context = cc_common.create_compilation_context(
74            defines = defines,
75            local_defines = local_defines,
76            headers = hdrs,
77            includes = depset([_include_path(hdr) for hdr in hdrs.to_list()]),
78        ),
79        linking_context = cc_common.create_linking_context(
80            linker_inputs = depset([
81                cc_common.create_linker_input(
82                    owner = go.label,
83                    libraries = depset([
84                        cc_common.create_library_to_link(
85                            actions = go.actions,
86                            cc_toolchain = go.cgo_tools.cc_toolchain,
87                            feature_configuration = go.cgo_tools.feature_configuration,
88                            dynamic_library = dynamic_library,
89                            static_library = static_library,
90                            alwayslink = alwayslink,
91                        ),
92                    ]),
93                    user_link_flags = depset(linkopts),
94                ),
95            ]),
96        ),
97    )
98
99def _go_binary_impl(ctx):
100    """go_binary_impl emits actions for compiling and linking a go executable."""
101    go = go_context(ctx)
102
103    is_main = go.mode.link not in (LINKMODE_SHARED, LINKMODE_PLUGIN)
104    library = go.new_library(go, importable = False, is_main = is_main)
105    source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented())
106    name = ctx.attr.basename
107    if not name:
108        name = ctx.label.name
109    executable = None
110    if ctx.attr.out:
111        # Use declare_file instead of attr.output(). When users set output files
112        # directly, Bazel warns them not to use the same name as the rule, which is
113        # the common case with go_binary.
114        executable = ctx.actions.declare_file(ctx.attr.out)
115    archive, executable, runfiles = go.binary(
116        go,
117        name = name,
118        source = source,
119        gc_linkopts = gc_linkopts(ctx),
120        version_file = ctx.version_file,
121        info_file = ctx.info_file,
122        executable = executable,
123    )
124
125    providers = [
126        library,
127        source,
128        archive,
129        OutputGroupInfo(
130            cgo_exports = archive.cgo_exports,
131            compilation_outputs = [archive.data.file],
132        ),
133    ]
134
135    if go.mode.link in LINKMODES_EXECUTABLE:
136        env = {}
137        for k, v in ctx.attr.env.items():
138            env[k] = ctx.expand_location(v, ctx.attr.data)
139        providers.append(RunEnvironmentInfo(environment = env))
140
141        # The executable is automatically added to the runfiles.
142        providers.append(DefaultInfo(
143            files = depset([executable]),
144            runfiles = runfiles,
145            executable = executable,
146        ))
147    else:
148        # Workaround for https://github.com/bazelbuild/bazel/issues/15043
149        # As of Bazel 5.1.1, native rules do not pick up the "files" of a data
150        # dependency's DefaultInfo, only the "data_runfiles". Since transitive
151        # non-data dependents should not pick up the executable as a runfile
152        # implicitly, the deprecated "default_runfiles" and "data_runfiles"
153        # constructor parameters have to be used.
154        providers.append(DefaultInfo(
155            files = depset([executable]),
156            default_runfiles = runfiles,
157            data_runfiles = runfiles.merge(ctx.runfiles([executable])),
158        ))
159
160    # If the binary's linkmode is c-archive or c-shared, expose CcInfo
161    if go.cgo_tools and go.mode.link in (LINKMODE_C_ARCHIVE, LINKMODE_C_SHARED):
162        cc_import_kwargs = {
163            "linkopts": {
164                "darwin": [],
165                "ios": [],
166                "windows": ["-mthreads"],
167            }.get(go.mode.goos, ["-pthread"]),
168        }
169        cgo_exports = archive.cgo_exports.to_list()
170        if cgo_exports:
171            header = ctx.actions.declare_file("{}.h".format(name))
172            ctx.actions.symlink(
173                output = header,
174                target_file = cgo_exports[0],
175            )
176            cc_import_kwargs["hdrs"] = depset([header])
177        if go.mode.link == LINKMODE_C_SHARED:
178            cc_import_kwargs["dynamic_library"] = executable
179        elif go.mode.link == LINKMODE_C_ARCHIVE:
180            cc_import_kwargs["static_library"] = executable
181            cc_import_kwargs["alwayslink"] = True
182        ccinfo = new_cc_import(go, **cc_import_kwargs)
183        ccinfo = cc_common.merge_cc_infos(
184            cc_infos = [ccinfo, source.cc_info],
185        )
186        providers.append(ccinfo)
187
188    return providers
189
190_go_binary_kwargs = {
191    "implementation": _go_binary_impl,
192    "attrs": {
193        "srcs": attr.label_list(
194            allow_files = go_exts + asm_exts + cgo_exts,
195            doc = """The list of Go source files that are compiled to create the package.
196            Only `.go` and `.s` files are permitted, unless the `cgo`
197            attribute is set, in which case,
198            `.c .cc .cpp .cxx .h .hh .hpp .hxx .inc .m .mm`
199            files are also permitted. Files may be filtered at build time
200            using Go [build constraints].
201            """,
202        ),
203        "data": attr.label_list(
204            allow_files = True,
205            doc = """List of files needed by this rule at run-time. This may include data files
206            needed or other programs that may be executed. The [bazel] package may be
207            used to locate run files; they may appear in different places depending on the
208            operating system and environment. See [data dependencies] for more
209            information on data files.
210            """,
211        ),
212        "deps": attr.label_list(
213            providers = [GoLibrary],
214            doc = """List of Go libraries this package imports directly.
215            These may be `go_library` rules or compatible rules with the [GoLibrary] provider.
216            """,
217            cfg = go_transition,
218        ),
219        "embed": attr.label_list(
220            providers = [GoLibrary],
221            doc = """List of Go libraries whose sources should be compiled together with this
222            binary's sources. Labels listed here must name `go_library`,
223            `go_proto_library`, or other compatible targets with the [GoLibrary] and
224            [GoSource] providers. Embedded libraries must all have the same `importpath`,
225            which must match the `importpath` for this `go_binary` if one is
226            specified. At most one embedded library may have `cgo = True`, and the
227            embedding binary may not also have `cgo = True`. See [Embedding] for
228            more information.
229            """,
230            cfg = go_transition,
231        ),
232        "embedsrcs": attr.label_list(
233            allow_files = True,
234            doc = """The list of files that may be embedded into the compiled package using
235            `//go:embed` directives. All files must be in the same logical directory
236            or a subdirectory as source files. All source files containing `//go:embed`
237            directives must be in the same logical directory. It's okay to mix static and
238            generated source files and static and generated embeddable files.
239            """,
240        ),
241        "env": attr.string_dict(
242            doc = """Environment variables to set when the binary is executed with bazel run.
243            The values (but not keys) are subject to
244            [location expansion](https://docs.bazel.build/versions/main/skylark/macros.html) but not full
245            [make variable expansion](https://docs.bazel.build/versions/main/be/make-variables.html).
246            """,
247        ),
248        "importpath": attr.string(
249            doc = """The import path of this binary. Binaries can't actually be imported, but this
250            may be used by [go_path] and other tools to report the location of source
251            files. This may be inferred from embedded libraries.
252            """,
253        ),
254        "gc_goopts": attr.string_list(
255            doc = """List of flags to add to the Go compilation command when using the gc compiler.
256            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
257            """,
258        ),
259        "gc_linkopts": attr.string_list(
260            doc = """List of flags to add to the Go link command when using the gc compiler.
261            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
262            """,
263        ),
264        "x_defs": attr.string_dict(
265            doc = """Map of defines to add to the go link command.
266            See [Defines and stamping] for examples of how to use these.
267            """,
268        ),
269        "basename": attr.string(
270            doc = """The basename of this binary. The binary
271            basename may also be platform-dependent: on Windows, we add an .exe extension.
272            """,
273        ),
274        "out": attr.string(
275            doc = """Sets the output filename for the generated executable. When set, `go_binary`
276            will write this file without mode-specific directory prefixes, without
277            linkmode-specific prefixes like "lib", and without platform-specific suffixes
278            like ".exe". Note that without a mode-specific directory prefix, the
279            output file (but not its dependencies) will be invalidated in Bazel's cache
280            when changing configurations.
281            """,
282        ),
283        "cgo": attr.bool(
284            doc = """If `True`, the package may contain [cgo] code, and `srcs` may contain
285            C, C++, Objective-C, and Objective-C++ files and non-Go assembly files.
286            When cgo is enabled, these files will be compiled with the C/C++ toolchain
287            and included in the package. Note that this attribute does not force cgo
288            to be enabled. Cgo is enabled for non-cross-compiling builds when a C/C++
289            toolchain is configured.
290            """,
291        ),
292        "cdeps": attr.label_list(
293            doc = """The list of other libraries that the c code depends on.
294            This can be anything that would be allowed in [cc_library deps]
295            Only valid if `cgo` = `True`.
296            """,
297        ),
298        "cppopts": attr.string_list(
299            doc = """List of flags to add to the C/C++ preprocessor command.
300            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
301            Only valid if `cgo` = `True`.
302            """,
303        ),
304        "copts": attr.string_list(
305            doc = """List of flags to add to the C compilation command.
306            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
307            Only valid if `cgo` = `True`.
308            """,
309        ),
310        "cxxopts": attr.string_list(
311            doc = """List of flags to add to the C++ compilation command.
312            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
313            Only valid if `cgo` = `True`.
314            """,
315        ),
316        "clinkopts": attr.string_list(
317            doc = """List of flags to add to the C link command.
318            Subject to ["Make variable"] substitution and [Bourne shell tokenization].
319            Only valid if `cgo` = `True`.
320            """,
321        ),
322        "pure": attr.string(
323            default = "auto",
324            doc = """Controls whether cgo source code and dependencies are compiled and linked,
325            similar to setting `CGO_ENABLED`. May be one of `on`, `off`,
326            or `auto`. If `auto`, pure mode is enabled when no C/C++
327            toolchain is configured or when cross-compiling. It's usually better to
328            control this on the command line with
329            `--@io_bazel_rules_go//go/config:pure`. See [mode attributes], specifically
330            [pure].
331            """,
332        ),
333        "static": attr.string(
334            default = "auto",
335            doc = """Controls whether a binary is statically linked. May be one of `on`,
336            `off`, or `auto`. Not available on all platforms or in all
337            modes. It's usually better to control this on the command line with
338            `--@io_bazel_rules_go//go/config:static`. See [mode attributes],
339            specifically [static].
340            """,
341        ),
342        "race": attr.string(
343            default = "auto",
344            doc = """Controls whether code is instrumented for race detection. May be one of
345            `on`, `off`, or `auto`. Not available when cgo is
346            disabled. In most cases, it's better to control this on the command line with
347            `--@io_bazel_rules_go//go/config:race`. See [mode attributes], specifically
348            [race].
349            """,
350        ),
351        "msan": attr.string(
352            default = "auto",
353            doc = """Controls whether code is instrumented for memory sanitization. May be one of
354            `on`, `off`, or `auto`. Not available when cgo is
355            disabled. In most cases, it's better to control this on the command line with
356            `--@io_bazel_rules_go//go/config:msan`. See [mode attributes], specifically
357            [msan].
358            """,
359        ),
360        "gotags": attr.string_list(
361            doc = """Enables a list of build tags when evaluating [build constraints]. Useful for
362            conditional compilation.
363            """,
364        ),
365        "goos": attr.string(
366            default = "auto",
367            doc = """Forces a binary to be cross-compiled for a specific operating system. It's
368            usually better to control this on the command line with `--platforms`.
369
370            This disables cgo by default, since a cross-compiling C/C++ toolchain is
371            rarely available. To force cgo, set `pure` = `off`.
372
373            See [Cross compilation] for more information.
374            """,
375        ),
376        "goarch": attr.string(
377            default = "auto",
378            doc = """Forces a binary to be cross-compiled for a specific architecture. It's usually
379            better to control this on the command line with `--platforms`.
380
381            This disables cgo by default, since a cross-compiling C/C++ toolchain is
382            rarely available. To force cgo, set `pure` = `off`.
383
384            See [Cross compilation] for more information.
385            """,
386        ),
387        "linkmode": attr.string(
388            default = LINKMODE_NORMAL,
389            doc = """Determines how the binary should be built and linked. This accepts some of
390            the same values as `go build -buildmode` and works the same way.
391            <br><br>
392            <ul>
393            <li>`normal`: Builds a normal executable with position-dependent code.</li>
394            <li>`pie`: Builds a position-independent executable.</li>
395            <li>`plugin`: Builds a shared library that can be loaded as a Go plugin. Only supported on platforms that support plugins.</li>
396            <li>`c-shared`: Builds a shared library that can be linked into a C program.</li>
397            <li>`c-archive`: Builds an archive that can be linked into a C program.</li>
398            </ul>
399            """,
400        ),
401        "_go_context_data": attr.label(default = "//:go_context_data", cfg = go_transition),
402        "_allowlist_function_transition": attr.label(
403            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
404        ),
405    },
406    "toolchains": [GO_TOOLCHAIN],
407    "doc": """This builds an executable from a set of source files,
408    which must all be in the `main` package. You can run the binary with
409    `bazel run`, or you can build it with `bazel build` and run it directly.<br><br>
410    ***Note:*** `name` should be the same as the desired name of the generated binary.<br><br>
411    **Providers:**
412    <ul>
413      <li>[GoLibrary]</li>
414      <li>[GoSource]</li>
415      <li>[GoArchive]</li>
416    </ul>
417    """,
418}
419
420go_binary = rule(executable = True, **_go_binary_kwargs)
421go_non_executable_binary = rule(executable = False, **_go_binary_kwargs)
422
423def _go_tool_binary_impl(ctx):
424    sdk = ctx.attr.sdk[GoSDK]
425    name = ctx.label.name
426    if sdk.goos == "windows":
427        name += ".exe"
428
429    out = ctx.actions.declare_file(name)
430    if sdk.goos == "windows":
431        gopath = ctx.actions.declare_directory("gopath")
432        gocache = ctx.actions.declare_directory("gocache")
433        cmd = "@echo off\nset GOMAXPROCS=1\nset GOCACHE=%cd%\\{gocache}\nset GOPATH=%cd%\\{gopath}\n{go} build -o {out} -trimpath {srcs}".format(
434            gopath = gopath.path,
435            gocache = gocache.path,
436            go = sdk.go.path.replace("/", "\\"),
437            out = out.path,
438            srcs = " ".join([f.path for f in ctx.files.srcs]),
439        )
440        bat = ctx.actions.declare_file(name + ".bat")
441        ctx.actions.write(
442            output = bat,
443            content = cmd,
444        )
445        ctx.actions.run(
446            executable = bat,
447            inputs = sdk.headers + sdk.tools + sdk.srcs + ctx.files.srcs + [sdk.go],
448            outputs = [out, gopath, gocache],
449            mnemonic = "GoToolchainBinaryBuild",
450        )
451    else:
452        # Note: GOPATH is needed for Go 1.16.
453        cmd = "GOMAXPROCS=1 GOCACHE=$(mktemp -d) GOPATH=$(mktemp -d) {go} build -o {out} -trimpath {srcs}".format(
454            go = sdk.go.path,
455            out = out.path,
456            srcs = " ".join([f.path for f in ctx.files.srcs]),
457        )
458        ctx.actions.run_shell(
459            command = cmd,
460            inputs = sdk.headers + sdk.tools + sdk.srcs + sdk.libs + ctx.files.srcs + [sdk.go],
461            outputs = [out],
462            mnemonic = "GoToolchainBinaryBuild",
463        )
464
465    return [DefaultInfo(
466        files = depset([out]),
467        executable = out,
468    )]
469
470go_tool_binary = rule(
471    implementation = _go_tool_binary_impl,
472    attrs = {
473        "srcs": attr.label_list(
474            allow_files = True,
475            doc = "Source files for the binary. Must be in 'package main'.",
476        ),
477        "sdk": attr.label(
478            mandatory = True,
479            providers = [GoSDK],
480            doc = "The SDK containing tools and libraries to build this binary",
481        ),
482    },
483    executable = True,
484    doc = """Used instead of go_binary for executables used in the toolchain.
485
486go_tool_binary depends on tools and libraries that are part of the Go SDK.
487It does not depend on other toolchains. It can only compile binaries that
488just have a main package and only depend on the standard library and don't
489require build constraints.
490""",
491)
492
493def gc_linkopts(ctx):
494    gc_linkopts = [
495        ctx.expand_make_variables("gc_linkopts", f, {})
496        for f in ctx.attr.gc_linkopts
497    ]
498    return gc_linkopts
499