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