blob: 90caa5158226cd8d8817cf41664240e1c1ddf5ac [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",
"sets",
"to_set",
)
load("@io_bazel_rules_go//go/private:mode.bzl",
"LINKMODE_NORMAL",
)
def emit_link(go,
archive = None,
executable = None,
gc_linkopts = [],
x_defs = {},
linkstamp=None,
version_file=None,
info_file=None):
"""See go/toolchains.rst#link for full documentation."""
if archive == None: fail("archive is a required parameter")
if executable == None: fail("executable is a required parameter")
#TODO: There has to be a better way to work out the rpath
config_strip = len(go._ctx.configuration.bin_dir.path) + 1
pkg_depth = executable.dirname[config_strip:].count('/') + 1
ld = None
extldflags = []
if go.stdlib.cgo_tools:
ld = go.stdlib.cgo_tools.compiler_executable
extldflags.extend(go.stdlib.cgo_tools.options)
extldflags.extend(["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth)])
gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags)
# Add in any mode specific behaviours
if archive.source.mode.race:
gc_linkopts.append("-race")
if archive.source.mode.msan:
gc_linkopts.append("-msan")
if archive.source.mode.static:
gc_linkopts.extend(["-linkmode", "external"])
extldflags.append("-static")
if archive.source.mode.link != LINKMODE_NORMAL:
fail("Link mode {} is not yet supported".format(archive.source.mode.link))
link_opts = ["-L", "."]
for p in archive.searchpaths: #TODO delay this depset expansion:
link_opts.extend(["-L", p])
for d in archive.cgo_deps:
if d.basename.endswith('.so'):
short_dir = d.dirname[len(d.root.path):]
extldflags.extend(["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + short_dir])
link_opts.extend(["-o", executable.path])
link_opts.extend(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.extend(["-X", "%s=%s" % (k, v)])
link_opts.extend(go.toolchain.flags.link)
if archive.source.mode.strip:
link_opts.extend(["-w"])
if ld:
link_opts.extend([
"-extld", ld,
"-extldflags", " ".join(extldflags),
])
link_opts.append(archive.data.file.path)
link_args = go.args(go)
# Stamping support
stamp_inputs = []
if stamp_x_defs or linkstamp:
stamp_inputs = [info_file, version_file]
link_args.add(stamp_inputs, before_each="-stamp")
for k,v in stamp_x_defs.items():
link_args.add(["-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 linkstamp:
link_args.add(["-linkstamp", linkstamp])
link_args.add("--")
link_args.add(link_opts)
go.actions.run(
inputs = sets.union(archive.libs, archive.cgo_deps,
go.crosstool, stamp_inputs, go.stdlib.files),
outputs = [executable],
mnemonic = "GoLink",
executable = go.toolchain.tools.link,
arguments = [link_args],
)
def bootstrap_link(go,
archive = None,
executable = None,
gc_linkopts = [],
x_defs = {},
linkstamp=None,
version_file=None,
info_file=None):
"""See go/toolchains.rst#link for full documentation."""
if archive == None: fail("archive is a required parameter")
if executable == None: fail("executable is a required parameter")
if x_defs: fail("link does not accept x_defs in bootstrap mode")
inputs = depset([archive.data.file])
args = ["tool", "link", "-o", executable.path]
args.extend(gc_linkopts)
args.append(archive.data.file.path)
go.actions.run_shell(
inputs = inputs + go.stdlib.files,
outputs = [executable],
mnemonic = "GoLink",
command = "export GOROOT=$(pwd)/{} && {} {}".format(go.stdlib.root_file.dirname, go.stdlib.go.path, " ".join(args)),
)
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.append(opt)
elif opt == "-extldflags":
is_extldflags = True
else:
filtered_gc_linkopts.append(opt)
return filtered_gc_linkopts, extldflags