1# Copyright 2014 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
15load("//go/private:common.bzl", "executable_path")
16load("//go/private:nogo.bzl", "go_register_nogo")
17load("//go/private/skylib/lib:versions.bzl", "versions")
18load("@bazel_tools//tools/build_defs/repo:utils.bzl", "patch", "read_user_netrc", "use_netrc")
19
20MIN_SUPPORTED_VERSION = (1, 14, 0)
21
22def _go_host_sdk_impl(ctx):
23 goroot = _detect_host_sdk(ctx)
24 platform = _detect_sdk_platform(ctx, goroot)
25 version = _detect_sdk_version(ctx, goroot)
26 _sdk_build_file(ctx, platform, version, experiments = ctx.attr.experiments)
27 _local_sdk(ctx, goroot)
28
29go_host_sdk_rule = repository_rule(
30 implementation = _go_host_sdk_impl,
31 environ = ["GOROOT"],
32 attrs = {
33 "version": attr.string(),
34 "experiments": attr.string_list(
35 doc = "Go experiments to enable via GOEXPERIMENT",
36 ),
37 "_sdk_build_file": attr.label(
38 default = Label("//go/private:BUILD.sdk.bazel"),
39 ),
40 },
41)
42
43def go_host_sdk(name, register_toolchains = True, **kwargs):
44 go_host_sdk_rule(name = name, **kwargs)
45 _go_toolchains(
46 name = name + "_toolchains",
47 sdk_repo = name,
48 sdk_type = "host",
49 sdk_version = kwargs.get("version"),
50 goos = kwargs.get("goos"),
51 goarch = kwargs.get("goarch"),
52 )
53 if register_toolchains:
54 _register_toolchains(name)
55
56def _go_download_sdk_impl(ctx):
57 if not ctx.attr.goos and not ctx.attr.goarch:
58 goos, goarch = detect_host_platform(ctx)
59 else:
60 if not ctx.attr.goos:
61 fail("goarch set but goos not set")
62 if not ctx.attr.goarch:
63 fail("goos set but goarch not set")
64 goos, goarch = ctx.attr.goos, ctx.attr.goarch
65 platform = goos + "_" + goarch
66
67 version = ctx.attr.version
68 sdks = ctx.attr.sdks
69
70 if not version:
71 if ctx.attr.patches:
72 fail("a single version must be specified to apply patches")
73
74 if not sdks:
75 # If sdks was unspecified, download a full list of files.
76 # If version was unspecified, pick the latest version.
77 # Even if version was specified, we need to download the file list
78 # to find the SHA-256 sum. If we don't have it, Bazel won't cache
79 # the downloaded archive.
80 if not version:
81 ctx.report_progress("Finding latest Go version")
82 else:
83 ctx.report_progress("Finding Go SHA-256 sums")
84 ctx.download(
85 url = [
86 "https://go.dev/dl/?mode=json&include=all",
87 "https://golang.google.cn/dl/?mode=json&include=all",
88 ],
89 output = "versions.json",
90 )
91
92 data = ctx.read("versions.json")
93 sdks_by_version = _parse_versions_json(data)
94
95 if not version:
96 highest_version = None
97 for v in sdks_by_version.keys():
98 pv = parse_version(v)
99 if not pv or _version_is_prerelease(pv):
100 # skip parse errors and pre-release versions
101 continue
102 if not highest_version or _version_less(highest_version, pv):
103 highest_version = pv
104 if not highest_version:
105 fail("did not find any Go versions in https://go.dev/dl/?mode=json")
106 version = _version_string(highest_version)
107 if version not in sdks_by_version:
108 fail("did not find version {} in https://go.dev/dl/?mode=json".format(version))
109 sdks = sdks_by_version[version]
110
111 if platform not in sdks:
112 fail("unsupported platform {}".format(platform))
113 filename, sha256 = sdks[platform]
114
115 _remote_sdk(ctx, [url.format(filename) for url in ctx.attr.urls], ctx.attr.strip_prefix, sha256)
116 patch(ctx, patch_args = _get_patch_args(ctx.attr.patch_strip))
117
118 detected_version = _detect_sdk_version(ctx, ".")
119 _sdk_build_file(ctx, platform, detected_version, experiments = ctx.attr.experiments)
120
121 if not ctx.attr.sdks and not ctx.attr.version:
122 # Returning this makes Bazel print a message that 'version' must be
123 # specified for a reproducible build.
124 return {
125 "name": ctx.attr.name,
126 "goos": ctx.attr.goos,
127 "goarch": ctx.attr.goarch,
128 "sdks": ctx.attr.sdks,
129 "urls": ctx.attr.urls,
130 "version": version,
131 "strip_prefix": ctx.attr.strip_prefix,
132 }
133 return None
134
135go_download_sdk_rule = repository_rule(
136 implementation = _go_download_sdk_impl,
137 attrs = {
138 "goos": attr.string(),
139 "goarch": attr.string(),
140 "sdks": attr.string_list_dict(),
141 "experiments": attr.string_list(
142 doc = "Go experiments to enable via GOEXPERIMENT",
143 ),
144 "urls": attr.string_list(default = ["https://dl.google.com/go/{}"]),
145 "version": attr.string(),
146 "strip_prefix": attr.string(default = "go"),
147 "patches": attr.label_list(
148 doc = "A list of patches to apply to the SDK after downloading it",
149 ),
150 "patch_strip": attr.int(
151 default = 0,
152 doc = "The number of leading path segments to be stripped from the file name in the patches.",
153 ),
154 "_sdk_build_file": attr.label(
155 default = Label("//go/private:BUILD.sdk.bazel"),
156 ),
157 },
158)
159
160def _define_version_constants(version, prefix = ""):
161 pv = parse_version(version)
162 if pv == None or len(pv) < 3:
163 fail("error parsing sdk version: " + version)
164 major, minor, patch = pv[0], pv[1], pv[2]
165 prerelease = pv[3] if len(pv) > 3 else ""
166 return """
167{prefix}MAJOR_VERSION = "{major}"
168{prefix}MINOR_VERSION = "{minor}"
169{prefix}PATCH_VERSION = "{patch}"
170{prefix}PRERELEASE_SUFFIX = "{prerelease}"
171""".format(
172 prefix = prefix,
173 major = major,
174 minor = minor,
175 patch = patch,
176 prerelease = prerelease,
177 )
178
179def _to_constant_name(s):
180 # Prefix with _ as identifiers are not allowed to start with numbers.
181 return "_" + "".join([c if c.isalnum() else "_" for c in s.elems()]).upper()
182
183def _get_patch_args(patch_strip):
184 if patch_strip:
185 return ["-p{}".format(patch_strip)]
186 return []
187
188def go_toolchains_single_definition(ctx, *, prefix, goos, goarch, sdk_repo, sdk_type, sdk_version):
189 if not goos and not goarch:
190 goos, goarch = detect_host_platform(ctx)
191 else:
192 if not goos:
193 fail("goarch set but goos not set")
194 if not goarch:
195 fail("goos set but goarch not set")
196
197 chunks = []
198 loads = []
199 identifier_prefix = _to_constant_name(prefix)
200
201 # If a sdk_version attribute is provided, use that version. This avoids
202 # eagerly fetching the SDK repository. But if it's not provided, we have
203 # no choice and must load version constants from the version.bzl file that
204 # _sdk_build_file creates. This will trigger an eager fetch.
205 if sdk_version:
206 chunks.append(_define_version_constants(sdk_version, prefix = identifier_prefix))
207 else:
208 loads.append("""load(
209 "@{sdk_repo}//:version.bzl",
210 {identifier_prefix}MAJOR_VERSION = "MAJOR_VERSION",
211 {identifier_prefix}MINOR_VERSION = "MINOR_VERSION",
212 {identifier_prefix}PATCH_VERSION = "PATCH_VERSION",
213 {identifier_prefix}PRERELEASE_SUFFIX = "PRERELEASE_SUFFIX",
214)
215""".format(
216 sdk_repo = sdk_repo,
217 identifier_prefix = identifier_prefix,
218 ))
219
220 chunks.append("""declare_bazel_toolchains(
221 prefix = "{prefix}",
222 go_toolchain_repo = "@{sdk_repo}",
223 host_goarch = "{goarch}",
224 host_goos = "{goos}",
225 major = {identifier_prefix}MAJOR_VERSION,
226 minor = {identifier_prefix}MINOR_VERSION,
227 patch = {identifier_prefix}PATCH_VERSION,
228 prerelease = {identifier_prefix}PRERELEASE_SUFFIX,
229 sdk_type = "{sdk_type}",
230)
231""".format(
232 prefix = prefix,
233 identifier_prefix = identifier_prefix,
234 sdk_repo = sdk_repo,
235 goarch = goarch,
236 goos = goos,
237 sdk_type = sdk_type,
238 ))
239
240 return struct(
241 loads = loads,
242 chunks = chunks,
243 )
244
245def go_toolchains_build_file_content(
246 ctx,
247 prefixes,
248 geese,
249 goarchs,
250 sdk_repos,
251 sdk_types,
252 sdk_versions):
253 if not _have_same_length(prefixes, geese, goarchs, sdk_repos, sdk_types, sdk_versions):
254 fail("all lists must have the same length")
255
256 loads = [
257 """load("@io_bazel_rules_go//go/private:go_toolchain.bzl", "declare_bazel_toolchains")""",
258 ]
259 chunks = [
260 """package(default_visibility = ["//visibility:public"])""",
261 ]
262
263 for i in range(len(geese)):
264 definition = go_toolchains_single_definition(
265 ctx,
266 prefix = prefixes[i],
267 goos = geese[i],
268 goarch = goarchs[i],
269 sdk_repo = sdk_repos[i],
270 sdk_type = sdk_types[i],
271 sdk_version = sdk_versions[i],
272 )
273 loads.extend(definition.loads)
274 chunks.extend(definition.chunks)
275
276 return "\n".join(loads + chunks)
277
278def _go_multiple_toolchains_impl(ctx):
279 ctx.file(
280 "BUILD.bazel",
281 go_toolchains_build_file_content(
282 ctx,
283 prefixes = ctx.attr.prefixes,
284 geese = ctx.attr.geese,
285 goarchs = ctx.attr.goarchs,
286 sdk_repos = ctx.attr.sdk_repos,
287 sdk_types = ctx.attr.sdk_types,
288 sdk_versions = ctx.attr.sdk_versions,
289 ),
290 executable = False,
291 )
292
293go_multiple_toolchains = repository_rule(
294 implementation = _go_multiple_toolchains_impl,
295 attrs = {
296 "prefixes": attr.string_list(mandatory = True),
297 "sdk_repos": attr.string_list(mandatory = True),
298 "sdk_types": attr.string_list(mandatory = True),
299 "sdk_versions": attr.string_list(mandatory = True),
300 "geese": attr.string_list(mandatory = True),
301 "goarchs": attr.string_list(mandatory = True),
302 },
303)
304
305def _go_toolchains(name, sdk_repo, sdk_type, sdk_version = None, goos = None, goarch = None):
306 go_multiple_toolchains(
307 name = name,
308 prefixes = [""],
309 geese = [goos or ""],
310 goarchs = [goarch or ""],
311 sdk_repos = [sdk_repo],
312 sdk_types = [sdk_type],
313 sdk_versions = [sdk_version or ""],
314 )
315
316def go_download_sdk(name, register_toolchains = True, **kwargs):
317 go_download_sdk_rule(name = name, **kwargs)
318 _go_toolchains(
319 name = name + "_toolchains",
320 sdk_repo = name,
321 sdk_type = "remote",
322 sdk_version = kwargs.get("version"),
323 goos = kwargs.get("goos"),
324 goarch = kwargs.get("goarch"),
325 )
326 if register_toolchains:
327 _register_toolchains(name)
328
329def _go_local_sdk_impl(ctx):
330 goroot = ctx.attr.path
331 platform = _detect_sdk_platform(ctx, goroot)
332 version = _detect_sdk_version(ctx, goroot)
333 _sdk_build_file(ctx, platform, version, ctx.attr.experiments)
334 _local_sdk(ctx, goroot)
335
336_go_local_sdk = repository_rule(
337 implementation = _go_local_sdk_impl,
338 attrs = {
339 "path": attr.string(),
340 "version": attr.string(),
341 "experiments": attr.string_list(
342 doc = "Go experiments to enable via GOEXPERIMENT",
343 ),
344 "_sdk_build_file": attr.label(
345 default = Label("//go/private:BUILD.sdk.bazel"),
346 ),
347 },
348)
349
350def go_local_sdk(name, register_toolchains = True, **kwargs):
351 _go_local_sdk(name = name, **kwargs)
352 _go_toolchains(
353 name = name + "_toolchains",
354 sdk_repo = name,
355 sdk_type = "remote",
356 sdk_version = kwargs.get("version"),
357 goos = kwargs.get("goos"),
358 goarch = kwargs.get("goarch"),
359 )
360 if register_toolchains:
361 _register_toolchains(name)
362
363def _go_wrap_sdk_impl(ctx):
364 if not ctx.attr.root_file and not ctx.attr.root_files:
365 fail("either root_file or root_files must be provided")
366 if ctx.attr.root_file and ctx.attr.root_files:
367 fail("root_file and root_files cannot be both provided")
368 if ctx.attr.root_file:
369 root_file = ctx.attr.root_file
370 else:
371 goos, goarch = detect_host_platform(ctx)
372 platform = goos + "_" + goarch
373 if platform not in ctx.attr.root_files:
374 fail("unsupported platform {}".format(platform))
375 root_file = Label(ctx.attr.root_files[platform])
376 goroot = str(ctx.path(root_file).dirname)
377 platform = _detect_sdk_platform(ctx, goroot)
378 version = _detect_sdk_version(ctx, goroot)
379 _sdk_build_file(ctx, platform, version, ctx.attr.experiments)
380 _local_sdk(ctx, goroot)
381
382_go_wrap_sdk = repository_rule(
383 implementation = _go_wrap_sdk_impl,
384 attrs = {
385 "root_file": attr.label(
386 mandatory = False,
387 doc = "A file in the SDK root direcotry. Used to determine GOROOT.",
388 ),
389 "root_files": attr.string_dict(
390 mandatory = False,
391 doc = "A set of mappings from the host platform to a file in the SDK's root directory",
392 ),
393 "version": attr.string(),
394 "experiments": attr.string_list(
395 doc = "Go experiments to enable via GOEXPERIMENT",
396 ),
397 "_sdk_build_file": attr.label(
398 default = Label("//go/private:BUILD.sdk.bazel"),
399 ),
400 },
401)
402
403def go_wrap_sdk(name, register_toolchains = True, **kwargs):
404 _go_wrap_sdk(name = name, **kwargs)
405 _go_toolchains(
406 name = name + "_toolchains",
407 sdk_repo = name,
408 sdk_type = "remote",
409 sdk_version = kwargs.get("version"),
410 goos = kwargs.get("goos"),
411 goarch = kwargs.get("goarch"),
412 )
413 if register_toolchains:
414 _register_toolchains(name)
415
416def _register_toolchains(repo):
417 native.register_toolchains("@{}_toolchains//:all".format(repo))
418
419def _remote_sdk(ctx, urls, strip_prefix, sha256):
420 if len(urls) == 0:
421 fail("no urls specified")
422 host_goos, _ = detect_host_platform(ctx)
423
424 ctx.report_progress("Downloading and extracting Go toolchain")
425
426 auth = use_netrc(read_user_netrc(ctx), urls, {})
427
428 # TODO(#2771): After bazelbuild/bazel#18448 is merged and available in
429 # the minimum supported version of Bazel, remove the workarounds below.
430 #
431 # Go ships archives containing some non-ASCII file names, used in
432 # test cases for Go's build system. Bazel has a bug extracting these
433 # archives on certain file systems (macOS AFS at least, possibly also
434 # Docker on macOS with a bind mount).
435 #
436 # For .tar.gz files (available for most platforms), we work around this bug
437 # by using the system tar instead of ctx.download_and_extract.
438 #
439 # For .zip files, we use ctx.download_and_extract but with rename_files,
440 # changing certain paths that trigger the bug. This is only available
441 # in Bazel 6.0.0+ (bazelbuild/bazel#16052). The only situation where
442 # .zip files are needed seems to be a macOS host using a Windows toolchain
443 # for remote execution.
444 if urls[0].endswith(".tar.gz"):
445 if strip_prefix != "go":
446 fail("strip_prefix not supported")
447 ctx.download(
448 url = urls,
449 sha256 = sha256,
450 output = "go_sdk.tar.gz",
451 auth = auth,
452 )
453 res = ctx.execute(["tar", "-xf", "go_sdk.tar.gz", "--strip-components=1"])
454 if res.return_code:
455 fail("error extracting Go SDK:\n" + res.stdout + res.stderr)
456 ctx.delete("go_sdk.tar.gz")
457 elif (urls[0].endswith(".zip") and
458 host_goos != "windows" and
459 # Development versions of Bazel have an empty version string. We assume that they are
460 # more recent than the version that introduced rename_files.
461 versions.is_at_least("6.0.0", versions.get() or "6.0.0")):
462 ctx.download_and_extract(
463 url = urls,
464 stripPrefix = strip_prefix,
465 sha256 = sha256,
466 rename_files = {
467 "go/test/fixedbugs/issue27836.dir/\336foo.go": "go/test/fixedbugs/issue27836.dir/thfoo.go",
468 "go/test/fixedbugs/issue27836.dir/\336main.go": "go/test/fixedbugs/issue27836.dir/thmain.go",
469 },
470 auth = auth,
471 )
472 else:
473 ctx.download_and_extract(
474 url = urls,
475 stripPrefix = strip_prefix,
476 sha256 = sha256,
477 auth = auth,
478 )
479
480def _local_sdk(ctx, path):
481 for entry in ctx.path(path).readdir():
482 if ctx.path(entry.basename).exists:
483 continue
484 ctx.symlink(entry, entry.basename)
485
486def _sdk_build_file(ctx, platform, version, experiments):
487 ctx.file("ROOT")
488 goos, _, goarch = platform.partition("_")
489
490 pv = parse_version(version)
491 if pv != None and pv[1] >= 20:
492 # Turn off coverageredesign GOEXPERIMENT on 1.20+
493 # until rules_go is updated to work with the
494 # coverage redesign.
495 if not "nocoverageredesign" in experiments and not "coverageredesign" in experiments:
496 experiments = experiments + ["nocoverageredesign"]
497
498 ctx.template(
499 "BUILD.bazel",
500 ctx.path(ctx.attr._sdk_build_file),
501 executable = False,
502 substitutions = {
503 "{goos}": goos,
504 "{goarch}": goarch,
505 "{exe}": ".exe" if goos == "windows" else "",
506 "{version}": version,
507 "{experiments}": repr(experiments),
508 },
509 )
510
511 ctx.file(
512 "version.bzl",
513 executable = False,
514 content = _define_version_constants(version),
515 )
516
517def detect_host_platform(ctx):
518 goos = ctx.os.name
519 if goos == "mac os x":
520 goos = "darwin"
521 elif goos.startswith("windows"):
522 goos = "windows"
523
524 goarch = ctx.os.arch
525 if goarch == "aarch64":
526 goarch = "arm64"
527 elif goarch == "x86_64":
528 goarch = "amd64"
529
530 return goos, goarch
531
532def _detect_host_sdk(ctx):
533 root = "@invalid@"
534 if "GOROOT" in ctx.os.environ:
535 return ctx.os.environ["GOROOT"]
536 res = ctx.execute([executable_path(ctx, "go"), "env", "GOROOT"])
537 if res.return_code:
538 fail("Could not detect host go version")
539 root = res.stdout.strip()
540 if not root:
541 fail("host go version failed to report it's GOROOT")
542 return root
543
544def _detect_sdk_platform(ctx, goroot):
545 path = ctx.path(goroot + "/pkg/tool")
546 if not path.exists:
547 fail("Could not detect SDK platform: failed to find " + str(path))
548 tool_entries = path.readdir()
549
550 platforms = []
551 for f in tool_entries:
552 if f.basename.find("_") >= 0:
553 platforms.append(f.basename)
554
555 if len(platforms) == 0:
556 fail("Could not detect SDK platform: found no platforms in %s" % path)
557 if len(platforms) > 1:
558 fail("Could not detect SDK platform: found multiple platforms %s in %s" % (platforms, path))
559 return platforms[0]
560
561def _detect_sdk_version(ctx, goroot):
562 version_file_path = goroot + "/VERSION"
563 if ctx.path(version_file_path).exists:
564 # VERSION file has version prefixed by go, eg. go1.18.3
565 # 1.21: The version is the first line
566 version_line = ctx.read(version_file_path).splitlines()[0]
567 version = version_line[2:]
568 if ctx.attr.version and ctx.attr.version != version:
569 fail("SDK is version %s, but version %s was expected" % (version, ctx.attr.version))
570 return version
571
572 # The top-level VERSION file does not exist in all Go SDK distributions, e.g. those shipped by Debian or Fedora.
573 # Falling back to running "go version"
574 go_binary_path = goroot + "/bin/go"
575 result = ctx.execute([go_binary_path, "version"])
576 if result.return_code != 0:
577 fail("Could not detect SDK version: '%s version' exited with exit code %d" % (go_binary_path, result.return_code))
578
579 # go version output is of the form "go version go1.18.3 linux/amd64" or "go
580 # version devel go1.19-fd1b5904ae Tue Mar 22 21:38:10 2022 +0000
581 # linux/amd64". See the following links for how this output is generated:
582 # - https://github.com/golang/go/blob/2bdb5c57f1efcbddab536028d053798e35de6226/src/cmd/go/internal/version/version.go#L75
583 # - https://github.com/golang/go/blob/2bdb5c57f1efcbddab536028d053798e35de6226/src/cmd/dist/build.go#L333
584 #
585 # Read the third word, or the fourth word if the third word is "devel", to
586 # find the version number.
587 output_parts = result.stdout.split(" ")
588 if len(output_parts) > 2 and output_parts[2].startswith("go"):
589 version = output_parts[2][len("go"):]
590 elif len(output_parts) > 3 and output_parts[2] == "devel" and output_parts[3].startswith("go"):
591 version = output_parts[3][len("go"):]
592 else:
593 fail("Could not parse SDK version from '%s version' output: %s" % (go_binary_path, result.stdout))
594 if parse_version(version) == None:
595 fail("Could not parse SDK version from '%s version' output: %s" % (go_binary_path, result.stdout))
596 if ctx.attr.version and ctx.attr.version != version:
597 fail("SDK is version %s, but version %s was expected" % (version, ctx.attr.version))
598 return version
599
600def _parse_versions_json(data):
601 """Parses version metadata returned by go.dev.
602
603 Args:
604 data: the contents of the file downloaded from
605 https://go.dev/dl/?mode=json. We assume the file is valid
606 JSON, is spaced and indented, and is in a particular format.
607
608 Return:
609 A dict mapping version strings (like "1.15.5") to dicts mapping
610 platform names (like "linux_amd64") to pairs of filenames
611 (like "go1.15.5.linux-amd64.tar.gz") and hex-encoded SHA-256 sums.
612 """
613 sdks = json.decode(data)
614 return {
615 sdk["version"][len("go"):]: {
616 "%s_%s" % (file["os"], file["arch"]): (
617 file["filename"],
618 file["sha256"],
619 )
620 for file in sdk["files"]
621 if file["kind"] == "archive"
622 }
623 for sdk in sdks
624 }
625
626def parse_version(version):
627 """Parses a version string like "1.15.5" and returns a tuple of numbers or None"""
628 l, r = 0, 0
629 parsed = []
630 for c in version.elems():
631 if c == ".":
632 if l == r:
633 # empty component
634 return None
635 parsed.append(int(version[l:r]))
636 r += 1
637 l = r
638 continue
639
640 if c.isdigit():
641 r += 1
642 continue
643
644 # pre-release suffix
645 break
646
647 if l == r:
648 # empty component
649 return None
650 parsed.append(int(version[l:r]))
651 if len(parsed) == 2:
652 # first minor version, like (1, 15)
653 parsed.append(0)
654 if len(parsed) != 3:
655 # too many or too few components
656 return None
657 if r < len(version):
658 # pre-release suffix
659 parsed.append(version[r:])
660 return tuple(parsed)
661
662def _version_is_prerelease(v):
663 return len(v) > 3
664
665def _version_less(a, b):
666 if a[:3] < b[:3]:
667 return True
668 if a[:3] > b[:3]:
669 return False
670 if len(a) > len(b):
671 return True
672 if len(a) < len(b) or len(a) == 3:
673 return False
674 return a[3:] < b[3:]
675
676def _version_string(v):
677 suffix = v[3] if _version_is_prerelease(v) else ""
678 if v[-1] == 0:
679 v = v[:-1]
680 return ".".join([str(n) for n in v]) + suffix
681
682def _have_same_length(*lists):
683 if not lists:
684 fail("expected at least one list")
685 return len({len(l): None for l in lists}) == 1
686
687def go_register_toolchains(version = None, nogo = None, go_version = None, experiments = None):
688 """See /go/toolchains.rst#go-register-toolchains for full documentation."""
689 if not version:
690 version = go_version # old name
691
692 sdk_kinds = ("go_download_sdk_rule", "go_host_sdk_rule", "_go_local_sdk", "_go_wrap_sdk")
693 existing_rules = native.existing_rules()
694 sdk_rules = [r for r in existing_rules.values() if r["kind"] in sdk_kinds]
695 if len(sdk_rules) == 0 and "go_sdk" in existing_rules:
696 # may be local_repository in bazel_tests.
697 sdk_rules.append(existing_rules["go_sdk"])
698
699 if version and len(sdk_rules) > 0:
700 fail("go_register_toolchains: version set after go sdk rule declared ({})".format(", ".join([r["name"] for r in sdk_rules])))
701 if len(sdk_rules) == 0:
702 if not version:
703 fail('go_register_toolchains: version must be a string like "1.15.5" or "host"')
704 elif version == "host":
705 go_host_sdk(name = "go_sdk", experiments = experiments)
706 else:
707 pv = parse_version(version)
708 if not pv:
709 fail('go_register_toolchains: version must be a string like "1.15.5" or "host"')
710 if _version_less(pv, MIN_SUPPORTED_VERSION):
711 print("DEPRECATED: Go versions before {} are not supported and may not work".format(_version_string(MIN_SUPPORTED_VERSION)))
712 go_download_sdk(
713 name = "go_sdk",
714 version = version,
715 experiments = experiments,
716 )
717
718 if nogo:
719 # Override default definition in go_rules_dependencies().
720 go_register_nogo(
721 name = "io_bazel_rules_nogo",
722 nogo = nogo,
723 )
View as plain text