...

Text file src/github.com/bazelbuild/rules_go/go/private/rules/binary.bzl

Documentation: github.com/bazelbuild/rules_go/go/private/rules

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

View as plain text