Skip to content

Commit

Permalink
Allow pure go cross compilation within a single build (bazel-contrib#…
Browse files Browse the repository at this point in the history
…1114)

This adds goos and goarch attributes to a go_binary
If specified they force the binary to cross compile to the specified
architecture.
It also adds a test that uses this to cross compile and verify the binary was
correctly cross compiled.
This also required multiple rules for the same binary in different modes, which
meant we needed control over the binary file name (so not using the rule name)

Fixes bazel-contrib#1104
Fixes bazel-contrib#1030
  • Loading branch information
ianthehat authored Dec 7, 2017
1 parent cdbe33c commit 7b0091d
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 61 deletions.
4 changes: 2 additions & 2 deletions go/private/actions/action.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ def add_go_env(args, stdlib, mode):
"-cgo=" + ("0" if mode.pure else "1"),
])

def bootstrap_action(ctx, go_toolchain, inputs, outputs, mnemonic, arguments):
stdlib = go_toolchain.stdlib.cgo
def bootstrap_action(ctx, go_toolchain, mode, inputs, outputs, mnemonic, arguments):
stdlib = go_toolchain.stdlib.get(ctx, go_toolchain, mode)
ctx.actions.run_shell(
inputs = inputs + stdlib.files,
outputs = outputs,
Expand Down
2 changes: 1 addition & 1 deletion go/private/actions/binary.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def emit_binary(ctx, go_toolchain,
importpath = importpath,
importable = False,
)
executable = declare_file(ctx, ext=go_toolchain.data.extension, mode=mode)
executable = declare_file(ctx, name=name, ext=go_toolchain.data.extension, mode=mode)
go_toolchain.actions.link(ctx,
go_toolchain = go_toolchain,
archive=goarchive,
Expand Down
2 changes: 1 addition & 1 deletion go/private/actions/compile.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def bootstrap_compile(ctx, go_toolchain,
args = ["tool", "compile", "-o", out_lib.path]
args.extend(gc_goopts)
args.extend([s.path for s in sources])
bootstrap_action(ctx, go_toolchain,
bootstrap_action(ctx, go_toolchain, mode,
inputs = sources,
outputs = [out_lib],
mnemonic = "GoCompile",
Expand Down
2 changes: 1 addition & 1 deletion go/private/actions/link.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def bootstrap_link(ctx, go_toolchain,
args = ["tool", "link", "-o", executable.path]
args.extend(gc_linkopts)
args.append(archive.data.file.path)
bootstrap_action(ctx, go_toolchain,
bootstrap_action(ctx, go_toolchain, mode,
inputs = inputs,
outputs = [executable],
mnemonic = "GoCompile",
Expand Down
14 changes: 7 additions & 7 deletions go/private/common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ def to_set(v):
fail("Do not pass a depset to to_set")
return depset(v)

def declare_file(ctx, path="", ext="", mode=None):
name = ""
def declare_file(ctx, path="", ext="", mode=None, name = ""):
filename = ""
if mode:
name += mode_string(mode) + "/"
name += ctx.label.name
filename += mode_string(mode) + "/"
filename += name if name else ctx.label.name
if path:
name += "~/" + path
filename += "~/" + path
if ext:
name += ext
return ctx.actions.declare_file(name)
filename += ext
return ctx.actions.declare_file(filename)
53 changes: 24 additions & 29 deletions go/private/go_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ load("@io_bazel_rules_go//go/private:actions/library.bzl", "emit_library")
load("@io_bazel_rules_go//go/private:actions/link.bzl", "emit_link", "bootstrap_link")
load("@io_bazel_rules_go//go/private:actions/pack.bzl", "emit_pack")
load("@io_bazel_rules_go//go/private:providers.bzl", "GoStdLib")
load("@io_bazel_rules_go//go/platform:list.bzl", "GOOS_GOARCH")
load("@io_bazel_rules_go//go/private:mode.bzl", "mode_string")

def _get_stdlib(ctx, go_toolchain, mode):
if mode.race and mode.pure:
return go_toolchain.stdlib.pure_race
elif mode.pure:
return go_toolchain.stdlib.pure
elif mode.race:
return go_toolchain.stdlib.cgo_race
else:
return go_toolchain.stdlib.cgo
for stdlib in go_toolchain.stdlib.all:
stdlib = stdlib[GoStdLib]
if (stdlib.goos == mode.goos and
stdlib.goarch == mode.goarch and
stdlib.race == mode.race and
stdlib.pure == mode.pure):
return stdlib
fail("No matching standard library for "+mode_string(mode))

def _goos_to_extension(goos):
if goos == "windows":
Expand All @@ -43,13 +45,10 @@ def _go_toolchain_impl(ctx):
return [platform_common.ToolchainInfo(
name = ctx.label.name,
cross_compile = ctx.attr.cross_compile,
goos = ctx.attr.goos,
goarch = ctx.attr.goarch,
default_goos = ctx.attr.goos,
default_goarch = ctx.attr.goarch,
stdlib = struct(
cgo = ctx.attr._stdlib_cgo[GoStdLib],
pure = ctx.attr._stdlib_pure[GoStdLib],
cgo_race = ctx.attr._stdlib_cgo_race[GoStdLib],
pure_race = ctx.attr._stdlib_pure_race[GoStdLib],
all = ctx.attr._stdlib_all,
get = _get_stdlib,
),
actions = struct(
Expand Down Expand Up @@ -83,17 +82,16 @@ def _go_toolchain_impl(ctx):
),
)]

def _stdlib_cgo(goos, goarch):
return Label("@go_stdlib_{}_{}_cgo".format(goos, goarch))

def _stdlib_pure(goos, goarch):
return Label("@go_stdlib_{}_{}_pure".format(goos, goarch))

def _stdlib_cgo_race(goos, goarch):
return Label("@go_stdlib_{}_{}_cgo_race".format(goos, goarch))

def _stdlib_pure_race(goos, goarch):
return Label("@go_stdlib_{}_{}_pure_race".format(goos, goarch))
def _stdlib_all():
stdlibs = []
for goos, goarch in GOOS_GOARCH:
stdlibs.extend([
Label("@go_stdlib_{}_{}_cgo".format(goos, goarch)),
Label("@go_stdlib_{}_{}_pure".format(goos, goarch)),
Label("@go_stdlib_{}_{}_cgo_race".format(goos, goarch)),
Label("@go_stdlib_{}_{}_pure_race".format(goos, goarch)),
])
return stdlibs

def _asm(bootstrap):
if bootstrap:
Expand Down Expand Up @@ -150,10 +148,7 @@ _go_toolchain = rule(
"_test_generator": attr.label(allow_files = True, single_file = True, executable = True, cfg = "host", default = _test_generator),
"_cover": attr.label(allow_files = True, single_file = True, executable = True, cfg = "host", default = _cover),
# Hidden internal attributes
"_stdlib_cgo": attr.label(allow_files = True, default = _stdlib_cgo),
"_stdlib_pure": attr.label(allow_files = True, default = _stdlib_pure),
"_stdlib_cgo_race": attr.label(allow_files = True, default = _stdlib_cgo_race),
"_stdlib_pure_race": attr.label(allow_files = True, default = _stdlib_pure_race),
"_stdlib_all": attr.label_list(default = _stdlib_all()),
"_crosstool": attr.label(default=Label("//tools/defaults:crosstool")),
"_package_list": attr.label(allow_files = True, single_file = True, default="@go_sdk//:packages.txt"),
},
Expand Down
53 changes: 34 additions & 19 deletions go/private/mode.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ def get_mode(ctx, toolchain_flags):
force_pure = go_toolchain.cross_compile

#TODO: allow link mode selection
static = _ternary(
getattr(ctx.attr, "static", None),
"static" in ctx.features,
)
race = _ternary(
getattr(ctx.attr, "race", None),
"race" in ctx.features,
)
msan = _ternary(
getattr(ctx.attr, "msan", None),
"msan" in ctx.features,
)
pure = _ternary(
getattr(ctx.attr, "pure", None),
force_pure,
"pure" in ctx.features,
)
debug = ctx.var["COMPILATION_MODE"] == "debug"
strip_mode = "sometimes"
if toolchain_flags:
Expand All @@ -68,27 +85,25 @@ def get_mode(ctx, toolchain_flags):
strip = True
elif strip_mode == "sometimes":
strip = not debug
goos = getattr(ctx.attr, "goos", None)
if goos == None or goos == "auto":
goos = go_toolchain.default_goos
elif not pure:
fail("If goos is set, pure must be true")
goarch = getattr(ctx.attr, "goarch", None)
if goarch == None or goarch == "auto":
goarch = go_toolchain.default_goarch
elif not pure:
fail("If goarch is set, pure must be true")

return struct(
static = _ternary(
getattr(ctx.attr, "static", None),
"static" in ctx.features,
),
race = _ternary(
getattr(ctx.attr, "race", None),
"race" in ctx.features,
),
msan = _ternary(
getattr(ctx.attr, "msan", None),
"msan" in ctx.features,
),
pure = _ternary(
getattr(ctx.attr, "pure", None),
force_pure,
"pure" in ctx.features,
),
static = static,
race = race,
msan = msan,
pure = pure,
link = LINKMODE_NORMAL,
debug = debug,
strip = strip,
goos = go_toolchain.goos,
goarch = go_toolchain.goarch,
goos = goos,
goarch = goarch,
)
6 changes: 6 additions & 0 deletions go/private/rules/aspect.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ load("@io_bazel_rules_go//go/private:providers.bzl",
"GoArchiveData",
"sources",
)
load("@io_bazel_rules_go//go/platform:list.bzl",
"GOOS",
"GOARCH",
)

GoAspectProviders = provider()

Expand Down Expand Up @@ -99,6 +103,8 @@ go_archive_aspect = aspect(
"static": attr.string(values=["on", "off", "auto"]),
"msan": attr.string(values=["on", "off", "auto"]),
"race": attr.string(values=["on", "off", "auto"]),
"goos": attr.string(values=GOOS.keys() + ["auto"], default="auto"),
"goarch": attr.string(values=GOARCH.keys() + ["auto"], default="auto"),
},
toolchains = ["@io_bazel_rules_go//go:toolchain"],
)
13 changes: 12 additions & 1 deletion go/private/rules/binary.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ load("@io_bazel_rules_go//go/private:providers.bzl",
"GoSourceList",
"sources",
)
load("@io_bazel_rules_go//go/platform:list.bzl",
"GOOS",
"GOARCH",
)

def _go_binary_impl(ctx):
"""go_binary_impl emits actions for compiling and linking a go executable."""
Expand All @@ -36,8 +40,11 @@ def _go_binary_impl(ctx):
else:
go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:bootstrap_toolchain"]
gosource = collect_src(ctx)
name = ctx.attr.basename
if not name:
name = ctx.label.name
golib, goarchive, executable = go_toolchain.actions.binary(ctx, go_toolchain,
name = ctx.label.name,
name = name,
importpath = go_importpath(ctx),
source = gosource,
gc_linkopts = gc_linkopts(ctx),
Expand All @@ -55,6 +62,7 @@ def _go_binary_impl(ctx):
go_binary = rule(
_go_binary_impl,
attrs = {
"basename": attr.string(),
"data": attr.label_list(
allow_files = True,
cfg = "data",
Expand All @@ -67,6 +75,8 @@ go_binary = rule(
"static": attr.string(values=["on", "off", "auto"], default="auto"),
"race": attr.string(values=["on", "off", "auto"], default="auto"),
"msan": attr.string(values=["on", "off", "auto"], default="auto"),
"goos": attr.string(values=GOOS.keys() + ["auto"], default="auto"),
"goarch": attr.string(values=GOARCH.keys() + ["auto"], default="auto"),
"gc_goopts": attr.string_list(),
"gc_linkopts": attr.string_list(),
"linkstamp": attr.string(),
Expand All @@ -82,6 +92,7 @@ go_binary = rule(
go_tool_binary = rule(
_go_binary_impl,
attrs = {
"basename": attr.string(),
"data": attr.label_list(
allow_files = True,
cfg = "data",
Expand Down
2 changes: 2 additions & 0 deletions go/private/rules/stdlib.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ def _stdlib_impl(ctx):
root_file = root_file,
goos = ctx.attr.goos,
goarch = ctx.attr.goarch,
race = ctx.attr.race,
pure = not ctx.attr.cgo,
libs = [pkg],
headers = [pkg],
files = files,
Expand Down
44 changes: 44 additions & 0 deletions tests/cross/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test", "go_source")

go_source(
name = "cross",
srcs = ["main.go"],
)

go_binary(
name = "windows",
embed = [":cross"],
basename = "cross",
goos = "windows",
goarch = "amd64",
pure = "on",
)

go_binary(
name = "linux",
embed = [":cross"],
basename = "cross",
goos = "linux",
goarch = "amd64",
pure = "on",
)

go_binary(
name = "darwin",
embed = [":cross"],
basename = "cross",
goos = "darwin",
goarch = "amd64",
pure = "on",
)

go_test(
name = "cross_test",
size = "small",
srcs = ["cross_test.go"],
data = [
":linux",
":darwin",
":windows",
],
)
Loading

0 comments on commit 7b0091d

Please sign in to comment.