Skip to content

Commit

Permalink
Combine extldflags lists when linking
Browse files Browse the repository at this point in the history
_emit_go_link_action now scans gc_linkopts for -extldflags
arguments. The Go linker will only use the last list passed to it
(earlier lists are silently ignored), so we combine these into one
external flags list.

Fixes bazel-contrib#305
  • Loading branch information
Jay Conrod committed Mar 14, 2017
1 parent f6952a9 commit ef302ea
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 5 deletions.
35 changes: 30 additions & 5 deletions go/def.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,32 @@ def _gc_linkopts(ctx):
gc_linkopts += ["-X", "%s='%s'" % (k, v)]
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, importmap, transitive_libs, cgo_deps, lib,
executable, gc_linkopts):
executable, gc_linkopts):
"""Sets up a symlink tree to libraries to link together."""
out_dir = executable.path + ".dir"
out_depth = out_dir.count('/') + 1
Expand All @@ -422,15 +446,16 @@ def _emit_go_link_action(ctx, importmap, transitive_libs, cgo_deps, lib,
ld = "%s" % ctx.fragments.cpp.compiler_executable
if ld[0] != '/':
ld = ('../' * out_depth) + ld
ldflags = _c_linker_options(ctx) + [
extldflags = _c_linker_options(ctx) + [
"-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth),
]
if prefix:
ldflags.append("-L" + prefix)
extldflags.append("-L" + prefix)
for d in cgo_deps:
if d.basename.endswith('.so'):
dirname = _short_path(d)[:-len(d.basename)]
ldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + dirname]
extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + dirname]
gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags)

link_cmd = [
('../' * out_depth) + ctx.file.go_tool.path,
Expand All @@ -447,7 +472,7 @@ def _emit_go_link_action(ctx, importmap, transitive_libs, cgo_deps, lib,

link_cmd += [
"-extld", ld,
"-extldflags", "'%s'" % " ".join(ldflags),
"-extldflags", "'%s'" % " ".join(extldflags),
main_archive,
]

Expand Down
37 changes: 37 additions & 0 deletions tests/extldflags_static_link/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
load("//go:def.bzl", "go_binary", "cgo_library")

cgo_library(
name = "cgo_default_library",
srcs = ["main.go"],
)

# Go binaries with Cgo code are dynamically linked by default.
go_binary(
name = "dynamic_binary",
library = ":cgo_default_library",
)

go_binary(
name = "static_binary",
gc_linkopts = [
"-extldflags",
"-static",
],
library = ":cgo_default_library",
)

sh_test(
name = "extldflags_link_test",
size = "small",
srcs = ["extldflags_link_test.sh"],
args = [
"$(location :dynamic_binary)",
"'dynamically linked'",
"$(location :static_binary)",
"'statically linked'",
],
data = [
":dynamic_binary",
":static_binary",
],
)
16 changes: 16 additions & 0 deletions tests/extldflags_static_link/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
This test checks that flags passed to the external linker with -extldflags
through gc_linkopts are correctly combined with other flags passed through
-extldflags.

`go tool link` only uses the last set of flags passed with -extldflags. If more
than one set of flags is passed, the last set is passed to the external linker,
and the rest are silently ignored.

`go_binary` and `go_test` should look for -extldflags in gc_linkopts and
combine the flags into a single list passed to `go tool link`. This includes
flags generated by the rules themselves.

This test checks this behavior by passing "-extldflags -static", which has an
observable effect on a `go_binary` with cgo code (which is linked dynamically by
default). This set of flags is combined with the linker flags generated by the
Go rules themselves.
14 changes: 14 additions & 0 deletions tests/extldflags_static_link/extldflags_link_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

result=0
while [ $# -gt 0 ]; do
path=$1
pattern=$2
shift 2
if ! file --dereference "$path" | grep --quiet "$pattern"; then
echo "file '$path' did not match pattern '$pattern'" >&1
result=1
fi
done

exit $result
13 changes: 13 additions & 0 deletions tests/extldflags_static_link/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

/*
int foo() {
return 42;
}
*/
import "C"
import "fmt"

func main() {
fmt.Printf("%d\n", int(C.foo()))
}

0 comments on commit ef302ea

Please sign in to comment.