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