1# Copyright 2017 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# Modes are documented in go/modes.rst#compilation-modes 16 17LINKMODE_NORMAL = "normal" 18 19LINKMODE_SHARED = "shared" 20 21LINKMODE_PIE = "pie" 22 23LINKMODE_PLUGIN = "plugin" 24 25LINKMODE_C_SHARED = "c-shared" 26 27LINKMODE_C_ARCHIVE = "c-archive" 28 29LINKMODES = [LINKMODE_NORMAL, LINKMODE_PLUGIN, LINKMODE_C_SHARED, LINKMODE_C_ARCHIVE, LINKMODE_PIE] 30 31# All link modes that produce executables to be run with bazel run. 32LINKMODES_EXECUTABLE = [LINKMODE_NORMAL, LINKMODE_PIE] 33 34# All link modes that require external linking and thus a cgo context. 35LINKMODES_REQUIRING_EXTERNAL_LINKING = [ 36 LINKMODE_PLUGIN, 37 LINKMODE_C_ARCHIVE, 38 LINKMODE_C_SHARED, 39] 40 41def mode_string(mode): 42 result = [mode.goos, mode.goarch] 43 if mode.static: 44 result.append("static") 45 if mode.race: 46 result.append("race") 47 if mode.msan: 48 result.append("msan") 49 if mode.pure: 50 result.append("pure") 51 if mode.debug: 52 result.append("debug") 53 if mode.strip: 54 result.append("stripped") 55 if not result or not mode.link == LINKMODE_NORMAL: 56 result.append(mode.link) 57 if mode.gc_goopts: 58 result.extend(mode.gc_goopts) 59 return "_".join(result) 60 61def _ternary(*values): 62 for v in values: 63 if v == None: 64 continue 65 if type(v) == "bool": 66 return v 67 if type(v) != "string": 68 fail("Invalid value type {}".format(type(v))) 69 v = v.lower() 70 if v == "on": 71 return True 72 if v == "off": 73 return False 74 if v == "auto": 75 continue 76 fail("Invalid value {}".format(v)) 77 fail("_ternary failed to produce a final result from {}".format(values)) 78 79def get_mode(ctx, go_toolchain, cgo_context_info, go_config_info): 80 static = _ternary(go_config_info.static if go_config_info else "off") 81 pure = _ternary( 82 "on" if not cgo_context_info else "auto", 83 go_config_info.pure if go_config_info else "off", 84 ) 85 race = _ternary(go_config_info.race if go_config_info else "off") 86 msan = _ternary(go_config_info.msan if go_config_info else "off") 87 strip = go_config_info.strip if go_config_info else False 88 stamp = go_config_info.stamp if go_config_info else False 89 debug = go_config_info.debug if go_config_info else False 90 linkmode = go_config_info.linkmode if go_config_info else LINKMODE_NORMAL 91 cover_format = go_config_info and go_config_info.cover_format 92 amd64 = go_config_info.amd64 if go_config_info else None 93 goos = go_toolchain.default_goos if getattr(ctx.attr, "goos", "auto") == "auto" else ctx.attr.goos 94 goarch = go_toolchain.default_goarch if getattr(ctx.attr, "goarch", "auto") == "auto" else ctx.attr.goarch 95 gc_goopts = go_config_info.gc_goopts if go_config_info else [] 96 97 # TODO(jayconrod): check for more invalid and contradictory settings. 98 if pure and race: 99 fail("race instrumentation can't be enabled when cgo is disabled. Check that pure is not set to \"off\" and a C/C++ toolchain is configured.") 100 if pure and msan: 101 fail("msan instrumentation can't be enabled when cgo is disabled. Check that pure is not set to \"off\" and a C/C++ toolchain is configured.") 102 if pure and linkmode in LINKMODES_REQUIRING_EXTERNAL_LINKING: 103 fail(("linkmode '{}' can't be used when cgo is disabled. Check that pure is not set to \"off\" and that a C/C++ toolchain is configured for " + 104 "your current platform. If you defined a custom platform, make sure that it has the @io_bazel_rules_go//go/toolchain:cgo_on constraint value.").format(linkmode)) 105 106 gc_linkopts = list(go_config_info.gc_linkopts) if go_config_info else [] 107 tags = list(go_config_info.tags) if go_config_info else [] 108 if "gotags" in ctx.var: 109 tags.extend(ctx.var["gotags"].split(",")) 110 if cgo_context_info: 111 tags.extend(cgo_context_info.tags) 112 if race: 113 tags.append("race") 114 if msan: 115 tags.append("msan") 116 117 return struct( 118 static = static, 119 race = race, 120 msan = msan, 121 pure = pure, 122 link = linkmode, 123 gc_linkopts = gc_linkopts, 124 strip = strip, 125 stamp = stamp, 126 debug = debug, 127 goos = goos, 128 goarch = goarch, 129 tags = tags, 130 cover_format = cover_format, 131 amd64 = amd64, 132 gc_goopts = gc_goopts, 133 ) 134 135def installsuffix(mode): 136 s = mode.goos + "_" + mode.goarch 137 if mode.race: 138 s += "_race" 139 elif mode.msan: 140 s += "_msan" 141 return s 142 143def mode_tags_equivalent(l, r): 144 # Returns whether two modes are equivalent for Go build tags. For example, 145 # goos and goarch must match, but static doesn't matter. 146 return (l.goos == r.goos and 147 l.goarch == r.goarch and 148 l.race == r.race and 149 l.msan == r.msan) 150 151# Ported from https://github.com/golang/go/blob/master/src/cmd/go/internal/work/init.go#L76 152_LINK_C_ARCHIVE_PLATFORMS = { 153 "darwin/arm64": None, 154 "ios/arm64": None, 155} 156 157_LINK_C_ARCHIVE_GOOS = { 158 "dragonfly": None, 159 "freebsd": None, 160 "linux": None, 161 "netbsd": None, 162 "openbsd": None, 163 "solaris": None, 164} 165 166_LINK_C_SHARED_GOOS = [ 167 "android", 168 "freebsd", 169 "linux", 170] 171 172_LINK_PLUGIN_PLATFORMS = { 173 "linux/amd64": None, 174 "linux/arm": None, 175 "linux/arm64": None, 176 "linux/386": None, 177 "linux/s390x": None, 178 "linux/ppc64le": None, 179 "android/amd64": None, 180 "android/arm": None, 181 "android/arm64": None, 182 "android/386": None, 183 "darwin/amd64": None, 184 "darwin/arm64": None, 185 "ios/arm": None, 186 "ios/arm64": None, 187} 188 189_LINK_PIE_PLATFORMS = { 190 "linux/amd64": None, 191 "linux/arm": None, 192 "linux/arm64": None, 193 "linux/386": None, 194 "linux/s390x": None, 195 "linux/ppc64le": None, 196 "android/amd64": None, 197 "android/arm": None, 198 "android/arm64": None, 199 "android/386": None, 200 "freebsd/amd64": None, 201} 202 203def link_mode_args(mode): 204 # based on buildModeInit in cmd/go/internal/work/init.go 205 platform = mode.goos + "/" + mode.goarch 206 args = [] 207 if mode.link == LINKMODE_C_ARCHIVE: 208 if (platform in _LINK_C_ARCHIVE_PLATFORMS or 209 mode.goos in _LINK_C_ARCHIVE_GOOS and platform != "linux/ppc64"): 210 args.append("-shared") 211 elif mode.link == LINKMODE_C_SHARED: 212 if mode.goos in _LINK_C_SHARED_GOOS: 213 args.append("-shared") 214 elif mode.link == LINKMODE_PLUGIN: 215 if platform in _LINK_PLUGIN_PLATFORMS: 216 args.append("-dynlink") 217 elif mode.link == LINKMODE_PIE: 218 if platform in _LINK_PIE_PLATFORMS: 219 args.append("-shared") 220 return args 221 222def extldflags_from_cc_toolchain(go): 223 if not go.cgo_tools: 224 return [] 225 elif go.mode.link in (LINKMODE_SHARED, LINKMODE_PLUGIN, LINKMODE_C_SHARED): 226 return go.cgo_tools.ld_dynamic_lib_options 227 else: 228 # NOTE: in c-archive mode, -extldflags are ignored by the linker. 229 # However, we still need to set them for cgo, which links a binary 230 # in each package. We use the executable options for this. 231 return go.cgo_tools.ld_executable_options 232 233def extld_from_cc_toolchain(go): 234 if not go.cgo_tools: 235 return [] 236 elif go.mode.link in (LINKMODE_SHARED, LINKMODE_PLUGIN, LINKMODE_C_SHARED, LINKMODE_PIE): 237 return ["-extld", go.cgo_tools.ld_dynamic_lib_path] 238 elif go.mode.link == LINKMODE_C_ARCHIVE: 239 if go.mode.goos in ["darwin", "ios"]: 240 # TODO(jayconrod): on macOS, set -extar. At this time, wrapped_ar is 241 # a bash script without a shebang line, so we can't execute it. We 242 # use /usr/bin/ar (the default) instead. 243 return [] 244 else: 245 return ["-extar", go.cgo_tools.ld_static_lib_path] 246 else: 247 # NOTE: In c-archive mode, we should probably set -extar. However, 248 # on macOS, Bazel returns wrapped_ar, which is not executable. 249 # /usr/bin/ar (the default) should be visible though, and we have a 250 # hack in link.go to strip out non-reproducible stuff. 251 return ["-extld", go.cgo_tools.ld_executable_path] 252