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