...

Text file src/github.com/bazelbuild/bazel-gazelle/internal/bzlmod/go_deps.bzl

Documentation: github.com/bazelbuild/bazel-gazelle/internal/bzlmod

     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