From f2373c9fbd09586d8e591dda3c43d66445b2d7ca Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Thu, 13 Jun 2019 16:45:20 -0400 Subject: [PATCH] Add iOS toolchains, platforms, and constraint_values (#2090) Refactored the description of target platforms. The source of truth is now the PLATFORMS table go/private/platforms.bzl. Declarations for config_settings in //go/platform; constraint_values and aliases in //go/toolchain; and toolchains in @go_sdk are generated from this table. In addition to the normal GOOS_GOARCH list, there are some additional entries for iOS. iOS platforms still have GOOS=darwin, but they are compatible with a different constraint_value (@bazel_tools//platforms:ios instead of @bazel_tools//platforms:osx). There are also separate platforms for pure mode, and cgo (with _cgo suffix). Cross compilation may still be done with (for example) --platforms=@io_bazel_rules_go//go/platforms:linux_amd64, and this no longer requires a configured C/C++ toolchain. To cross-compile with cgo, use --platforms=@io_bazel_rules_go//go/platforms:linux_amd64_cgo. A compatible configured C/C++ toolchain is required in that case. Fixes #2079 --- BUILD.bazel | 11 +- go/platform/list.bzl | 123 +++++-------------- go/private/actions/archive.bzl | 66 ++++++----- go/private/actions/stdlib.bzl | 15 ++- go/private/compat/compat_repo.bzl | 7 ++ go/private/compat/darwin.bzl.tpl | 15 +++ go/private/context.bzl | 92 ++++++++++----- go/private/go_toolchain.bzl | 95 ++++++--------- go/private/mode.bzl | 34 ++++-- go/private/platforms.bzl | 190 ++++++++++++++++++++++++++++++ go/private/providers.bzl | 2 + go/private/sdk.bzl | 2 +- go/toolchain/toolchains.bzl | 110 +++++++++++++---- go/toolchains.rst | 24 ++-- go/tools/builders/stdlib.go | 5 + tests/bazel_tests.bzl | 2 +- tests/core/cross/BUILD.bazel | 52 ++++++-- tests/core/cross/README.rst | 9 ++ tests/core/cross/ios_bad.go | 2 + tests/core/cross/ios_good.go | 1 + 20 files changed, 583 insertions(+), 274 deletions(-) create mode 100644 go/private/compat/darwin.bzl.tpl create mode 100644 go/private/platforms.bzl create mode 100644 tests/core/cross/ios_bad.go create mode 100644 tests/core/cross/ios_good.go diff --git a/BUILD.bazel b/BUILD.bazel index 6f3b2390e2..49f6946f2c 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -2,7 +2,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_path") load("@io_bazel_rules_go//go/private:tools/lines_sorted_test.bzl", "lines_sorted_test") load("@io_bazel_rules_go//go/private:rules/nogo.bzl", "nogo") load("@io_bazel_rules_go//go/private:rules/info.bzl", "go_info") -load("@io_bazel_rules_go//go/private:context.bzl", "go_context_data") +load("@io_bazel_rules_go//go/private:context.bzl", "cgo_context_data", "go_context_data") load("@io_bazel_rules_go//go/private:rules/stdlib.bzl", "stdlib") stdlib( @@ -59,6 +59,8 @@ nogo( ], ) +# go_context_data collects build options and is depended on by all Go targets. +# It may depend on cgo_context_data via go_toolchain. go_context_data( name = "go_context_data", strip = select({ @@ -69,6 +71,13 @@ go_context_data( visibility = ["//visibility:public"], ) +# cgo_context_data collects information about the C/C++ toolchain. +# go_toolchains may depend on this in order to support cgo. +cgo_context_data( + name = "cgo_context_data", + visibility = ["//visibility:public"], +) + lines_sorted_test( name = "contributors_sorted_test", size = "small", diff --git a/go/platform/list.bzl b/go/platform/list.bzl index ec6ec79786..635dc2c169 100644 --- a/go/platform/list.bzl +++ b/go/platform/list.bzl @@ -12,113 +12,54 @@ # See the License for the specific language governing permissions and # limitations under the License. -GOOS = { - "android": None, - "darwin": "@bazel_tools//platforms:osx", - "dragonfly": None, - "freebsd": "@bazel_tools//platforms:freebsd", - "linux": "@bazel_tools//platforms:linux", - "nacl": None, - "netbsd": None, - "openbsd": None, - "plan9": None, - "solaris": None, - "windows": "@bazel_tools//platforms:windows", - "js": None, -} - -GOARCH = { - "386": "@bazel_tools//platforms:x86_32", - "amd64": "@bazel_tools//platforms:x86_64", - "amd64p32": None, - "arm": "@bazel_tools//platforms:arm", - "arm64": "@bazel_tools//platforms:aarch64", - "mips": None, - "mips64": None, - "mips64le": None, - "mipsle": None, - "ppc64": None, - "ppc64le": "@bazel_tools//platforms:ppc", - "s390x": "@bazel_tools//platforms:s390x", - "wasm": None, -} - -GOOS_GOARCH = ( - ("android", "386"), - ("android", "amd64"), - ("android", "arm"), - ("android", "arm64"), - ("darwin", "386"), - ("darwin", "amd64"), - ("darwin", "arm"), - ("darwin", "arm64"), - ("dragonfly", "amd64"), - ("freebsd", "386"), - ("freebsd", "amd64"), - ("freebsd", "arm"), - ("linux", "386"), - ("linux", "amd64"), - ("linux", "arm"), - ("linux", "arm64"), - ("linux", "mips"), - ("linux", "mips64"), - ("linux", "mips64le"), - ("linux", "mipsle"), - ("linux", "ppc64"), - ("linux", "ppc64le"), - ("linux", "s390x"), - ("nacl", "386"), - ("nacl", "amd64p32"), - ("nacl", "arm"), - ("netbsd", "386"), - ("netbsd", "amd64"), - ("netbsd", "arm"), - ("openbsd", "386"), - ("openbsd", "amd64"), - ("openbsd", "arm"), - ("plan9", "386"), - ("plan9", "amd64"), - ("plan9", "arm"), - ("solaris", "amd64"), - ("windows", "386"), - ("windows", "amd64"), - ("js", "wasm"), +load( + "@io_bazel_rules_go//go/private:platforms.bzl", + "PLATFORMS", + _GOARCH = "GOARCH_CONSTRAINTS", + _GOOS = "GOOS_CONSTRAINTS", + _GOOS_GOARCH = "GOOS_GOARCH", + _MSAN_GOOS_GOARCH = "MSAN_GOOS_GOARCH", + _RACE_GOOS_GOARCH = "RACE_GOOS_GOARCH", ) -RACE_GOOS_GOARCH = ( - ("darwin", "amd64"), - ("freebsd", "amd64"), - ("linux", "amd64"), - ("windows", "amd64"), -) +GOOS_GOARCH = _GOOS_GOARCH +GOOS = _GOOS +GOARCH = _GOARCH +RACE_GOOS_GOARCH = _RACE_GOOS_GOARCH +MSAN_GOOS_GOARCH = _MSAN_GOOS_GOARCH -MSAN_GOOS_GOARCH = ( - ("linux", "amd64"), -) +def _os_constraint(goos): + if goos == "darwin": + return "@io_bazel_rules_go//go/toolchain:is_darwin" + else: + return "@io_bazel_rules_go//go/toolchain:" + goos + +def _arch_constraint(goarch): + return "@io_bazel_rules_go//go/toolchain:" + goarch def declare_config_settings(): + """Generates config_setting targets for each goos, goarch, and valid + goos_goarch pair. These targets may be used in select expressions. + Each target refers to a corresponding constraint_value in //go/toolchain. + + Note that the "darwin" targets are true when building for either + macOS or iOS. + """ for goos in GOOS: native.config_setting( name = goos, - constraint_values = ["//go/toolchain:" + goos], + constraint_values = [_os_constraint(goos)], ) for goarch in GOARCH: native.config_setting( name = goarch, - constraint_values = ["//go/toolchain:" + goarch], + constraint_values = [_arch_constraint(goarch)], ) for goos, goarch in GOOS_GOARCH: native.config_setting( name = goos + "_" + goarch, constraint_values = [ - "//go/toolchain:" + goos, - "//go/toolchain:" + goarch, + _os_constraint(goos), + _arch_constraint(goarch), ], ) - -def generate_toolchain_names(): - # Keep in sync with generate_toolchains - return [ - "go_{}_{}".format(target_goos, target_goarch) - for target_goos, target_goarch in GOOS_GOARCH - ] diff --git a/go/private/actions/archive.bzl b/go/private/actions/archive.bzl index c0f0bec47b..db61006804 100644 --- a/go/private/actions/archive.bzl +++ b/go/private/actions/archive.bzl @@ -82,9 +82,7 @@ def emit_archive(go, source = None): cxxopts = [f for fs in source.cxxopts for f in fs.split(" ")] clinkopts = [f for fs in source.clinkopts for f in fs.split(" ")] - cgo_inputs = depset() - cgo_deps = depset() - if source.cgo: + if source.cgo and not go.mode.pure: cgo = cgo_configure( go, srcs = split.go + split.c + split.asm + split.cxx + split.headers, @@ -94,33 +92,43 @@ def emit_archive(go, source = None): cxxopts = cxxopts, clinkopts = clinkopts, ) - runfiles = runfiles.merge(cgo.runfiles) - cgo_inputs = cgo.inputs cgo_deps = cgo.deps - cppopts = cgo.cppopts - copts = cgo.copts - cxxopts = cgo.cxxopts - clinkopts = cgo.clinkopts - - emit_compilepkg( - go, - sources = split.go + split.c + split.asm + split.cxx + split.headers, - cover = source.cover, - importpath = effective_importpath_pkgpath(source.library)[0], - importmap = source.library.importmap, - archives = direct, - out_lib = out_lib, - out_export = out_export, - gc_goopts = source.gc_goopts, - cgo = source.cgo, - cgo_inputs = cgo_inputs, - cppopts = cppopts, - copts = copts, - cxxopts = cxxopts, - clinkopts = clinkopts, - cgo_archives = source.cgo_archives, - testfilter = testfilter, - ) + runfiles = runfiles.merge(cgo.runfiles) + emit_compilepkg( + go, + sources = split.go + split.c + split.asm + split.cxx + split.headers, + cover = source.cover, + importpath = effective_importpath_pkgpath(source.library)[0], + importmap = source.library.importmap, + archives = direct, + out_lib = out_lib, + out_export = out_export, + gc_goopts = source.gc_goopts, + cgo = True, + cgo_inputs = cgo.inputs, + cppopts = cgo.cppopts, + copts = cgo.copts, + cxxopts = cgo.cxxopts, + clinkopts = cgo.clinkopts, + cgo_archives = source.cgo_archives, + testfilter = testfilter, + ) + else: + cgo_deps = depset() + emit_compilepkg( + go, + sources = split.go + split.c + split.asm + split.cxx + split.headers, + cover = source.cover, + importpath = effective_importpath_pkgpath(source.library)[0], + importmap = source.library.importmap, + archives = direct, + out_lib = out_lib, + out_export = out_export, + gc_goopts = source.gc_goopts, + cgo = False, + cgo_archives = source.cgo_archives, + testfilter = testfilter, + ) else: cgo_deps = source.cgo_deps diff --git a/go/private/actions/stdlib.bzl b/go/private/actions/stdlib.bzl index 9ce4bd43d9..6ac245954a 100644 --- a/go/private/actions/stdlib.bzl +++ b/go/private/actions/stdlib.bzl @@ -66,11 +66,15 @@ def _build_stdlib(go): args.add_all(link_mode_args(go.mode)) go.actions.write(root_file, "") env = go.env - env.update({ - "CC": go.cgo_tools.c_compiler_path, - "CGO_CFLAGS": " ".join(go.cgo_tools.c_compile_options), - "CGO_LDFLAGS": " ".join(extldflags_from_cc_toolchain(go)), - }) + if go.mode.pure: + env.update({"CGO_ENABLED": "0"}) + else: + env.update({ + "CGO_ENABLED": "1", + "CC": go.cgo_tools.c_compiler_path, + "CGO_CFLAGS": " ".join(go.cgo_tools.c_compile_options), + "CGO_LDFLAGS": " ".join(extldflags_from_cc_toolchain(go)), + }) inputs = (go.sdk.srcs + go.sdk.headers + go.sdk.tools + @@ -89,4 +93,3 @@ def _build_stdlib(go): root_file = root_file, libs = [pkg], ) - diff --git a/go/private/compat/compat_repo.bzl b/go/private/compat/compat_repo.bzl index 1273753f50..0e89cd7d22 100644 --- a/go/private/compat/compat_repo.bzl +++ b/go/private/compat/compat_repo.bzl @@ -27,6 +27,13 @@ load("@io_bazel_rules_go//go/private:skylib/lib/versions.bzl", "versions") def _go_rules_compat_impl(ctx): + darwin_tpl_path = ctx.path(Label("@io_bazel_rules_go//go/private:compat/darwin.bzl.tpl")) + if "mac" in ctx.os.name: + default_darwin_constraint_value = "@io_bazel_rules_go//go/toolchain:is_darwin" + else: + default_darwin_constraint_value = "@io_bazel_rules_go//go/toolchain:not_darwin" + ctx.template("darwin.bzl", darwin_tpl_path, {"{default_darwin_constraint_value}": default_darwin_constraint_value}) + ctx.file("BUILD.bazel") ctx.symlink(ctx.attr.impl, "compat.bzl") diff --git a/go/private/compat/darwin.bzl.tpl b/go/private/compat/darwin.bzl.tpl new file mode 100644 index 0000000000..254a8c1afd --- /dev/null +++ b/go/private/compat/darwin.bzl.tpl @@ -0,0 +1,15 @@ +# Copyright 2019 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. + +DEFAULT_DARWIN_CONSTRAINT_VALUE = "{default_darwin_constraint_value}" diff --git a/go/private/context.bzl b/go/private/context.bzl index 23d10dde27..46ff54b8bb 100644 --- a/go/private/context.bzl +++ b/go/private/context.bzl @@ -30,6 +30,7 @@ load( ) load( "@io_bazel_rules_go//go/private:providers.bzl", + "CgoContextData", "EXPLICIT_PATH", "EXPORT_PATH", "GoLibrary", @@ -411,6 +412,66 @@ def go_context(ctx, attr = None): ) def _go_context_data_impl(ctx): + go_toolchain = ctx.toolchains["@io_bazel_rules_go//go:toolchain"] + if go_toolchain._cgo_context_data: + crosstool = go_toolchain._cgo_context_data.crosstool + env = dict(go_toolchain._cgo_context_data.env) + tags = go_toolchain._cgo_context_data.tags + cgo_tools = go_toolchain._cgo_context_data.cgo_tools + tool_paths = [ + cgo_tools.c_compiler_path, + cgo_tools.ld_executable_path, + cgo_tools.ld_static_lib_path, + cgo_tools.ld_dynamic_lib_path, + ] + else: + crosstool = [] + env = {} + tags = ctx.var["gotags"].split(",") if "gotags" in ctx.var else [] + cgo_tools = None + tool_paths = [] + + # Add C toolchain directories to PATH. + # On ARM, go tool link uses some features of gcc to complete its work, + # so PATH is needed on ARM. + path_set = {} + if "PATH" in env: + for p in env["PATH"].split(ctx.configuration.host_path_separator): + path_set[p] = None + for tool_path in tool_paths: + tool_dir, _, _ = tool_path.rpartition("/") + path_set[tool_dir] = None + paths = sorted(path_set.keys()) + if ctx.configuration.host_path_separator == ":": + # HACK: ":" is a proxy for a UNIX-like host. + # The tools returned above may be bash scripts that reference commands + # in directories we might not otherwise include. For example, + # on macOS, wrapped_ar calls dirname. + if "/bin" not in path_set: + paths.append("/bin") + if "/usr/bin" not in path_set: + paths.append("/usr/bin") + env["PATH"] = ctx.configuration.host_path_separator.join(paths) + + return [_GoContextData( + strip = ctx.attr.strip, + crosstool = crosstool, + tags = tags, + env = env, + cgo_tools = cgo_tools, + )] + +go_context_data = rule( + _go_context_data_impl, + attrs = { + "strip": attr.string(mandatory = True), + }, + toolchains = ["@io_bazel_rules_go//go:toolchain"], + doc = """go_context_data gathers information about the build configuration. +It is a common dependency of all Go targets.""", +) + +def _cgo_context_data_impl(ctx): # TODO(jayconrod): find a way to get a list of files that comprise the # toolchain (to be inputs into actions that need it). # ctx.files._cc_toolchain won't work when cc toolchain resolution @@ -542,30 +603,7 @@ def _go_context_data_impl(ctx): cc_toolchain.target_gnu_system_name, ) - # Add C toolchain directories to PATH. - # On ARM, go tool link uses some features of gcc to complete its work, - # so PATH is needed on ARM. - path_set = {} - if "PATH" in env: - for p in env["PATH"].split(ctx.configuration.host_path_separator): - path_set[p] = None - for tool_path in [c_compiler_path, ld_executable_path, ld_static_lib_path, ld_dynamic_lib_path]: - tool_dir, _, _ = tool_path.rpartition("/") - path_set[tool_dir] = None - paths = sorted(path_set.keys()) - if ctx.configuration.host_path_separator == ":": - # HACK: ":" is a proxy for a UNIX-like host. - # The tools returned above may be bash scripts that reference commands - # in directories we might not otherwise include. For example, - # on macOS, wrapped_ar calls dirname. - if "/bin" not in path_set: - paths.append("/bin") - if "/usr/bin" not in path_set: - paths.append("/usr/bin") - env["PATH"] = ctx.configuration.host_path_separator.join(paths) - - return [_GoContextData( - strip = ctx.attr.strip, + return [CgoContextData( crosstool = ctx.files._cc_toolchain, tags = tags, env = env, @@ -581,10 +619,9 @@ def _go_context_data_impl(ctx): ), )] -go_context_data = rule( - _go_context_data_impl, +cgo_context_data = rule( + implementation = _cgo_context_data_impl, attrs = { - "strip": attr.string(mandatory = True), "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"), "_xcode_config": attr.label( default = "@bazel_tools//tools/osx:current_xcode_config", @@ -592,4 +629,5 @@ go_context_data = rule( }, toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], fragments = ["apple", "cpp"], + provides = [CgoContextData], ) diff --git a/go/private/go_toolchain.bzl b/go/private/go_toolchain.bzl index 9e98fde8ba..349569e9eb 100644 --- a/go/private/go_toolchain.bzl +++ b/go/private/go_toolchain.bzl @@ -15,11 +15,8 @@ Toolchain rules used by go. """ -load( - "@io_bazel_rules_go//go/platform:list.bzl", - "GOOS_GOARCH", -) -load("@io_bazel_rules_go//go/private:providers.bzl", "GoSDK") +load("@io_bazel_rules_go//go/private:platforms.bzl", "PLATFORMS") +load("@io_bazel_rules_go//go/private:providers.bzl", "CgoContextData", "GoSDK") load("@io_bazel_rules_go//go/private:actions/archive.bzl", "emit_archive") load("@io_bazel_rules_go//go/private:actions/asm.bzl", "emit_asm") load("@io_bazel_rules_go//go/private:actions/binary.bzl", "emit_binary") @@ -57,9 +54,10 @@ def _go_toolchain_impl(ctx): # Internal fields -- may be read by emit functions. _builder = ctx.executable.builder, + _cgo_context_data = ctx.attr.cgo_context_data[CgoContextData] if ctx.attr.cgo_context_data else None, )] -_go_toolchain = rule( +go_toolchain = rule( _go_toolchain_impl, attrs = { # Minimum requirements to specify a toolchain @@ -83,6 +81,10 @@ _go_toolchain = rule( doc = "The SDK this toolchain is based on", ), # Optional extras to a toolchain + "cgo_context_data": attr.label( + providers = [CgoContextData], + doc = "A target that collects information about the C/C++ toolchain.", + ), "link_flags": attr.string_list( doc = "Flags passed to the Go internal linker", ), @@ -94,66 +96,41 @@ _go_toolchain = rule( provides = [platform_common.ToolchainInfo], ) -def go_toolchain(name, target, sdk, host = None, constraints = [], **kwargs): - """See go/toolchains.rst#go-toolchain for full documentation.""" - - if not host: - host = target - goos, _, goarch = target.partition("_") - target_constraints = constraints + [ - "@io_bazel_rules_go//go/toolchain:" + goos, - "@io_bazel_rules_go//go/toolchain:" + goarch, - ] - host_goos, _, host_goarch = host.partition("_") - exec_constraints = [ - "@io_bazel_rules_go//go/toolchain:" + host_goos, - "@io_bazel_rules_go//go/toolchain:" + host_goarch, - ] - - impl_name = name + "-impl" - _go_toolchain( - name = impl_name, - goos = goos, - goarch = goarch, - sdk = sdk, - tags = ["manual"], - visibility = ["//visibility:public"], - **kwargs - ) - native.toolchain( - name = name, - toolchain_type = "@io_bazel_rules_go//go:toolchain", - exec_compatible_with = exec_constraints, - target_compatible_with = target_constraints, - toolchain = ":" + impl_name, - ) - -def generate_toolchains(host, sdk, builder): +def declare_toolchains(host, sdk, builder): + # keep in sync with generate_toolchain_names host_goos, _, host_goarch = host.partition("_") - toolchains = [] - for target_goos, target_goarch in GOOS_GOARCH: - target = "{}_{}".format(target_goos, target_goarch) - toolchain_name = "go_" + target + for p in PLATFORMS: link_flags = [] cgo_link_flags = [] - if "darwin" in host: + if host_goos == "darwin": cgo_link_flags.extend(["-shared", "-Wl,-all_load"]) - if "linux" in host: + if host_goos == "linux": cgo_link_flags.append("-Wl,-whole-archive") - # Add the primary toolchain - toolchains.append(dict( - name = toolchain_name, - host = host, - target = target, + toolchain_name = "go_" + p.name + impl_name = toolchain_name + "-impl" + + cgo_context_data = "@io_bazel_rules_go//:cgo_context_data" if p.cgo else None + + go_toolchain( + name = impl_name, + goos = p.goos, + goarch = p.goarch, sdk = sdk, builder = builder, link_flags = link_flags, cgo_link_flags = cgo_link_flags, - )) - return toolchains - -def declare_toolchains(host, sdk, builder): - # Use the final dictionaries to create all the toolchains - for toolchain in generate_toolchains(host, sdk, builder): - go_toolchain(**toolchain) + cgo_context_data = cgo_context_data, + tags = ["manual"], + visibility = ["//visibility:public"], + ) + native.toolchain( + name = toolchain_name, + toolchain_type = "@io_bazel_rules_go//go:toolchain", + exec_compatible_with = [ + "@io_bazel_rules_go//go/toolchain:" + host_goos, + "@io_bazel_rules_go//go/toolchain:" + host_goarch, + ], + target_compatible_with = p.constraints, + toolchain = ":" + impl_name, + ) diff --git a/go/private/mode.bzl b/go/private/mode.bzl index ccb3e4f638..891b33e3de 100644 --- a/go/private/mode.bzl +++ b/go/private/mode.bzl @@ -78,17 +78,23 @@ def _ternary(*values): fail("_ternary failed to produce a final result from {}".format(values)) def get_mode(ctx, host_only, go_toolchain, go_context_data): - goos = getattr(ctx.attr, "goos", None) - if goos == None or goos == "auto": - goos = go_toolchain.default_goos - goarch = getattr(ctx.attr, "goarch", None) - if goarch == None or goarch == "auto": - goarch = go_toolchain.default_goarch + aspect_cross_compile = False + goos = go_toolchain.default_goos + attr_goos = getattr(ctx.attr, "goos", "auto") + if goos != attr_goos and attr_goos != "auto": + goos = attr_goos + aspect_cross_compile = True + goarch = go_toolchain.default_goarch + attr_goarch = getattr(ctx.attr, "goarch", "auto") + if goarch != attr_goarch and attr_goarch != "auto": + goarch = attr_goarch + aspect_cross_compile = True + + # We have to build in pure mode if we don't have a C toolchain. + # If we're cross-compiling using the aspect, we might have a C toolchain + # for the wrong platform, so also force pure mode then. + force_pure = "on" if not go_context_data.cgo_tools or aspect_cross_compile else "auto" - # We always have to use the pure stdlib in cross compilation mode - cross_compile = (goos != go_toolchain.sdk.goos or - goarch != go_toolchain.sdk.goarch) - force_pure = "on" if cross_compile else "auto" force_race = "off" if host_only else "auto" linkmode = getattr(ctx.attr, "linkmode", LINKMODE_NORMAL) @@ -230,7 +236,9 @@ def link_mode_args(mode): return args def extldflags_from_cc_toolchain(go): - if go.mode.link in (LINKMODE_SHARED, LINKMODE_PLUGIN, LINKMODE_C_SHARED): + if not go.cgo_tools: + return [] + elif go.mode.link in (LINKMODE_SHARED, LINKMODE_PLUGIN, LINKMODE_C_SHARED): return go.cgo_tools.ld_dynamic_lib_options else: # NOTE: in c-archive mode, -extldflags are ignored by the linker. @@ -239,7 +247,9 @@ def extldflags_from_cc_toolchain(go): return go.cgo_tools.ld_executable_options def extld_from_cc_toolchain(go): - if go.mode.link in (LINKMODE_SHARED, LINKMODE_PLUGIN, LINKMODE_C_SHARED, LINKMODE_PIE): + if not go.cgo_tools: + return [] + elif go.mode.link in (LINKMODE_SHARED, LINKMODE_PLUGIN, LINKMODE_C_SHARED, LINKMODE_PIE): return ["-extld", go.cgo_tools.ld_dynamic_lib_path] elif go.mode.link == LINKMODE_C_ARCHIVE: if go.mode.goos == "darwin": diff --git a/go/private/platforms.bzl b/go/private/platforms.bzl new file mode 100644 index 0000000000..55319ff10e --- /dev/null +++ b/go/private/platforms.bzl @@ -0,0 +1,190 @@ +# Copyright 2019 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. + +# platforms.bzl defines PLATFORMS, a table that describes each possible +# target platform. This table is used to generate config_settings, +# constraint_values, platforms, and toolchains. + +BAZEL_GOOS_CONSTRAINTS = { + "android": "@bazel_tools//platforms:android", + "darwin": "@bazel_tools//platforms:osx", + "freebsd": "@bazel_tools//platforms:freebsd", + "linux": "@bazel_tools//platforms:linux", + "windows": "@bazel_tools//platforms:windows", +} + +BAZEL_GOARCH_CONSTRAINTS = { + "386": "@bazel_tools//platforms:x86_32", + "amd64": "@bazel_tools//platforms:x86_64", + "arm": "@bazel_tools//platforms:arm", + "arm64": "@bazel_tools//platforms:aarch64", + "ppc64le": "@bazel_tools//platforms:ppc", + "s390x": "@bazel_tools//platforms:s390x", +} + +GOOS_GOARCH = ( + ("android", "386"), + ("android", "amd64"), + ("android", "arm"), + ("android", "arm64"), + ("darwin", "386"), + ("darwin", "amd64"), + ("darwin", "arm"), + ("darwin", "arm64"), + ("dragonfly", "amd64"), + ("freebsd", "386"), + ("freebsd", "amd64"), + ("freebsd", "arm"), + ("linux", "386"), + ("linux", "amd64"), + ("linux", "arm"), + ("linux", "arm64"), + ("linux", "mips"), + ("linux", "mips64"), + ("linux", "mips64le"), + ("linux", "mipsle"), + ("linux", "ppc64"), + ("linux", "ppc64le"), + ("linux", "s390x"), + ("nacl", "386"), + ("nacl", "amd64p32"), + ("nacl", "arm"), + ("netbsd", "386"), + ("netbsd", "amd64"), + ("netbsd", "arm"), + ("openbsd", "386"), + ("openbsd", "amd64"), + ("openbsd", "arm"), + ("plan9", "386"), + ("plan9", "amd64"), + ("plan9", "arm"), + ("solaris", "amd64"), + ("windows", "386"), + ("windows", "amd64"), + ("js", "wasm"), +) + +RACE_GOOS_GOARCH = { + ("darwin", "amd64"): None, + ("freebsd", "amd64"): None, + ("linux", "amd64"): None, + ("windows", "amd64"): None, +} + +MSAN_GOOS_GOARCH = { + ("linux", "amd64"): None, +} + +CGO_GOOS_GOARCH = { + ("aix", "ppc64"): None, + ("android", "386"): None, + ("android", "amd64"): None, + ("android", "arm"): None, + ("android", "arm64"): None, + ("darwin", "amd64"): None, + ("darwin", "arm"): None, + ("darwin", "arm64"): None, + ("dragonfly", "amd64"): None, + ("freebsd", "386"): None, + ("freebsd", "amd64"): None, + ("freebsd", "arm"): None, + ("illumos", "amd64"): None, + ("linux", "386"): None, + ("linux", "amd64"): None, + ("linux", "arm"): None, + ("linux", "arm64"): None, + ("linux", "mips"): None, + ("linux", "mips64"): None, + ("linux", "mips64le"): None, + ("linux", "mipsle"): None, + ("linux", "ppc64le"): None, + ("linux", "riscv64"): None, + ("linux", "s390x"): None, + ("linux", "sparc64"): None, + ("netbsd", "386"): None, + ("netbsd", "amd64"): None, + ("netbsd", "arm"): None, + ("netbsd", "arm64"): None, + ("openbsd", "386"): None, + ("openbsd", "amd64"): None, + ("openbsd", "arm"): None, + ("openbsd", "arm64"): None, + ("solaris", "amd64"): None, + ("windows", "386"): None, + ("windows", "amd64"): None, +} + +def _generate_constraints(names, bazel_constraints): + return { + name: bazel_constraints.get(name, "@io_bazel_rules_go//go/toolchain:" + name) + for name in names + } + +GOOS_CONSTRAINTS = _generate_constraints([p[0] for p in GOOS_GOARCH], BAZEL_GOOS_CONSTRAINTS) +GOARCH_CONSTRAINTS = _generate_constraints([p[1] for p in GOOS_GOARCH], BAZEL_GOARCH_CONSTRAINTS) + +def _generate_platforms(): + platforms = [] + for goos, goarch in GOOS_GOARCH: + constraints = [ + GOOS_CONSTRAINTS[goos], + GOARCH_CONSTRAINTS[goarch], + ] + constraints.append("@io_bazel_rules_go//go/toolchain:" + ("is_darwin" if goos == "darwin" else "not_darwin")) + platforms.append(struct( + name = goos + "_" + goarch, + goos = goos, + goarch = goarch, + constraints = constraints + ["@io_bazel_rules_go//go/toolchain:cgo_off"], + cgo = False, + )) + if (goos, goarch) in CGO_GOOS_GOARCH: + platforms.append(struct( + name = goos + "_" + goarch + "_cgo", + goos = goos, + goarch = goarch, + constraints = constraints + ["@io_bazel_rules_go//go/toolchain:cgo_on"], + cgo = True, + )) + + for goarch in ("arm", "arm64", "386", "amd64"): + constraints = [ + "@bazel_tools//platforms:ios", + GOARCH_CONSTRAINTS[goarch], + "@io_bazel_rules_go//go/toolchain:is_darwin", + "@io_bazel_rules_go//go/toolchain:cgo_off", + ] + platforms.append(struct( + name = "ios_" + goarch, + goos = "darwin", + goarch = "goarch", + constraints = constraints, + cgo = False, + )) + constraints[-1] = "@io_bazel_rules_go//go/toolchain:cgo_on" + platforms.append(struct( + name = "ios_" + goarch + "_cgo", + goos = "darwin", + goarch = "goarch", + constraints = constraints, + cgo = True, + )) + + return platforms + +PLATFORMS = _generate_platforms() + +def generate_toolchain_names(): + # keep in sync with declare_toolchains + return ["go_" + p.name for p in PLATFORMS] diff --git a/go/private/providers.bzl b/go/private/providers.bzl index 6539b0f319..6f89f537a3 100644 --- a/go/private/providers.bzl +++ b/go/private/providers.bzl @@ -71,6 +71,8 @@ GoSDK = provider( GoStdLib = provider() +CgoContextData = provider() + EXPLICIT_PATH = "explicit" INFERRED_PATH = "inferred" diff --git a/go/private/sdk.bzl b/go/private/sdk.bzl index 6fc8879459..c72791be58 100644 --- a/go/private/sdk.bzl +++ b/go/private/sdk.bzl @@ -28,7 +28,7 @@ load( "SDK_REPOSITORIES", ) load( - "@io_bazel_rules_go//go/platform:list.bzl", + "@io_bazel_rules_go//go/private:platforms.bzl", "generate_toolchain_names", ) load( diff --git a/go/toolchain/toolchains.bzl b/go/toolchain/toolchains.bzl index 4094bc733e..ed505bfcd5 100644 --- a/go/toolchain/toolchains.bzl +++ b/go/toolchain/toolchains.bzl @@ -1,18 +1,36 @@ +# Copyright 2019 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( - "//go/private:sdk.bzl", + "@io_bazel_rules_go//go/private:sdk.bzl", _go_register_toolchains = "go_register_toolchains", ) load( - "//go/private:sdk_list.bzl", + "@io_bazel_rules_go//go/private:sdk_list.bzl", _DEFAULT_VERSION = "DEFAULT_VERSION", _MIN_SUPPORTED_VERSION = "MIN_SUPPORTED_VERSION", _SDK_REPOSITORIES = "SDK_REPOSITORIES", ) load( - "//go/platform:list.bzl", - "GOARCH", - "GOOS", - "GOOS_GOARCH", + "@io_bazel_rules_go//go/private:platforms.bzl", + "GOARCH_CONSTRAINTS", + "GOOS_CONSTRAINTS", + "PLATFORMS", +) +load( + "@io_bazel_rules_go_compat//:darwin.bzl", + "DEFAULT_DARWIN_CONSTRAINT_VALUE", ) # These symbols should be loaded from sdk.bzl or deps.bzl instead of here.. @@ -22,33 +40,79 @@ SDK_REPOSITORIES = _SDK_REPOSITORIES go_register_toolchains = _go_register_toolchains def declare_constraints(): - for goos, constraint in GOOS.items(): - if constraint: - native.alias( - name = goos, - actual = constraint, - ) - else: + """Generates constraint_values and platform targets for valid platforms. + + Each constraint_value corresponds to a valid goos or goarch. + The goos and goarch values belong to the constraint_settings + @bazel_tools//platforms:os and @bazel_tools//platforms:cpu, respectively. + To avoid redundancy, if there is an equivalent value in @bazel_tools, + we define an alias here instead of another constraint_value. + + There is a special constraint_setting for Darwin, "darwin_constraint". + The value is "is_darwin" when the target is macOS or iOS and "not_darwin" + otherwise. + + Each platform defined here selects a goos and goarch constraint value. + These platforms may be used with --platforms for cross-compilation, + though users may create their own platforms (and + @bazel_tools//platforms:default_platform will be used most of the time). + """ + for goos, constraint in GOOS_CONSTRAINTS.items(): + if constraint.startswith("@io_bazel_rules_go//go/toolchain:"): native.constraint_value( name = goos, constraint_setting = "@bazel_tools//platforms:os", ) - for goarch, constraint in GOARCH.items(): - if constraint: + else: native.alias( - name = goarch, + name = goos, actual = constraint, ) - else: + + for goarch, constraint in GOARCH_CONSTRAINTS.items(): + if constraint.startswith("@io_bazel_rules_go//go/toolchain:"): native.constraint_value( name = goarch, constraint_setting = "@bazel_tools//platforms:cpu", ) - for goos, goarch in GOOS_GOARCH: + else: + native.alias( + name = goarch, + actual = constraint, + ) + + native.constraint_setting( + name = "darwin_constraint", + default_constraint_value = DEFAULT_DARWIN_CONSTRAINT_VALUE, + ) + + native.constraint_value( + name = "is_darwin", + constraint_setting = ":darwin_constraint", + ) + + native.constraint_value( + name = "not_darwin", + constraint_setting = ":darwin_constraint", + ) + + native.constraint_setting( + name = "cgo_constraint", + default_constraint_value = ":cgo_on", + ) + + native.constraint_value( + name = "cgo_on", + constraint_setting = ":cgo_constraint", + ) + + native.constraint_value( + name = "cgo_off", + constraint_setting = ":cgo_constraint", + ) + + for p in PLATFORMS: native.platform( - name = goos + "_" + goarch, - constraint_values = [ - ":" + goos, - ":" + goarch, - ], + name = p.name, + constraint_values = p.constraints, ) diff --git a/go/toolchains.rst b/go/toolchains.rst index 3497c92b58..5bad74ef75 100644 --- a/go/toolchains.rst +++ b/go/toolchains.rst @@ -442,11 +442,12 @@ usually won't need to declare this rule explicitly. go_toolchain ~~~~~~~~~~~~ -This adds a toolchain of type :value:`"@io_bazel_rules_go//go:toolchain"`. +This declares a toolchain that may be used with toolchain type +:value:`"@io_bazel_rules_go//go:toolchain"`. Normally, ``go_toolchain`` rules are declared and registered in repositories configured with `go_download_sdk`_, `go_host_sdk`_, `go_local_sdk`_, or -`go_wrap_sdk`_. You usually won't need to declare these explicitly +`go_wrap_sdk`_. You usually won't need to declare these explicitly. +--------------------------------+-----------------------------+-----------------------------------+ | **Name** | **Type** | **Default value** | @@ -455,19 +456,13 @@ configured with `go_download_sdk`_, `go_host_sdk`_, `go_local_sdk`_, or +--------------------------------+-----------------------------+-----------------------------------+ | A unique name for the toolchain. | +--------------------------------+-----------------------------+-----------------------------------+ -| :param:`target` | :type:`string` | |mandatory| | +| :param:`goos` | :type:`string` | |mandatory| | +--------------------------------+-----------------------------+-----------------------------------+ -| Specifies the default target platform for this toolchain. | -| | -| It must be of the form ``_`` (for example, ``linux_amd64``). | -| See `go/platform/list.bzl`_ for valid values. | +| The target operating system. Must be a standard ``GOOS`` value. | +--------------------------------+-----------------------------+-----------------------------------+ -| :param:`host` | :type:`string` | :value:`target` | +| :param:`goarch` | :type:`string` | |mandatory| | +--------------------------------+-----------------------------+-----------------------------------+ -| Specifies the host platform for this toolchain. | -| | -| It must be of the form ``_`` (for example, ``linux_amd64``). | -| See `go/platform/list.bzl`_ for valid values. | +| The target architecture. Must be a standard ``GOARCH`` value. | +--------------------------------+-----------------------------+-----------------------------------+ | :param:`sdk` | :type:`label` | |mandatory| | +--------------------------------+-----------------------------+-----------------------------------+ @@ -482,11 +477,6 @@ configured with `go_download_sdk`_, `go_host_sdk`_, `go_local_sdk`_, or +--------------------------------+-----------------------------+-----------------------------------+ | Flags passed to the external linker (if it is used). | +--------------------------------+-----------------------------+-----------------------------------+ -| :param:`constraints` | :type:`label_list` | :value:`[]` | -+--------------------------------+-----------------------------+-----------------------------------+ -| Additional constraints for the target platform. Bazel will take these into | -| account when selecting the toolchain. | -+--------------------------------+-----------------------------+-----------------------------------+ go_context ~~~~~~~~~~ diff --git a/go/tools/builders/stdlib.go b/go/tools/builders/stdlib.go index c21496b576..af16139255 100644 --- a/go/tools/builders/stdlib.go +++ b/go/tools/builders/stdlib.go @@ -63,6 +63,11 @@ func stdlib(args []string) error { os.Setenv("GOCACHE", cachePath) defer os.RemoveAll(cachePath) + // Disable modules for the 'go install' command. Depending on the sandboxing + // mode, there may be a go.mod file in a parent directory which will turn + // modules on in "auto" mode. + os.Setenv("GO111MODULE", "off") + // Make sure we have an absolute path to the C compiler. // TODO(#1357): also take absolute paths of includes and other paths in flags. os.Setenv("CC", abs(os.Getenv("CC"))) diff --git a/tests/bazel_tests.bzl b/tests/bazel_tests.bzl index c00d747d0b..6d7a7cdd6d 100644 --- a/tests/bazel_tests.bzl +++ b/tests/bazel_tests.bzl @@ -1,5 +1,5 @@ load( - "@io_bazel_rules_go//go/platform:list.bzl", + "@io_bazel_rules_go//go/private:platforms.bzl", "generate_toolchain_names", ) load( diff --git a/tests/core/cross/BUILD.bazel b/tests/core/cross/BUILD.bazel index e32c755a63..a1b932c1b6 100644 --- a/tests/core/cross/BUILD.bazel +++ b/tests/core/cross/BUILD.bazel @@ -1,4 +1,5 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test", "go_source") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_source", "go_test") +load("@io_bazel_rules_go//tests:bazel_tests.bzl", "bazel_test") test_suite( name = "cross", @@ -32,16 +33,53 @@ go_test( name = "cross_test", size = "small", srcs = ["cross_test.go"], - rundir = ".", + args = [ + "-darwin", + "$(location :darwin_cross)", + "-linux", + "$(location :linux_cross)", + "-windows", + "$(location :windows_cross)", + ], data = [ ":darwin_cross", ":linux_cross", ":windows_cross", ], + rundir = ".", deps = ["//go/tools/bazel:go_default_library"], - args = [ - "-darwin", "$(location :darwin_cross)", - "-linux", "$(location :linux_cross)", - "-windows", "$(location :windows_cross)", - ] ) + +# TODO(#2090): enable test when cgo toolchains are sorted out. This won't +# build on non-darwin platforms if we need a C/C++ toolchain. + +# bazel_test( +# name = "ios_select_test", +# args = ["--platforms=@io_bazel_rules_go//go/toolchain:ios_amd64"], +# command = "build", +# targets = [":ios_lib"], +# ) + +# go_library( +# name = "use_ios_lib", +# importpath = "github.com/bazelbuild/rules_go/tests/core/cross/use_ios_lib", +# deps = select({ +# ":is_osx": [":ios_lib"], +# "//conditions:default": [], +# }), +# ) + +# config_setting( +# name = "is_osx", +# constraint_values = ["@bazel_tools//platforms:osx"], +# ) + +# go_library( +# name = "ios_lib", +# srcs = select({ +# "@io_bazel_rules_go//go/platform:darwin": ["ios_good.go"], +# "//conditions:default": ["ios_bad.go"], +# }), +# importpath = "github.com/bazelbuild/rules_go/tests/core/cross/ios_lib", +# tags = ["manual"], +# ) diff --git a/tests/core/cross/README.rst b/tests/core/cross/README.rst index 9d9eec0c7d..6fd155b779 100644 --- a/tests/core/cross/README.rst +++ b/tests/core/cross/README.rst @@ -16,3 +16,12 @@ This builds binaries using `main.go `_ in multiple configurations, and test `written in go `_. The test executes the unix command "file" on the binaries to determine their type, and checks they were built for the expected architecture. + +ios_select_test +--------------- + +Tests that we can cross-compile a library for iOS. We should be able to select +a dependency using ``@io_bazel_rules_go//go/platform:darwin``, which is true +when building for iOS (tested by ``ios_select_test``) and macOS +(tested by ``use_ios_lib``). + diff --git a/tests/core/cross/ios_bad.go b/tests/core/cross/ios_bad.go new file mode 100644 index 0000000000..d49f52bc8d --- /dev/null +++ b/tests/core/cross/ios_bad.go @@ -0,0 +1,2 @@ +donotbuild + diff --git a/tests/core/cross/ios_good.go b/tests/core/cross/ios_good.go new file mode 100644 index 0000000000..d76c722798 --- /dev/null +++ b/tests/core/cross/ios_good.go @@ -0,0 +1 @@ +package ios_lib