aboutsummaryrefslogtreecommitdiff
path: root/go/private/rules/binary.bzl
diff options
context:
space:
mode:
authorSpandan Das <spandandas@google.com>2023-06-15 02:30:50 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-06-15 02:30:50 +0000
commit9bb1b549b6a84214c53be0924760be030e66b93a (patch)
treed9fac15bb5a835ae6ba757dc5eaf6ef597ea44cf /go/private/rules/binary.bzl
parent9803cf8403d7105bddc1d5304d6e694b781a6605 (diff)
parent780ccd3956961690db3e36d8fa1ed7649cb0057b (diff)
downloadbazelbuild-rules_go-android14-qpr2-s1-release.tar.gz
Original change: https://android-review.googlesource.com/c/platform/external/bazelbuild-rules_go/+/2625353 Change-Id: Id4ca3195d832eca77b29b2896b89027d847bb72d Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'go/private/rules/binary.bzl')
-rw-r--r--go/private/rules/binary.bzl498
1 files changed, 498 insertions, 0 deletions
diff --git a/go/private/rules/binary.bzl b/go/private/rules/binary.bzl
new file mode 100644
index 00000000..51530b04
--- /dev/null
+++ b/go/private/rules/binary.bzl
@@ -0,0 +1,498 @@
+# Copyright 2014 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:context.bzl",
+ "go_context",
+)
+load(
+ "//go/private:common.bzl",
+ "asm_exts",
+ "cgo_exts",
+ "go_exts",
+)
+load(
+ "//go/private:go_toolchain.bzl",
+ "GO_TOOLCHAIN",
+)
+load(
+ "//go/private:providers.bzl",
+ "GoLibrary",
+ "GoSDK",
+)
+load(
+ "//go/private/rules:transition.bzl",
+ "go_transition",
+)
+load(
+ "//go/private:mode.bzl",
+ "LINKMODES_EXECUTABLE",
+ "LINKMODE_C_ARCHIVE",
+ "LINKMODE_C_SHARED",
+ "LINKMODE_NORMAL",
+ "LINKMODE_PLUGIN",
+ "LINKMODE_SHARED",
+)
+
+_EMPTY_DEPSET = depset([])
+
+def _include_path(hdr):
+ if not hdr.root.path:
+ fail("Expected hdr to be a generated file, got source file: " + hdr.path)
+
+ root_relative_path = hdr.path[len(hdr.root.path + "/"):]
+ if not root_relative_path.startswith("external/"):
+ return hdr.root.path
+
+ # All headers should be includeable via a path relative to their repository
+ # root, regardless of whether the repository is external or not. If it is,
+ # we thus need to append "external/<external repo name>" to the path.
+ return "/".join([hdr.root.path] + root_relative_path.split("/")[0:2])
+
+def new_cc_import(
+ go,
+ hdrs = _EMPTY_DEPSET,
+ defines = _EMPTY_DEPSET,
+ local_defines = _EMPTY_DEPSET,
+ dynamic_library = None,
+ static_library = None,
+ alwayslink = False,
+ linkopts = []):
+ return CcInfo(
+ compilation_context = cc_common.create_compilation_context(
+ defines = defines,
+ local_defines = local_defines,
+ headers = hdrs,
+ includes = depset([_include_path(hdr) for hdr in hdrs.to_list()]),
+ ),
+ linking_context = cc_common.create_linking_context(
+ linker_inputs = depset([
+ cc_common.create_linker_input(
+ owner = go.label,
+ libraries = depset([
+ cc_common.create_library_to_link(
+ actions = go.actions,
+ cc_toolchain = go.cgo_tools.cc_toolchain,
+ feature_configuration = go.cgo_tools.feature_configuration,
+ dynamic_library = dynamic_library,
+ static_library = static_library,
+ alwayslink = alwayslink,
+ ),
+ ]),
+ user_link_flags = depset(linkopts),
+ ),
+ ]),
+ ),
+ )
+
+def _go_binary_impl(ctx):
+ """go_binary_impl emits actions for compiling and linking a go executable."""
+ go = go_context(ctx)
+
+ is_main = go.mode.link not in (LINKMODE_SHARED, LINKMODE_PLUGIN)
+ library = go.new_library(go, importable = False, is_main = is_main)
+ source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented())
+ name = ctx.attr.basename
+ if not name:
+ name = ctx.label.name
+ executable = None
+ if ctx.attr.out:
+ # Use declare_file instead of attr.output(). When users set output files
+ # directly, Bazel warns them not to use the same name as the rule, which is
+ # the common case with go_binary.
+ executable = ctx.actions.declare_file(ctx.attr.out)
+ archive, executable, runfiles = go.binary(
+ go,
+ name = name,
+ source = source,
+ gc_linkopts = gc_linkopts(ctx),
+ version_file = ctx.version_file,
+ info_file = ctx.info_file,
+ executable = executable,
+ )
+
+ providers = [
+ library,
+ source,
+ archive,
+ OutputGroupInfo(
+ cgo_exports = archive.cgo_exports,
+ compilation_outputs = [archive.data.file],
+ ),
+ ]
+
+ if go.mode.link in LINKMODES_EXECUTABLE:
+ env = {}
+ for k, v in ctx.attr.env.items():
+ env[k] = ctx.expand_location(v, ctx.attr.data)
+ providers.append(RunEnvironmentInfo(environment = env))
+
+ # The executable is automatically added to the runfiles.
+ providers.append(DefaultInfo(
+ files = depset([executable]),
+ runfiles = runfiles,
+ executable = executable,
+ ))
+ else:
+ # Workaround for https://github.com/bazelbuild/bazel/issues/15043
+ # As of Bazel 5.1.1, native rules do not pick up the "files" of a data
+ # dependency's DefaultInfo, only the "data_runfiles". Since transitive
+ # non-data dependents should not pick up the executable as a runfile
+ # implicitly, the deprecated "default_runfiles" and "data_runfiles"
+ # constructor parameters have to be used.
+ providers.append(DefaultInfo(
+ files = depset([executable]),
+ default_runfiles = runfiles,
+ data_runfiles = runfiles.merge(ctx.runfiles([executable])),
+ ))
+
+ # If the binary's linkmode is c-archive or c-shared, expose CcInfo
+ if go.cgo_tools and go.mode.link in (LINKMODE_C_ARCHIVE, LINKMODE_C_SHARED):
+ cc_import_kwargs = {
+ "linkopts": {
+ "darwin": [],
+ "ios": [],
+ "windows": ["-mthreads"],
+ }.get(go.mode.goos, ["-pthread"]),
+ }
+ cgo_exports = archive.cgo_exports.to_list()
+ if cgo_exports:
+ header = ctx.actions.declare_file("{}.h".format(name))
+ ctx.actions.symlink(
+ output = header,
+ target_file = cgo_exports[0],
+ )
+ cc_import_kwargs["hdrs"] = depset([header])
+ if go.mode.link == LINKMODE_C_SHARED:
+ cc_import_kwargs["dynamic_library"] = executable
+ elif go.mode.link == LINKMODE_C_ARCHIVE:
+ cc_import_kwargs["static_library"] = executable
+ cc_import_kwargs["alwayslink"] = True
+ ccinfo = new_cc_import(go, **cc_import_kwargs)
+ ccinfo = cc_common.merge_cc_infos(
+ cc_infos = [ccinfo, source.cc_info],
+ )
+ providers.append(ccinfo)
+
+ return providers
+
+_go_binary_kwargs = {
+ "implementation": _go_binary_impl,
+ "attrs": {
+ "srcs": attr.label_list(
+ allow_files = go_exts + asm_exts + cgo_exts,
+ doc = """The list of Go source files that are compiled to create the package.
+ Only `.go` and `.s` files are permitted, unless the `cgo`
+ attribute is set, in which case,
+ `.c .cc .cpp .cxx .h .hh .hpp .hxx .inc .m .mm`
+ files are also permitted. Files may be filtered at build time
+ using Go [build constraints].
+ """,
+ ),
+ "data": attr.label_list(
+ allow_files = True,
+ doc = """List of files needed by this rule at run-time. This may include data files
+ needed or other programs that may be executed. The [bazel] package may be
+ used to locate run files; they may appear in different places depending on the
+ operating system and environment. See [data dependencies] for more
+ information on data files.
+ """,
+ ),
+ "deps": attr.label_list(
+ providers = [GoLibrary],
+ doc = """List of Go libraries this package imports directly.
+ These may be `go_library` rules or compatible rules with the [GoLibrary] provider.
+ """,
+ cfg = go_transition,
+ ),
+ "embed": attr.label_list(
+ providers = [GoLibrary],
+ doc = """List of Go libraries whose sources should be compiled together with this
+ binary's sources. Labels listed here must name `go_library`,
+ `go_proto_library`, or other compatible targets with the [GoLibrary] and
+ [GoSource] providers. Embedded libraries must all have the same `importpath`,
+ which must match the `importpath` for this `go_binary` if one is
+ specified. At most one embedded library may have `cgo = True`, and the
+ embedding binary may not also have `cgo = True`. See [Embedding] for
+ more information.
+ """,
+ cfg = go_transition,
+ ),
+ "embedsrcs": attr.label_list(
+ allow_files = True,
+ doc = """The list of files that may be embedded into the compiled package using
+ `//go:embed` directives. All files must be in the same logical directory
+ or a subdirectory as source files. All source files containing `//go:embed`
+ directives must be in the same logical directory. It's okay to mix static and
+ generated source files and static and generated embeddable files.
+ """,
+ ),
+ "env": attr.string_dict(
+ doc = """Environment variables to set when the binary is executed with bazel run.
+ The values (but not keys) are subject to
+ [location expansion](https://docs.bazel.build/versions/main/skylark/macros.html) but not full
+ [make variable expansion](https://docs.bazel.build/versions/main/be/make-variables.html).
+ """,
+ ),
+ "importpath": attr.string(
+ doc = """The import path of this binary. Binaries can't actually be imported, but this
+ may be used by [go_path] and other tools to report the location of source
+ files. This may be inferred from embedded libraries.
+ """,
+ ),
+ "gc_goopts": attr.string_list(
+ doc = """List of flags to add to the Go compilation command when using the gc compiler.
+ Subject to ["Make variable"] substitution and [Bourne shell tokenization].
+ """,
+ ),
+ "gc_linkopts": attr.string_list(
+ doc = """List of flags to add to the Go link command when using the gc compiler.
+ Subject to ["Make variable"] substitution and [Bourne shell tokenization].
+ """,
+ ),
+ "x_defs": attr.string_dict(
+ doc = """Map of defines to add to the go link command.
+ See [Defines and stamping] for examples of how to use these.
+ """,
+ ),
+ "basename": attr.string(
+ doc = """The basename of this binary. The binary
+ basename may also be platform-dependent: on Windows, we add an .exe extension.
+ """,
+ ),
+ "out": attr.string(
+ doc = """Sets the output filename for the generated executable. When set, `go_binary`
+ will write this file without mode-specific directory prefixes, without
+ linkmode-specific prefixes like "lib", and without platform-specific suffixes
+ like ".exe". Note that without a mode-specific directory prefix, the
+ output file (but not its dependencies) will be invalidated in Bazel's cache
+ when changing configurations.
+ """,
+ ),
+ "cgo": attr.bool(
+ doc = """If `True`, the package may contain [cgo] code, and `srcs` may contain
+ C, C++, Objective-C, and Objective-C++ files and non-Go assembly files.
+ When cgo is enabled, these files will be compiled with the C/C++ toolchain
+ and included in the package. Note that this attribute does not force cgo
+ to be enabled. Cgo is enabled for non-cross-compiling builds when a C/C++
+ toolchain is configured.
+ """,
+ ),
+ "cdeps": attr.label_list(
+ doc = """The list of other libraries that the c code depends on.
+ This can be anything that would be allowed in [cc_library deps]
+ Only valid if `cgo` = `True`.
+ """,
+ ),
+ "cppopts": attr.string_list(
+ doc = """List of flags to add to the C/C++ preprocessor command.
+ Subject to ["Make variable"] substitution and [Bourne shell tokenization].
+ Only valid if `cgo` = `True`.
+ """,
+ ),
+ "copts": attr.string_list(
+ doc = """List of flags to add to the C compilation command.
+ Subject to ["Make variable"] substitution and [Bourne shell tokenization].
+ Only valid if `cgo` = `True`.
+ """,
+ ),
+ "cxxopts": attr.string_list(
+ doc = """List of flags to add to the C++ compilation command.
+ Subject to ["Make variable"] substitution and [Bourne shell tokenization].
+ Only valid if `cgo` = `True`.
+ """,
+ ),
+ "clinkopts": attr.string_list(
+ doc = """List of flags to add to the C link command.
+ Subject to ["Make variable"] substitution and [Bourne shell tokenization].
+ Only valid if `cgo` = `True`.
+ """,
+ ),
+ "pure": attr.string(
+ default = "auto",
+ doc = """Controls whether cgo source code and dependencies are compiled and linked,
+ similar to setting `CGO_ENABLED`. May be one of `on`, `off`,
+ or `auto`. If `auto`, pure mode is enabled when no C/C++
+ toolchain is configured or when cross-compiling. It's usually better to
+ control this on the command line with
+ `--@io_bazel_rules_go//go/config:pure`. See [mode attributes], specifically
+ [pure].
+ """,
+ ),
+ "static": attr.string(
+ default = "auto",
+ doc = """Controls whether a binary is statically linked. May be one of `on`,
+ `off`, or `auto`. Not available on all platforms or in all
+ modes. It's usually better to control this on the command line with
+ `--@io_bazel_rules_go//go/config:static`. See [mode attributes],
+ specifically [static].
+ """,
+ ),
+ "race": attr.string(
+ default = "auto",
+ doc = """Controls whether code is instrumented for race detection. May be one of
+ `on`, `off`, or `auto`. Not available when cgo is
+ disabled. In most cases, it's better to control this on the command line with
+ `--@io_bazel_rules_go//go/config:race`. See [mode attributes], specifically
+ [race].
+ """,
+ ),
+ "msan": attr.string(
+ default = "auto",
+ doc = """Controls whether code is instrumented for memory sanitization. May be one of
+ `on`, `off`, or `auto`. Not available when cgo is
+ disabled. In most cases, it's better to control this on the command line with
+ `--@io_bazel_rules_go//go/config:msan`. See [mode attributes], specifically
+ [msan].
+ """,
+ ),
+ "gotags": attr.string_list(
+ doc = """Enables a list of build tags when evaluating [build constraints]. Useful for
+ conditional compilation.
+ """,
+ ),
+ "goos": attr.string(
+ default = "auto",
+ doc = """Forces a binary to be cross-compiled for a specific operating system. It's
+ usually better to control this on the command line with `--platforms`.
+
+ This disables cgo by default, since a cross-compiling C/C++ toolchain is
+ rarely available. To force cgo, set `pure` = `off`.
+
+ See [Cross compilation] for more information.
+ """,
+ ),
+ "goarch": attr.string(
+ default = "auto",
+ doc = """Forces a binary to be cross-compiled for a specific architecture. It's usually
+ better to control this on the command line with `--platforms`.
+
+ This disables cgo by default, since a cross-compiling C/C++ toolchain is
+ rarely available. To force cgo, set `pure` = `off`.
+
+ See [Cross compilation] for more information.
+ """,
+ ),
+ "linkmode": attr.string(
+ default = LINKMODE_NORMAL,
+ doc = """Determines how the binary should be built and linked. This accepts some of
+ the same values as `go build -buildmode` and works the same way.
+ <br><br>
+ <ul>
+ <li>`normal`: Builds a normal executable with position-dependent code.</li>
+ <li>`pie`: Builds a position-independent executable.</li>
+ <li>`plugin`: Builds a shared library that can be loaded as a Go plugin. Only supported on platforms that support plugins.</li>
+ <li>`c-shared`: Builds a shared library that can be linked into a C program.</li>
+ <li>`c-archive`: Builds an archive that can be linked into a C program.</li>
+ </ul>
+ """,
+ ),
+ "_go_context_data": attr.label(default = "//:go_context_data", cfg = go_transition),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ "toolchains": [GO_TOOLCHAIN],
+ "doc": """This builds an executable from a set of source files,
+ which must all be in the `main` package. You can run the binary with
+ `bazel run`, or you can build it with `bazel build` and run it directly.<br><br>
+ ***Note:*** `name` should be the same as the desired name of the generated binary.<br><br>
+ **Providers:**
+ <ul>
+ <li>[GoLibrary]</li>
+ <li>[GoSource]</li>
+ <li>[GoArchive]</li>
+ </ul>
+ """,
+}
+
+go_binary = rule(executable = True, **_go_binary_kwargs)
+go_non_executable_binary = rule(executable = False, **_go_binary_kwargs)
+
+def _go_tool_binary_impl(ctx):
+ sdk = ctx.attr.sdk[GoSDK]
+ name = ctx.label.name
+ if sdk.goos == "windows":
+ name += ".exe"
+
+ out = ctx.actions.declare_file(name)
+ if sdk.goos == "windows":
+ gopath = ctx.actions.declare_directory("gopath")
+ gocache = ctx.actions.declare_directory("gocache")
+ cmd = "@echo off\nset GOMAXPROCS=1\nset GOCACHE=%cd%\\{gocache}\nset GOPATH=%cd%\\{gopath}\n{go} build -o {out} -trimpath {srcs}".format(
+ gopath = gopath.path,
+ gocache = gocache.path,
+ go = sdk.go.path.replace("/", "\\"),
+ out = out.path,
+ srcs = " ".join([f.path for f in ctx.files.srcs]),
+ )
+ bat = ctx.actions.declare_file(name + ".bat")
+ ctx.actions.write(
+ output = bat,
+ content = cmd,
+ )
+ ctx.actions.run(
+ executable = bat,
+ inputs = sdk.headers + sdk.tools + sdk.srcs + ctx.files.srcs + [sdk.go],
+ outputs = [out, gopath, gocache],
+ mnemonic = "GoToolchainBinaryBuild",
+ )
+ else:
+ # Note: GOPATH is needed for Go 1.16.
+ cmd = "GOMAXPROCS=1 GOCACHE=$(mktemp -d) GOPATH=$(mktemp -d) {go} build -o {out} -trimpath {srcs}".format(
+ go = sdk.go.path,
+ out = out.path,
+ srcs = " ".join([f.path for f in ctx.files.srcs]),
+ )
+ ctx.actions.run_shell(
+ command = cmd,
+ inputs = sdk.headers + sdk.tools + sdk.srcs + sdk.libs + ctx.files.srcs + [sdk.go],
+ outputs = [out],
+ mnemonic = "GoToolchainBinaryBuild",
+ )
+
+ return [DefaultInfo(
+ files = depset([out]),
+ executable = out,
+ )]
+
+go_tool_binary = rule(
+ implementation = _go_tool_binary_impl,
+ attrs = {
+ "srcs": attr.label_list(
+ allow_files = True,
+ doc = "Source files for the binary. Must be in 'package main'.",
+ ),
+ "sdk": attr.label(
+ mandatory = True,
+ providers = [GoSDK],
+ doc = "The SDK containing tools and libraries to build this binary",
+ ),
+ },
+ executable = True,
+ doc = """Used instead of go_binary for executables used in the toolchain.
+
+go_tool_binary depends on tools and libraries that are part of the Go SDK.
+It does not depend on other toolchains. It can only compile binaries that
+just have a main package and only depend on the standard library and don't
+require build constraints.
+""",
+)
+
+def gc_linkopts(ctx):
+ gc_linkopts = [
+ ctx.expand_make_variables("gc_linkopts", f, {})
+ for f in ctx.attr.gc_linkopts
+ ]
+ return gc_linkopts