1# Copyright 2023 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("//internal:go_repository.bzl", "go_repository")
16load(":go_mod.bzl", "deps_from_go_mod", "sums_from_go_mod")
17load(
18 ":default_gazelle_overrides.bzl",
19 "DEFAULT_BUILD_EXTRA_ARGS_BY_PATH",
20 "DEFAULT_BUILD_FILE_GENERATION_BY_PATH",
21 "DEFAULT_DIRECTIVES_BY_PATH",
22)
23load(":semver.bzl", "semver")
24load(
25 ":utils.bzl",
26 "drop_nones",
27 "extension_metadata",
28 "format_rule_call",
29 "get_directive_value",
30 "with_replaced_or_new_fields",
31)
32
33visibility("//")
34
35_HIGHEST_VERSION_SENTINEL = semver.to_comparable("999999.999999.999999")
36
37_FORBIDDEN_OVERRIDE_TAG = """\
38Using the "go_deps.{tag_class}" tag in a non-root Bazel module is forbidden, \
39but module "{module_name}" requests it.
40
41If you need this override for a Bazel module that will be available in a public \
42registry (such as the Bazel Central Registry), please file an issue at \
43https://github.com/bazelbuild/bazel-gazelle/issues/new or submit a PR adding \
44the required directives to the "default_gazelle_overrides.bzl" file at \
45https://github.com/bazelbuild/bazel-gazelle/tree/master/internal/bzlmod/default_gazelle_overrides.bzl.
46"""
47
48_GAZELLE_ATTRS = {
49 "build_file_generation": attr.string(
50 default = "on",
51 doc = """One of `"auto"`, `"on"` (default), `"off"`.
52
53 Whether Gazelle should generate build files for the Go module.
54
55 Although "auto" is the default globally for build_file_generation,
56 if a `"gazelle_override"` or `"gazelle_default_attributes"` tag is present
57 for a Go module, the `"build_file_generation"` attribute will default to "on"
58 since these tags indicate the presence of `"directives"` or `"build_extra_args"`.
59
60 In `"auto"` mode, Gazelle will run if there is no build file in the Go
61 module's root directory.
62
63 """,
64 values = [
65 "auto",
66 "off",
67 "on",
68 ],
69 ),
70 "build_extra_args": attr.string_list(
71 default = [],
72 doc = """
73 A list of additional command line arguments to pass to Gazelle when generating build files.
74 """,
75 ),
76 "directives": attr.string_list(
77 doc = """Gazelle configuration directives to use for this Go module's external repository.
78
79 Each directive uses the same format as those that Gazelle
80 accepts as comments in Bazel source files, with the
81 directive name followed by optional arguments separated by
82 whitespace.""",
83 ),
84}
85
86def _fail_on_non_root_overrides(module_ctx, module, tag_class):
87 if module.is_root:
88 return
89
90 # Isolated module extension usages only contain tags from a single module, so we can allow
91 # overrides. This is a new feature in Bazel 6.3.0, earlier versions do not allow module usages
92 # to be isolated.
93 if getattr(module_ctx, "is_isolated", False):
94 return
95
96 if getattr(module.tags, tag_class):
97 fail(_FORBIDDEN_OVERRIDE_TAG.format(
98 tag_class = tag_class,
99 module_name = module.name,
100 ))
101
102def _fail_on_duplicate_overrides(path, module_name, overrides):
103 if path in overrides:
104 fail("Multiple overrides defined for Go module path \"{}\" in module \"{}\".".format(path, module_name))
105
106def _fail_on_unmatched_overrides(override_keys, resolutions, override_name):
107 unmatched_overrides = [path for path in override_keys if path not in resolutions]
108 if unmatched_overrides:
109 fail("Some {} did not target a Go module with a matching path: {}".format(
110 override_name,
111 ", ".join(unmatched_overrides),
112 ))
113
114def _check_directive(directive):
115 if directive.startswith("gazelle:") and " " in directive and not directive[len("gazelle:"):][0].isspace():
116 return
117 fail("Invalid Gazelle directive: \"{}\". Gazelle directives must be of the form \"gazelle:key value\".".format(directive))
118
119def _get_override_or_default(specific_overrides, gazelle_default_attributes, default_path_overrides, path, default_value, attribute_name):
120 # 1st: Check for user-provided specific overrides. If a specific override is found,
121 # all of its attributes will be applied (even if left to the tag's default). This is to allow
122 # users to override the gazelle_default_attributes tag back to the tag's default.
123 #
124 # This will also cause "build_file_generation" to default to "on" if a specific override is found.
125 specific_override = specific_overrides.get(path)
126 if specific_override and hasattr(specific_override, attribute_name):
127 return getattr(specific_override, attribute_name)
128
129 # 2nd. Check for default attributes provided by the user. This must be done before checking for
130 # gazelle's defaults path overrides to prevent Gazelle from overriding a user-specified flag.
131 #
132 # This will also cause "build_file_generation" to default to "on" if default attributes are found.
133 global_override_value = getattr(gazelle_default_attributes, attribute_name, None)
134 if global_override_value:
135 return global_override_value
136
137 # 3rd: Check for default overrides for specific path.
138 default_path_override = default_path_overrides.get(path)
139 if default_path_override:
140 return default_path_override
141
142 # 4th. Return the default value if no override was found.
143 # This will cause "build_file_generation" to default to "auto".
144 return default_value
145
146def _get_directives(path, gazelle_overrides, gazelle_default_attributes):
147 return _get_override_or_default(gazelle_overrides, gazelle_default_attributes, DEFAULT_DIRECTIVES_BY_PATH, path, [], "directives")
148
149def _get_build_file_generation(path, gazelle_overrides, gazelle_default_attributes):
150 # The default value for build_file_generation is "auto" if no override is found, but will default to "on" if an override is found.
151 return _get_override_or_default(gazelle_overrides, gazelle_default_attributes, DEFAULT_BUILD_FILE_GENERATION_BY_PATH, path, "auto", "build_file_generation")
152
153def _get_build_extra_args(path, gazelle_overrides, gazelle_default_attributes):
154 return _get_override_or_default(gazelle_overrides, gazelle_default_attributes, DEFAULT_BUILD_EXTRA_ARGS_BY_PATH, path, [], "build_extra_args")
155
156def _get_patches(path, module_overrides):
157 return _get_override_or_default(module_overrides, struct(), {}, path, [], "patches")
158
159def _get_patch_args(path, module_overrides):
160 override = _get_override_or_default(module_overrides, struct(), {}, path, None, "patch_strip")
161 return ["-p{}".format(override)] if override else []
162
163def _repo_name(importpath):
164 path_segments = importpath.split("/")
165 segments = reversed(path_segments[0].split(".")) + path_segments[1:]
166 candidate_name = "_".join(segments).replace("-", "_")
167 return "".join([c.lower() if c.isalnum() else "_" for c in candidate_name.elems()])
168
169def _is_dev_dependency(module_ctx, tag):
170 if hasattr(tag, "_is_dev_dependency"):
171 # Synthetic tags generated from go_deps.from_file have this "hidden" attribute.
172 return tag._is_dev_dependency
173
174 # This function is available in Bazel 6.2.0 and later. This is the same version that has
175 # module_ctx.extension_metadata, so the return value of this function is not used if it is
176 # not available.
177 return module_ctx.is_dev_dependency(tag) if hasattr(module_ctx, "is_dev_dependency") else False
178
179def _intersperse_newlines(tags):
180 return [tag for p in zip(tags, len(tags) * ["\n"]) for tag in p]
181
182# This function processes the gazelle_default_attributes tag for a given module and returns a struct
183# containing the attributes from _GAZELLE_ATTRS that are defined in the tag.
184def _process_gazelle_default_attributes(module_ctx):
185 for module in module_ctx.modules:
186 _fail_on_non_root_overrides(module_ctx, module, "gazelle_default_attributes")
187
188 for module in module_ctx.modules:
189 tags = module.tags.gazelle_default_attributes
190 if not tags:
191 continue
192
193 if len(tags) > 1:
194 fail(
195 "go_deps.gazelle_default_attributes: only one tag can be specified per module, got:\n",
196 *[t for p in zip(module.tags.gazelle_default_attributes, len(module.tags.gazelle_default_attributes) * ["\n"]) for t in p]
197 )
198
199 tag = tags[0]
200 return struct(**{
201 attr: getattr(tag, attr)
202 for attr in _GAZELLE_ATTRS.keys()
203 if hasattr(tag, attr)
204 })
205
206 return None
207
208# This function processes a given override type for a given module, checks for duplicate overrides
209# and inserts the override returned from the process_override_func into the overrides dict.
210def _process_overrides(module_ctx, module, override_type, overrides, process_override_func, additional_overrides = None):
211 _fail_on_non_root_overrides(module_ctx, module, override_type)
212 for override_tag in getattr(module.tags, override_type):
213 _fail_on_duplicate_overrides(override_tag.path, module.name, overrides)
214
215 # Some overrides conflict with other overrides. These can be specified in the
216 # additional_overrides dict. If the override is in the additional_overrides dict, then fail.
217 if additional_overrides:
218 _fail_on_duplicate_overrides(override_tag.path, module.name, additional_overrides)
219
220 overrides[override_tag.path] = process_override_func(override_tag)
221
222def _process_gazelle_override(gazelle_override_tag):
223 for directive in gazelle_override_tag.directives:
224 _check_directive(directive)
225
226 return struct(**{
227 attr: getattr(gazelle_override_tag, attr)
228 for attr in _GAZELLE_ATTRS.keys()
229 if hasattr(gazelle_override_tag, attr)
230 })
231
232def _process_module_override(module_override_tag):
233 return struct(
234 patches = module_override_tag.patches,
235 patch_strip = module_override_tag.patch_strip,
236 )
237
238def _process_archive_override(archive_override_tag):
239 return struct(
240 urls = archive_override_tag.urls,
241 sha256 = archive_override_tag.sha256,
242 strip_prefix = archive_override_tag.strip_prefix,
243 patches = archive_override_tag.patches,
244 patch_strip = archive_override_tag.patch_strip,
245 )
246
247def _go_repository_config_impl(ctx):
248 repos = []
249 for name, importpath in sorted(ctx.attr.importpaths.items()):
250 repos.append(format_rule_call(
251 "go_repository",
252 name = name,
253 importpath = importpath,
254 module_name = ctx.attr.module_names.get(name),
255 build_naming_convention = ctx.attr.build_naming_conventions.get(name),
256 ))
257
258 ctx.file("WORKSPACE", "\n".join(repos))
259 ctx.file("BUILD.bazel", "exports_files(['WORKSPACE', 'config.json'])")
260 ctx.file("go_env.bzl", content = "GO_ENV = " + repr(ctx.attr.go_env))
261
262 # For use by @rules_go//go.
263 ctx.file("config.json", content = json.encode_indent({
264 "go_env": ctx.attr.go_env,
265 "dep_files": ctx.attr.dep_files,
266 }))
267
268_go_repository_config = repository_rule(
269 implementation = _go_repository_config_impl,
270 attrs = {
271 "importpaths": attr.string_dict(mandatory = True),
272 "module_names": attr.string_dict(mandatory = True),
273 "build_naming_conventions": attr.string_dict(mandatory = True),
274 "go_env": attr.string_dict(mandatory = True),
275 "dep_files": attr.string_list(),
276 },
277)
278
279def _noop(_):
280 pass
281
282# These repos are shared between the isolated and non-isolated instances of go_deps as they are
283# referenced directly by rules (go_proto_library) and would result in linker errors due to duplicate
284# packages if they were resolved separately.
285# When adding a new Go module to this list, make sure that:
286# 1. The corresponding repository is visible to the gazelle module via a use_repo directive.
287# 2. All transitive dependencies of the module are also in this list. Avoid adding module that have
288# a large number of transitive dependencies.
289_SHARED_REPOS = [
290 "github.com/golang/protobuf",
291 "google.golang.org/protobuf",
292]
293
294def _go_deps_impl(module_ctx):
295 module_resolutions = {}
296 sums = {}
297 replace_map = {}
298 bazel_deps = {}
299
300 gazelle_default_attributes = _process_gazelle_default_attributes(module_ctx)
301 archive_overrides = {}
302 gazelle_overrides = {}
303 module_overrides = {}
304
305 root_versions = {}
306 root_module_direct_deps = {}
307 root_module_direct_dev_deps = {}
308
309 first_module = module_ctx.modules[0]
310 if first_module.is_root and first_module.name in ["gazelle", "rules_go"]:
311 root_module_direct_deps["bazel_gazelle_go_repository_config"] = None
312
313 outdated_direct_dep_printer = print
314 go_env = {}
315 dep_files = []
316 for module in module_ctx.modules:
317 if len(module.tags.config) > 1:
318 fail(
319 "Multiple \"go_deps.config\" tags defined in module \"{}\":\n".format(module.name),
320 *_intersperse_newlines(module.tags.config)
321 )
322
323 # Parse the go_deps.config tag of the root module only.
324 for mod_config in module.tags.config:
325 if not module.is_root:
326 continue
327 check_direct_deps = mod_config.check_direct_dependencies
328 if check_direct_deps == "off":
329 outdated_direct_dep_printer = _noop
330 elif check_direct_deps == "warning":
331 outdated_direct_dep_printer = print
332 elif check_direct_deps == "error":
333 outdated_direct_dep_printer = fail
334 go_env = mod_config.go_env
335
336 _process_overrides(module_ctx, module, "gazelle_override", gazelle_overrides, _process_gazelle_override)
337 _process_overrides(module_ctx, module, "module_override", module_overrides, _process_module_override, archive_overrides)
338 _process_overrides(module_ctx, module, "archive_override", archive_overrides, _process_archive_override, module_overrides)
339
340 if len(module.tags.from_file) > 1:
341 fail(
342 "Multiple \"go_deps.from_file\" tags defined in module \"{}\": {}".format(
343 module.name,
344 ", ".join([str(tag.go_mod) for tag in module.tags.from_file]),
345 ),
346 )
347 additional_module_tags = []
348 for from_file_tag in module.tags.from_file:
349 module_path, module_tags_from_go_mod, go_mod_replace_map = deps_from_go_mod(module_ctx, from_file_tag.go_mod)
350
351 # Collect the relative path of the root module's go.mod file if it lives in the main
352 # repository.
353 if module.is_root and not from_file_tag.go_mod.workspace_name:
354 go_mod = "go.mod"
355 if from_file_tag.go_mod.package:
356 go_mod = from_file_tag.go_mod.package + "/" + go_mod
357 dep_files.append(go_mod)
358
359 is_dev_dependency = _is_dev_dependency(module_ctx, from_file_tag)
360 additional_module_tags += [
361 with_replaced_or_new_fields(tag, _is_dev_dependency = is_dev_dependency)
362 for tag in module_tags_from_go_mod
363 ]
364
365 if module.is_root or getattr(module_ctx, "is_isolated", False):
366 replace_map.update(go_mod_replace_map)
367 else:
368 # Register this Bazel module as providing the specified Go module. It participates
369 # in version resolution using its registry version, which uses a relaxed variant of
370 # semver that can however still be compared to strict semvers.
371 # An empty version string signals an override, which is assumed to be newer than any
372 # other version.
373 raw_version = _canonicalize_raw_version(module.version)
374 version = semver.to_comparable(raw_version, relaxed = True) if raw_version else _HIGHEST_VERSION_SENTINEL
375 if module_path not in bazel_deps or version > bazel_deps[module_path].version:
376 bazel_deps[module_path] = struct(
377 module_name = module.name,
378 repo_name = "@" + from_file_tag.go_mod.workspace_name,
379 version = version,
380 raw_version = raw_version,
381 )
382
383 # Load all sums from transitively resolved `go.sum` files that have modules.
384 if len(module_tags_from_go_mod) > 0:
385 for entry, new_sum in sums_from_go_mod(module_ctx, from_file_tag.go_mod).items():
386 _safe_insert_sum(sums, entry, new_sum)
387
388 # Load sums from manually specified modules separately.
389 for module_tag in module.tags.module:
390 if module_tag.build_naming_convention:
391 fail("""The "build_naming_convention" attribute is no longer supported for "go_deps.module" tags. Use a "gazelle:go_naming_convention" directive via the "gazelle_override" tag's "directives" attribute instead.""")
392 if module_tag.build_file_proto_mode:
393 fail("""The "build_file_proto_mode" attribute is no longer supported for "go_deps.module" tags. Use a "gazelle:proto" directive via the "gazelle_override" tag's "directives" attribute instead.""")
394 sum_version = _canonicalize_raw_version(module_tag.version)
395 _safe_insert_sum(sums, (module_tag.path, sum_version), module_tag.sum)
396
397 # Parse the go_dep.module tags of all transitive dependencies and apply
398 # Minimum Version Selection to resolve importpaths to Go module versions
399 # and sums.
400 #
401 # Note: This applies Minimum Version Selection on the resolved
402 # dependency graphs of all transitive Bazel module dependencies, which
403 # is not what `go mod` does. But since this algorithm ends up using only
404 # Go module versions that have been explicitly declared somewhere in the
405 # full graph, we can assume that at that place all its required
406 # transitive dependencies have also been declared - we may end up
407 # resolving them to higher versions, but only compatible ones.
408 paths = {}
409 for module_tag in module.tags.module + additional_module_tags:
410 if module_tag.path in paths:
411 fail("Duplicate Go module path \"{}\" in module \"{}\".".format(module_tag.path, module.name))
412 if module_tag.path in bazel_deps:
413 continue
414 paths[module_tag.path] = None
415 raw_version = _canonicalize_raw_version(module_tag.version)
416
417 # For modules imported from a go.sum, we know which ones are direct
418 # dependencies and can thus only report implicit version upgrades
419 # for direct dependencies. For manually specified go_deps.module
420 # tags, we always report version upgrades unless users override with
421 # the "indirect" attribute.
422 if module.is_root and not module_tag.indirect:
423 root_versions[module_tag.path] = raw_version
424 if _is_dev_dependency(module_ctx, module_tag):
425 root_module_direct_dev_deps[_repo_name(module_tag.path)] = None
426 else:
427 root_module_direct_deps[_repo_name(module_tag.path)] = None
428
429 version = semver.to_comparable(raw_version)
430 if module_tag.path not in module_resolutions or version > module_resolutions[module_tag.path].version:
431 module_resolutions[module_tag.path] = struct(
432 repo_name = _repo_name(module_tag.path),
433 version = version,
434 raw_version = raw_version,
435 )
436
437 _fail_on_unmatched_overrides(archive_overrides.keys(), module_resolutions, "archive_overrides")
438 _fail_on_unmatched_overrides(gazelle_overrides.keys(), module_resolutions, "gazelle_overrides")
439 _fail_on_unmatched_overrides(module_overrides.keys(), module_resolutions, "module_overrides")
440
441 # All `replace` directives are applied after version resolution.
442 # We can simply do this by checking the replace paths' existence
443 # in the module resolutions and swapping out the entry.
444 for path, replace in replace_map.items():
445 if path in module_resolutions:
446 # If the replace directive specified a version then we only
447 # apply it if the versions match.
448 if replace.from_version:
449 comparable_from_version = semver.to_comparable(replace.from_version)
450 if module_resolutions[path].version != comparable_from_version:
451 continue
452
453 new_version = semver.to_comparable(replace.version)
454 module_resolutions[path] = with_replaced_or_new_fields(
455 module_resolutions[path],
456 replace = replace.to_path,
457 version = new_version,
458 raw_version = replace.version,
459 )
460 if path in root_versions:
461 if replace != replace.to_path:
462 # If the root module replaces a Go module with a completely different one, do
463 # not ever report an implicit version upgrade.
464 root_versions.pop(path)
465 else:
466 root_versions[path] = replace.version
467
468 for path, bazel_dep in bazel_deps.items():
469 # We can't apply overrides to Bazel dependencies and thus fall back to using the Go module.
470 if path in archive_overrides or path in gazelle_overrides or path in module_overrides or path in replace_map:
471 continue
472
473 # Only use the Bazel module if it is at least as high as the required Go module version.
474 if path in module_resolutions and bazel_dep.version < module_resolutions[path].version:
475 outdated_direct_dep_printer(
476 "Go module \"{path}\" is provided by Bazel module \"{bazel_module}\" in version {bazel_dep_version}, but requested at higher version {go_version} via Go requirements. Consider adding or updating an appropriate \"bazel_dep\" to ensure that the Bazel module is used to provide the Go module.".format(
477 path = path,
478 bazel_module = bazel_dep.module_name,
479 bazel_dep_version = bazel_dep.raw_version,
480 go_version = module_resolutions[path].raw_version,
481 ),
482 )
483 continue
484
485 # TODO: We should update root_versions if the bazel_dep is a direct dependency of the root
486 # module. However, we currently don't have a way to determine that.
487 module_resolutions[path] = bazel_dep
488
489 for path, root_version in root_versions.items():
490 if semver.to_comparable(root_version) < module_resolutions[path].version:
491 outdated_direct_dep_printer(
492 "For Go module \"{path}\", the root module requires module version v{root_version}, but got v{resolved_version} in the resolved dependency graph.".format(
493 path = path,
494 root_version = root_version,
495 resolved_version = module_resolutions[path].raw_version,
496 ),
497 )
498
499 for path, module in module_resolutions.items():
500 if hasattr(module, "module_name"):
501 # Do not create a go_repository for a Go module provided by a bazel_dep.
502 root_module_direct_deps.pop(_repo_name(path), default = None)
503 root_module_direct_dev_deps.pop(_repo_name(path), default = None)
504 continue
505 if getattr(module_ctx, "is_isolated", False) and path in _SHARED_REPOS:
506 # Do not create a go_repository for a dep shared with the non-isolated instance of
507 # go_deps.
508 continue
509
510 go_repository_args = {
511 "name": module.repo_name,
512 "importpath": path,
513 "build_directives": _get_directives(path, gazelle_overrides, gazelle_default_attributes),
514 "build_file_generation": _get_build_file_generation(path, gazelle_overrides, gazelle_default_attributes),
515 "build_extra_args": _get_build_extra_args(path, gazelle_overrides, gazelle_default_attributes),
516 "patches": _get_patches(path, module_overrides),
517 "patch_args": _get_patch_args(path, module_overrides),
518 }
519
520 archive_override = archive_overrides.get(path)
521 if archive_override:
522 go_repository_args.update({
523 "urls": archive_override.urls,
524 "strip_prefix": archive_override.strip_prefix,
525 "sha256": archive_override.sha256,
526 "patches": _get_patches(path, archive_overrides),
527 "patch_args": _get_patch_args(path, archive_overrides),
528 })
529 else:
530 go_repository_args.update({
531 "sum": _get_sum_from_module(path, module, sums),
532 "replace": getattr(module, "replace", None),
533 "version": "v" + module.raw_version,
534 })
535
536 go_repository(**go_repository_args)
537
538 # Create a synthetic WORKSPACE file that lists all Go repositories created
539 # above and contains all the information required by Gazelle's -repo_config
540 # to generate BUILD files for external Go modules. This skips the need to
541 # run generate_repo_config. Only "importpath" and "build_naming_convention"
542 # are relevant.
543 _go_repository_config(
544 name = "bazel_gazelle_go_repository_config",
545 importpaths = {
546 module.repo_name: path
547 for path, module in module_resolutions.items()
548 },
549 module_names = {
550 info.repo_name: info.module_name
551 for path, info in bazel_deps.items()
552 },
553 build_naming_conventions = drop_nones({
554 module.repo_name: get_directive_value(
555 _get_directives(path, gazelle_overrides, gazelle_default_attributes),
556 "go_naming_convention",
557 )
558 for path, module in module_resolutions.items()
559 }),
560 go_env = go_env,
561 dep_files = dep_files,
562 )
563
564 return extension_metadata(
565 module_ctx,
566 root_module_direct_deps = root_module_direct_deps.keys(),
567 # If a Go module appears as both a dev and a non-dev dependency, it has to be imported as a
568 # non-dev dependency.
569 root_module_direct_dev_deps = {
570 repo_name: None
571 for repo_name in root_module_direct_dev_deps.keys()
572 if repo_name not in root_module_direct_deps
573 }.keys(),
574 reproducible = True,
575 )
576
577def _get_sum_from_module(path, module, sums):
578 entry = (path, module.raw_version)
579 if hasattr(module, "replace"):
580 entry = (module.replace, module.raw_version)
581
582 if entry not in sums:
583 fail("No sum for {}@{} found".format(path, module.raw_version))
584
585 return sums[entry]
586
587def _safe_insert_sum(sums, entry, new_sum):
588 if entry in sums and new_sum != sums[entry]:
589 fail("Multiple mismatching sums for {}@{} found.".format(entry[0], entry[1]))
590 sums[entry] = new_sum
591
592def _canonicalize_raw_version(raw_version):
593 if raw_version.startswith("v"):
594 return raw_version[1:]
595 return raw_version
596
597_config_tag = tag_class(
598 attrs = {
599 "check_direct_dependencies": attr.string(
600 values = ["off", "warning", "error"],
601 ),
602 "go_env": attr.string_dict(
603 doc = "The environment variables to use when fetching Go dependencies or running the `@rules_go//go` tool.",
604 ),
605 },
606)
607
608_from_file_tag = tag_class(
609 attrs = {
610 "go_mod": attr.label(mandatory = True),
611 },
612)
613
614_module_tag = tag_class(
615 attrs = {
616 "path": attr.string(mandatory = True),
617 "version": attr.string(mandatory = True),
618 "sum": attr.string(),
619 "indirect": attr.bool(
620 doc = """Whether this Go module is an indirect dependency.""",
621 default = False,
622 ),
623 "build_naming_convention": attr.string(doc = """Removed, do not use""", default = ""),
624 "build_file_proto_mode": attr.string(doc = """Removed, do not use""", default = ""),
625 },
626)
627
628_archive_override_tag = tag_class(
629 attrs = {
630 "path": attr.string(
631 doc = """The Go module path for the repository to be overridden.
632
633 This module path must be defined by other tags in this
634 extension within this Bazel module.""",
635 mandatory = True,
636 ),
637 "urls": attr.string_list(
638 doc = """A list of HTTP(S) URLs where an archive containing the project can be
639 downloaded. Bazel will attempt to download from the first URL; the others
640 are mirrors.""",
641 ),
642 "strip_prefix": attr.string(
643 doc = """If the repository is downloaded via HTTP (`urls` is set), this is a
644 directory prefix to strip. See [`http_archive.strip_prefix`].""",
645 ),
646 "sha256": attr.string(
647 doc = """If the repository is downloaded via HTTP (`urls` is set), this is the
648 SHA-256 sum of the downloaded archive. When set, Bazel will verify the archive
649 against this sum before extracting it.""",
650 ),
651 "patches": attr.label_list(
652 doc = "A list of patches to apply to the repository *after* gazelle runs.",
653 ),
654 "patch_strip": attr.int(
655 default = 0,
656 doc = "The number of leading path segments to be stripped from the file name in the patches.",
657 ),
658 },
659 doc = "Override the default source location on a given Go module in this extension.",
660)
661
662_gazelle_override_tag = tag_class(
663 attrs = {
664 "path": attr.string(
665 doc = """The Go module path for the repository to be overridden.
666
667 This module path must be defined by other tags in this
668 extension within this Bazel module.""",
669 mandatory = True,
670 ),
671 } | _GAZELLE_ATTRS,
672 doc = "Override Gazelle's behavior on a given Go module defined by other tags in this extension.",
673)
674
675_gazelle_default_attributes_tag = tag_class(
676 attrs = _GAZELLE_ATTRS,
677 doc = "Override Gazelle's default attribute values for all modules in this extension.",
678)
679
680_module_override_tag = tag_class(
681 attrs = {
682 "path": attr.string(
683 doc = """The Go module path for the repository to be overridden.
684
685 This module path must be defined by other tags in this
686 extension within this Bazel module.""",
687 mandatory = True,
688 ),
689 "patches": attr.label_list(
690 doc = "A list of patches to apply to the repository *after* gazelle runs.",
691 ),
692 "patch_strip": attr.int(
693 default = 0,
694 doc = "The number of leading path segments to be stripped from the file name in the patches.",
695 ),
696 },
697 doc = "Apply patches to a given Go module defined by other tags in this extension.",
698)
699
700go_deps = module_extension(
701 _go_deps_impl,
702 tag_classes = {
703 "archive_override": _archive_override_tag,
704 "config": _config_tag,
705 "from_file": _from_file_tag,
706 "gazelle_override": _gazelle_override_tag,
707 "gazelle_default_attributes": _gazelle_default_attributes_tag,
708 "module": _module_tag,
709 "module_override": _module_override_tag,
710 },
711)
View as plain text