Skip to content

Commit

Permalink
allow global overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
tyler-french committed Feb 14, 2024
1 parent 55b3ce6 commit 4cbbf20
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 87 deletions.
167 changes: 104 additions & 63 deletions internal/bzlmod/go_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,36 @@ the required directives to the "default_gazelle_overrides.bzl" file at \
https://github.com/bazelbuild/bazel-gazelle/tree/master/internal/bzlmod/default_gazelle_overrides.bzl.
"""

_GAZELLE_ATTRS = {
"build_file_generation": attr.string(
default = "auto",
doc = """One of `"auto"` (default), `"on"`, `"off"`.
Whether Gazelle should generate build files for the Go module. In
`"auto"` mode, Gazelle will run if there is no build file in the Go
module's root directory.""",
values = [
"auto",
"off",
"on",
],
),
"build_extra_args": attr.string_list(
default = [],
doc = """
A list of additional command line arguments to pass to Gazelle when generating build files.
""",
),
"directives": attr.string_list(
doc = """Gazelle configuration directives to use for this Go module's external repository.
Each directive uses the same format as those that Gazelle
accepts as comments in Bazel source files, with the
directive name followed by optional arguments separated by
whitespace.""",
),
}

def _fail_on_non_root_overrides(module_ctx, module, tag_class):
if module.is_root:
return
Expand All @@ -68,45 +98,51 @@ def _fail_on_unmatched_overrides(override_keys, resolutions, override_name):
unmatched_overrides = [path for path in override_keys if path not in resolutions]
if unmatched_overrides:
fail("Some {} did not target a Go module with a matching path: {}".format(
override_name, ", ".join(unmatched_overrides)
override_name,
", ".join(unmatched_overrides),
))

def _check_directive(directive):
if directive.startswith("gazelle:") and " " in directive and not directive[len("gazelle:"):][0].isspace():
return
fail("Invalid Gazelle directive: \"{}\". Gazelle directives must be of the form \"gazelle:key value\".".format(directive))

def _get_build_file_generation(path, gazelle_overrides):
override = gazelle_overrides.get(path)
if override:
return override.build_file_generation
def _get_override_or_default(specific_overrides, gazelle_default_attributes, default_path_overrides, path, default_value, attribute_name):
# 1st: Check for user-provided specific overrides. If a specific override is found,
# all of its attributes will be applied (even if left to the tag's default). This is to allow
# users to override the gazelle_default_attributes tag back to the tag's default
specific_override = specific_overrides.get(path)
if specific_override and hasattr(specific_override, attribute_name):
return getattr(specific_override, attribute_name)

# 2nd. Check for default attributes provided by the user.
global_override_value = getattr(gazelle_default_attributes, attribute_name, None)
if global_override_value:
return global_override_value

# 3rd: Check for default overrides for specific path.
default_path_override = default_path_overrides.get(path)
if default_path_override:
return default_path_override

return DEFAULT_BUILD_FILE_GENERATION_BY_PATH.get(path, "auto")
# 4th. Return the default value if no override was found.
return default_value

def _get_build_extra_args(path, gazelle_overrides):
override = gazelle_overrides.get(path)
if override:
return override.build_extra_args
return DEFAULT_BUILD_EXTRA_ARGS_BY_PATH.get(path, [])
def _get_directives(path, gazelle_overrides, gazelle_default_attributes):
return _get_override_or_default(gazelle_overrides, gazelle_default_attributes, DEFAULT_DIRECTIVES_BY_PATH, path, [], "directives")

def _get_directives(path, gazelle_overrides):
override = gazelle_overrides.get(path)
if override:
return override.directives
def _get_build_file_generation(path, gazelle_overrides, gazelle_default_attributes):
return _get_override_or_default(gazelle_overrides, gazelle_default_attributes, DEFAULT_BUILD_FILE_GENERATION_BY_PATH, path, "auto", "build_file_generation")

