blob: 138c6ca84c1fdee2d680672ed6cac4633c8a80d6 [file] [log] [blame]
# 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("@io_bazel_rules_go//go/private:common.bzl",
"NORMAL_MODE",
"RACE_MODE",
"compile_modes",
"go_filetype",
)
load("@io_bazel_rules_go//go/private:library.bzl",
"emit_library_actions",
"get_library",
"get_searchpath",
"go_importpath",
"go_prefix_default",
)
load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary", "GoBinary")
def _go_binary_impl(ctx):
"""go_binary_impl emits actions for compiling and linking a go executable."""
go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:toolchain"]
golib, _ = emit_library_actions(ctx,
go_toolchain = go_toolchain,
srcs = ctx.files.srcs,
deps = ctx.attr.deps,
cgo_object = None,
library = ctx.attr.library,
want_coverage = False,
importpath = go_importpath(ctx),
)
# Default (dynamic) linking
race_executable = ctx.new_file(ctx.attr.name + ".race")
for mode in compile_modes:
executable = ctx.outputs.executable
if mode == RACE_MODE:
executable = race_executable
emit_go_link_action(
ctx,
go_toolchain = go_toolchain,
library=golib,
mode=mode,
executable=executable,
gc_linkopts=gc_linkopts(ctx),
x_defs=ctx.attr.x_defs,
)
# Static linking (in the 'static' output group)
static_linkopts = [
"-linkmode", "external",
"-extldflags", "-static",
]
static_executable = ctx.new_file(ctx.attr.name + ".static")
emit_go_link_action(
ctx,
go_toolchain = go_toolchain,
library=golib,
mode=NORMAL_MODE,
executable=static_executable,
gc_linkopts=gc_linkopts(ctx) + static_linkopts,
x_defs=ctx.attr.x_defs,
)
return [
golib,
GoBinary(
executable = ctx.outputs.executable,
static = static_executable,
race = race_executable,
),
DefaultInfo(
files = depset([ctx.outputs.executable]),
runfiles = golib.runfiles,
),
OutputGroupInfo(
static = depset([static_executable]),
race = depset([race_executable]),
),
]
go_binary = rule(
_go_binary_impl,
attrs = {
"data": attr.label_list(
allow_files = True,
cfg = "data",
),
"srcs": attr.label_list(allow_files = go_filetype),
"deps": attr.label_list(providers = [GoLibrary]),
"importpath": attr.string(),
"library": attr.label(providers = [GoLibrary]),
"gc_goopts": attr.string_list(),
"gc_linkopts": attr.string_list(),
"linkstamp": attr.string(),
"x_defs": attr.string_dict(),
"_go_prefix": attr.label(default = go_prefix_default),
},
executable = True,
toolchains = ["@io_bazel_rules_go//go:toolchain"],
)
def gc_linkopts(ctx):
gc_linkopts = [ctx.expand_make_variables("gc_linkopts", f, {})
for f in ctx.attr.gc_linkopts]
return gc_linkopts
def _extract_extldflags(gc_linkopts, extldflags):
"""Extracts -extldflags from gc_linkopts and combines them into a single list.
Args:
gc_linkopts: a list of flags passed in through the gc_linkopts attributes.
ctx.expand_make_variables should have already been applied.
extldflags: a list of flags to be passed to the external linker.
Return:
A tuple containing the filtered gc_linkopts with external flags removed,
and a combined list of external flags.
"""
filtered_gc_linkopts = []
is_extldflags = False
for opt in gc_linkopts:
if is_extldflags:
is_extldflags = False
extldflags += [opt]
elif opt == "-extldflags":
is_extldflags = True
else:
filtered_gc_linkopts += [opt]
return filtered_gc_linkopts, extldflags
def emit_go_link_action(ctx, go_toolchain, library, mode, executable, gc_linkopts, x_defs):
"""Adds an action to link the supplied library in the given mode, producing the executable.
Args:
ctx: The skylark Context.
library: The library to link.
mode: Controls the linking setup affecting things like enabling profilers and sanitizers.
This must be one of the values in common.bzl#compile_modes
executable: The binary to produce.
gc_linkopts: basic link options, these may be adjusted by the mode.
x_defs: link defines, including build stamping ones
"""
# Add in any mode specific behaviours
if mode == RACE_MODE:
gc_linkopts += ["-race"]
config_strip = len(ctx.configuration.bin_dir.path) + 1
pkg_depth = executable.dirname[config_strip:].count('/') + 1
ld = None
extldflags = []
if go_toolchain.external_linker:
ld = go_toolchain.external_linker.compiler_executable
extldflags = go_toolchain.external_linker.options
extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth)]
gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags)
link_opts = ["-L", "."]
libs = depset()
cgo_deps = depset()
for golib in depset([library]) + library.transitive:
libs += [get_library(golib, mode)]
link_opts += ["-L", get_searchpath(golib, mode)]
cgo_deps += golib.cgo_deps
for d in cgo_deps:
if d.basename.endswith('.so'):
short_dir = d.dirname[len(d.root.path):]
extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + short_dir]
link_opts += ["-o", executable.path] + gc_linkopts
# Process x_defs, either adding them directly to linker options, or
# saving them to process through stamping support.
stamp_x_defs = {}
for k, v in x_defs.items():
if v.startswith("{") and v.endswith("}"):
stamp_x_defs[k] = v[1:-1]
else:
link_opts += ["-X", "%s=%s" % (k, v)]
link_opts += go_toolchain.flags.link
if ld:
link_opts += [
"-extld", ld,
"-extldflags", " ".join(extldflags),
]
link_opts += [get_library(golib, mode).path]
link_args = [go_toolchain.tools.go.path]
# Stamping support
stamp_inputs = []
if stamp_x_defs or ctx.attr.linkstamp:
stamp_inputs = [ctx.info_file, ctx.version_file]
for f in stamp_inputs:
link_args += ["-stamp", f.path]
for k,v in stamp_x_defs.items():
link_args += ["-X", "%s=%s" % (k, v)]
# linkstamp option support: read workspace status files,
# converting "KEY value" lines to "-X $linkstamp.KEY=value" arguments
# to the go linker.
if ctx.attr.linkstamp:
link_args += ["-linkstamp", ctx.attr.linkstamp]
link_args += ["--"] + link_opts
ctx.action(
inputs = list(libs + cgo_deps +
go_toolchain.data.tools + go_toolchain.data.crosstool + stamp_inputs),
outputs = [executable],
mnemonic = "GoLink",
executable = go_toolchain.tools.link,
arguments = link_args,
env = go_toolchain.env,
)