return DEFAULT_DIRECTIVES_BY_PATH.get(path, [])
def _get_build_extra_args(path, gazelle_overrides, gazelle_default_attributes):
return _get_override_or_default(gazelle_overrides, gazelle_default_attributes, DEFAULT_BUILD_EXTRA_ARGS_BY_PATH, path, [], "build_extra_args")

def _get_patches(path, module_overrides):
override = module_overrides.get(path)
if override:
return override.patches
return []
return _get_override_or_default(module_overrides, struct(), {}, path, [], "patches")

def _get_patch_args(path, module_overrides):
override = module_overrides.get(path)
if override:
return ["-p{}".format(override.patch_strip)]
return []
override = _get_override_or_default(module_overrides, struct(), {}, path, None, "patch_strip")
return ["-p{}".format(override)] if override else []

def _repo_name(importpath):
path_segments = importpath.split("/")
Expand All @@ -124,6 +160,32 @@ def _is_dev_dependency(module_ctx, tag):
# not available.
return module_ctx.is_dev_dependency(tag) if hasattr(module_ctx, "is_dev_dependency") else False

# This function processes the gazelle_default_attributes tag for a given module and returns a struct
# containing the attributes from _GAZELLE_ATTRS that are defined in the tag.
def _process_gazelle_default_attributes(module_ctx):
for module in module_ctx.modules:
_fail_on_non_root_overrides(module_ctx, module, "gazelle_default_attributes")

for module in module_ctx.modules:
tags = module.tags.gazelle_default_attributes
if not tags:
continue

if len(tags) > 1:
fail(
"go_deps.gazelle_default_attributes: only one tag can be specified per module, got:\n",
*[t for p in zip(module.tags.gazelle_default_attributes, len(module.tags.gazelle_default_attributes) * ["\n"]) for t in p]
)

tag = tags[0]
return struct(**{
attr: getattr(tag, attr)
for attr in _GAZELLE_ATTRS.keys()
if hasattr(tag, attr)
})

return None

# This function processes a given override type for a given module, checks for duplicate overrides
# and inserts the override returned from the process_override_func into the overrides dict.
def _process_overrides(module_ctx, module, override_type, overrides, process_override_func, additional_overrides = None):
Expand All @@ -142,11 +204,11 @@ def _process_gazelle_override(gazelle_override_tag):
for directive in gazelle_override_tag.directives:
_check_directive(directive)

return struct(
directives = gazelle_override_tag.directives,
build_file_generation = gazelle_override_tag.build_file_generation,
build_extra_args = gazelle_override_tag.build_extra_args,
)
return struct(**{
attr: getattr(gazelle_override_tag, attr)
for attr in _GAZELLE_ATTRS.keys()
if hasattr(gazelle_override_tag, attr)
})

def _process_module_override(module_override_tag):
return struct(
Expand Down Expand Up @@ -215,6 +277,7 @@ def _go_deps_impl(module_ctx):
replace_map = {}
bazel_deps = {}

gazelle_default_attributes = _process_gazelle_default_attributes(module_ctx)
archive_overrides = {}
gazelle_overrides = {}
module_overrides = {}
Expand Down Expand Up @@ -341,7 +404,6 @@ def _go_deps_impl(module_ctx):
# in the module resolutions and swapping out the entry.
for path, replace in replace_map.items():
if path in module_resolutions:

# If the replace directive specified a version then we only
# apply it if the versions match.
if replace.from_version:
Expand Down Expand Up @@ -409,9 +471,9 @@ def _go_deps_impl(module_ctx):
go_repository_args = {
"name": module.repo_name,
"importpath": path,
"build_directives": _get_directives(path, gazelle_overrides),
"build_file_generation": _get_build_file_generation(path, gazelle_overrides),
"build_extra_args": _get_build_extra_args(path, gazelle_overrides),
"build_directives": _get_directives(path, gazelle_overrides, gazelle_default_attributes),
"build_file_generation": _get_build_file_generation(path, gazelle_overrides, gazelle_default_attributes),
"build_extra_args": _get_build_extra_args(path, gazelle_overrides, gazelle_default_attributes),
"patches": _get_patches(path, module_overrides),
"patch_args": _get_patch_args(path, module_overrides),
}
Expand Down Expand Up @@ -451,7 +513,7 @@ def _go_deps_impl(module_ctx):
},
build_naming_conventions = drop_nones({
module.repo_name: get_directive_value(
_get_directives(path, gazelle_overrides),
_get_directives(path, gazelle_overrides, gazelle_default_attributes),
"go_naming_convention",
)
for path, module in module_resolutions.items()
Expand Down Expand Up @@ -553,45 +615,23 @@ _archive_override_tag = tag_class(
)

_gazelle_override_tag = tag_class(
attrs = {
attrs = dict({
"path": attr.string(
doc = """The Go module path for the repository to be overridden.
This module path must be defined by other tags in this
extension within this Bazel module.""",
mandatory = True,
),
"build_file_generation": attr.string(
default = "auto",
doc = """One of `"auto"` (default), `"on"`, `"off"`.
Whether Gazelle should generate build files for the Go module. In
`"auto"` mode, Gazelle will run if there is no build file in the Go
module's root directory.""",
values = [
"auto",
"off",
"on",
],
),
"build_extra_args": attr.string_list(
default = [],
doc = """
A list of additional command line arguments to pass to Gazelle when generating build files.
""",
),
"directives": attr.string_list(
doc = """Gazelle configuration directives to use for this Go module's external repository.
Each directive uses the same format as those that Gazelle
accepts as comments in Bazel source files, with the
directive name followed by optional arguments separated by
whitespace.""",
),
},
}, **_GAZELLE_ATTRS),
doc = "Override Gazelle's behavior on a given Go module defined by other tags in this extension.",
)

_gazelle_default_attributes_tag = tag_class(
attrs = _GAZELLE_ATTRS,
doc = "Override Gazelle's default attribute values for all modules in this extension.",
)

_module_override_tag = tag_class(
attrs = {
"path": attr.string(
Expand Down Expand Up @@ -619,6 +659,7 @@ go_deps = module_extension(
"config": _config_tag,
"from_file": _from_file_tag,
"gazelle_override": _gazelle_override_tag,
"gazelle_default_attributes": _gazelle_default_attributes_tag,
"module": _module_tag,
"module_override": _module_override_tag,
},
Expand Down
34 changes: 25 additions & 9 deletions tests/bcr/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")

# Validate a go.mod replace directive works.
go_deps.from_file(go_mod = "//:go.mod")
go_deps.gazelle_default_attributes(
build_file_generation = "on",
directives = [
"gazelle:proto disable",
],
)

# By defining `gazelle_default_attributes`, we also must individually
# specify certain overrides from internal/bzlmod/default_gazelle_overrides.bzl
go_deps.gazelle_override(

# Because this tag is specified, we must specify build_file_generation
# otherwise it will be set to "auto" by default.
build_file_generation = "on",
directives = [
"gazelle:build_file_name BUILD.bazel",
"gazelle:build_file_proto_mode disable_global",
],
path = "github.com/google/safetext",
)

# Verify that the gazelle:go_naming_convention directive in an override is
# respected.
Expand Down Expand Up @@ -56,16 +76,16 @@ go_deps.gazelle_override(
path = "github.com/bazelbuild/buildtools",
)
go_deps.archive_override(
urls = [
"https://github.com/bazelbuild/buildtools/archive/ae8e3206e815d086269eb208b01f300639a4b194.tar.gz",
],
patch_strip = 1,
patches = [
"//patches:buildtools.patch",
],
strip_prefix = "buildtools-ae8e3206e815d086269eb208b01f300639a4b194",
path = "github.com/bazelbuild/buildtools",
sha256 = "05d7c3d2bd3cc0b02d15672fefa0d6be48c7aebe459c1c99dced7ac5e598508f",
strip_prefix = "buildtools-ae8e3206e815d086269eb208b01f300639a4b194",
urls = [
"https://github.com/bazelbuild/buildtools/archive/ae8e3206e815d086269eb208b01f300639a4b194.tar.gz",
],
)

# Transitive dependencies have to be listed here explicitly.
Expand All @@ -76,12 +96,12 @@ go_deps.module(
version = "v3.0.1",
)
go_deps.gazelle_override(
path = "gopkg.in/yaml.v3",
directives = [
# Verify that the build naming convention is picked up by Gazelle when it
# emits references to this repo.
"gazelle:go_naming_convention go_default_library",
],
path = "gopkg.in/yaml.v3",
)
go_deps.module(
indirect = True,
Expand All @@ -93,14 +113,10 @@ use_repo(
go_deps,
"com_github_bazelbuild_buildtools",
"com_github_bmatcuk_doublestar_v4",
"com_github_datadog_sketches_go",
"com_github_envoyproxy_protoc_gen_validate",
"com_github_fmeum_dep_on_gazelle",
"com_github_google_safetext",
"com_github_stretchr_testify",
"org_golang_x_sys",
# It is not necessary to list transitive dependencies here, only direct ones.
# "in_gopkg_yaml_v3",
# Only used for testing.
"bazel_gazelle_go_repository_config",
)
Expand Down
2 changes: 0 additions & 2 deletions tests/bcr/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ go 1.19
replace github.com/bmatcuk/doublestar/v4 => github.com/bmatcuk/doublestar v1.3.4

require (
github.com/DataDog/sketches-go v1.4.1
github.com/bazelbuild/rules_go v0.39.1
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/cloudflare/circl v1.3.7
github.com/envoyproxy/protoc-gen-validate v1.0.1
github.com/fmeum/dep_on_gazelle v1.0.0
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2
golang.org/x/sys v0.15.0
Expand Down
4 changes: 0 additions & 4 deletions tests/bcr/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
github.com/DataDog/sketches-go v1.4.1 h1:j5G6as+9FASM2qC36lvpvQAj9qsv/jUs3FtO8CwZNAY=
github.com/DataDog/sketches-go v1.4.1/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk=
github.com/bazelbuild/bazel-gazelle v0.30.0 h1:q9XLWQSCA5NZPJ98WFqicHkq6fxrDPnfvMO1XycQBMg=
github.com/bazelbuild/bazel-gazelle v0.30.0/go.mod h1:6RxhjM1v/lTpD3JlMpKUCcdus4tvdqsqdfbjYi+czYs=
github.com/bazelbuild/rules_go v0.39.1 h1:wkJLUDx59dntWMghuL8++GteoU1To6sRoKJXuyFtmf8=
Expand All @@ -13,8 +11,6 @@ github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBS
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/protoc-gen-validate v1.0.1 h1:kt9FtLiooDc0vbwTLhdg3dyNX1K9Qwa1EK9LcD4jVUQ=
github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs=
github.com/fmeum/dep_on_gazelle v1.0.0 h1:7gEtQ2CoD77tYca+1iUnKjIBUZ4mX7mZwjdWp3uuN/E=
github.com/fmeum/dep_on_gazelle v1.0.0/go.mod h1:VYCjwfsyRHOJL8oenaEjhIzgM7Oj70iTxgJ2RfXbYr0=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
Expand Down
2 changes: 0 additions & 2 deletions tests/bcr/pkg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ go_test(
"@circl//dh/x25519",
"@com_github_bazelbuild_buildtools//labels:go_default_library",
"@com_github_bmatcuk_doublestar_v4//:doublestar",
"@com_github_datadog_sketches_go//ddsketch",
"@com_github_envoyproxy_protoc_gen_validate//validate",
"@com_github_fmeum_dep_on_gazelle//:dep_on_gazelle",
"@com_github_google_safetext//yamltemplate",
"@com_github_stretchr_testify//require:go_default_library",
Expand Down
Loading

0 comments on commit 4cbbf20

Please sign in to comment.